luci2: keep scroll position when redrawing CBI forms, fix crash when deleting last...
[project/luci2/ui.git] / luci2 / htdocs / luci2 / luci2.js
index 745f955..b8e9068 100644 (file)
@@ -1452,6 +1452,21 @@ function LuCI2()
 
        this.ui = {
 
+               saveScrollTop: function()
+               {
+                       this._scroll_top = $(document).scrollTop();
+               },
+
+               restoreScrollTop: function()
+               {
+                       if (typeof(this._scroll_top) == 'undefined')
+                               return;
+
+                       $(document).scrollTop(this._scroll_top);
+
+                       delete this._scroll_top;
+               },
+
                loading: function(enable)
                {
                        var win = $(window);
@@ -1979,13 +1994,22 @@ function LuCI2()
                        if (_luci2._views[name] instanceof _luci2.ui.view)
                                return _luci2._views[name].render();
 
-                       return $.ajax(_luci2.globals.resource + '/view/' + name + '.js', {
+                       var url = _luci2.globals.resource + '/view/' + name + '.js';
+
+                       return $.ajax(url, {
                                method: 'GET',
                                cache: true,
                                dataType: 'text'
                        }).then(function(data) {
                                try {
-                                       var viewConstructor = (new Function(['L', '$'], 'return ' + data))(_luci2, $);
+                                       var viewConstructorSource = (
+                                               '(function(L, $) {\n' +
+                                                       'return %s' +
+                                               '})(_luci2, $);\n\n' +
+                                               '//@ sourceURL=%s'
+                                       ).format(data, url);
+
+                                       var viewConstructor = eval(viewConstructorSource);
 
                                        _luci2._views[name] = new viewConstructor({
                                                name: name,
@@ -1994,7 +2018,9 @@ function LuCI2()
 
                                        return _luci2._views[name].render();
                                }
-                               catch(e) { };
+                               catch(e) {
+                                       alert('Unable to instantiate view "%s": %s'.format(url, e));
+                               };
 
                                return $.Deferred().resolve();
                        });
@@ -2984,7 +3010,7 @@ function LuCI2()
        };
 
 
-       var AbstractValue = AbstractWidget.extend({
+       this.cbi.AbstractValue = AbstractWidget.extend({
                init: function(name, options)
                {
                        this.name = name;
@@ -3250,11 +3276,11 @@ function LuCI2()
                                {
                                        if (typeof(d[i]) == 'string')
                                                dep[d[i]] = true;
-                                       else if (d[i] instanceof AbstractValue)
+                                       else if (d[i] instanceof _luci2.cbi.AbstractValue)
                                                dep[d[i].name] = true;
                                }
                        }
-                       else if (d instanceof AbstractValue)
+                       else if (d instanceof _luci2.cbi.AbstractValue)
                        {
                                dep = { };
                                dep[d.name] = (typeof(v) == 'undefined') ? true : v;
@@ -3362,7 +3388,7 @@ function LuCI2()
                }
        });
 
-       this.cbi.CheckboxValue = AbstractValue.extend({
+       this.cbi.CheckboxValue = this.cbi.AbstractValue.extend({
                widget: function(sid)
                {
                        var o = this.options;
@@ -3427,7 +3453,7 @@ function LuCI2()
                }
        });
 
-       this.cbi.InputValue = AbstractValue.extend({
+       this.cbi.InputValue = this.cbi.AbstractValue.extend({
                widget: function(sid)
                {
                        var i = $('<input />')
@@ -3440,7 +3466,7 @@ function LuCI2()
                }
        });
 
-       this.cbi.PasswordValue = AbstractValue.extend({
+       this.cbi.PasswordValue = this.cbi.AbstractValue.extend({
                widget: function(sid)
                {
                        var i = $('<input />')
@@ -3469,7 +3495,7 @@ function LuCI2()
                }
        });
 
-       this.cbi.ListValue = AbstractValue.extend({
+       this.cbi.ListValue = this.cbi.AbstractValue.extend({
                widget: function(sid)
                {
                        var s = $('<select />');
@@ -3564,7 +3590,7 @@ function LuCI2()
                }
        });
 
-       this.cbi.ComboBox = AbstractValue.extend({
+       this.cbi.ComboBox = this.cbi.AbstractValue.extend({
                _change: function(ev)
                {
                        var s = ev.target;
@@ -3933,7 +3959,7 @@ function LuCI2()
                }
        });
 
-       this.cbi.DummyValue = AbstractValue.extend({
+       this.cbi.DummyValue = this.cbi.AbstractValue.extend({
                widget: function(sid)
                {
                        return $('<div />')
@@ -3948,7 +3974,7 @@ function LuCI2()
                }
        });
 
-       this.cbi.NetworkList = AbstractValue.extend({
+       this.cbi.NetworkList = this.cbi.AbstractValue.extend({
                load: function(sid)
                {
                        var self = this;
@@ -4111,7 +4137,7 @@ function LuCI2()
        });
 
 
-       var AbstractSection = AbstractWidget.extend({
+       this.cbi.AbstractSection = AbstractWidget.extend({
                id: function()
                {
                        var s = [ arguments[0], this.map.uci_package, this.uci_type ];
@@ -4161,7 +4187,7 @@ function LuCI2()
 
                        var w = widget ? new widget(name, options) : null;
 
-                       if (!(w instanceof AbstractValue))
+                       if (!(w instanceof _luci2.cbi.AbstractValue))
                                throw 'Widget must be an instance of AbstractValue';
 
                        w.section = this;
@@ -4257,7 +4283,7 @@ function LuCI2()
                }
        });
 
-       this.cbi.TypedSection = AbstractSection.extend({
+       this.cbi.TypedSection = this.cbi.AbstractSection.extend({
                init: function(uci_type, options)
                {
                        this.uci_type = uci_type;
@@ -4312,10 +4338,14 @@ function LuCI2()
                        if (addb.prop('disabled') || name === '')
                                return;
 
+                       _luci2.ui.saveScrollTop();
+
                        self.active_panel = -1;
                        self.map.save();
                        self.add(name);
                        self.map.redraw();
+
+                       _luci2.ui.restoreScrollTop();
                },
 
                _remove: function(ev)
@@ -4323,10 +4353,17 @@ function LuCI2()
                        var self = ev.data.self;
                        var sid  = ev.data.sid;
 
+                       if (ev.data.index == (self.sections().length - 1))
+                               self.active_panel = -1;
+
+                       _luci2.ui.saveScrollTop();
+
                        self.map.save();
                        self.remove(sid);
                        self.map.redraw();
 
+                       _luci2.ui.restoreScrollTop();
+
                        ev.stopPropagation();
                },
 
@@ -4388,9 +4425,9 @@ function LuCI2()
                                        for (var i = 0; i < this.options.teasers.length; i++)
                                        {
                                                var f = this.options.teasers[i];
-                                               if (f instanceof AbstractValue)
+                                               if (f instanceof _luci2.cbi.AbstractValue)
                                                        tf.push(f);
-                                               else if (typeof(f) == 'string' && this.fields[f] instanceof AbstractValue)
+                                               else if (typeof(f) == 'string' && this.fields[f] instanceof _luci2.cbi.AbstractValue)
                                                        tf.push(this.fields[f]);
                                        }
                                }
@@ -4469,7 +4506,7 @@ function LuCI2()
                        return add;
                },
 
-               _render_remove: function(sid)
+               _render_remove: function(sid, index)
                {
                        var text = _luci2.tr('Remove');
                        var ttip = _luci2.tr('Remove this section');
@@ -4484,7 +4521,7 @@ function LuCI2()
                                .addClass('cbi-button')
                                .addClass('cbi-button-remove')
                                .val(text).attr('title', ttip)
-                               .click({ self: this, sid: sid }, this._remove);
+                               .click({ self: this, sid: sid, index: index }, this._remove);
                },
 
                _render_caption: function(sid)
@@ -4568,7 +4605,7 @@ function LuCI2()
                                        $('<div />')
                                                .addClass('cbi-section-remove')
                                                .addClass('right')
-                                               .append(this._render_remove(sid))
+                                               .append(this._render_remove(sid, panel_index))
                                                .appendTo(head);
 
                                var body = $('<div />')
@@ -4911,7 +4948,8 @@ function LuCI2()
                                deletes: { }
                        };
 
-                       this.active_panel = 0;
+                       if (typeof(this.active_panel) == 'undefined')
+                               this.active_panel = 0;
 
                        var packages = { };
 
@@ -5061,7 +5099,7 @@ function LuCI2()
                {
                        var w = widget ? new widget(uci_type, options) : null;
 
-                       if (!(w instanceof AbstractSection))
+                       if (!(w instanceof _luci2.cbi.AbstractSection))
                                throw 'Widget must be an instance of AbstractSection';
 
                        w.map = this;
@@ -5335,6 +5373,7 @@ function LuCI2()
 
                        var self = this;
 
+                       _luci2.ui.saveScrollTop();
                        _luci2.ui.loading(true);
 
                        return this.save().then(send_cb).then(function() {
@@ -5344,6 +5383,7 @@ function LuCI2()
                                self = null;
 
                                _luci2.ui.loading(false);
+                               _luci2.ui.restoreScrollTop();
                        });
                },