}
}),
+ _render_change_indicator: function()
+ {
+ return $('<ul />')
+ .addClass('nav navbar-nav navbar-right')
+ .append($('<li />')
+ .append($('<a />')
+ .attr('id', 'changes')
+ .attr('href', '#')
+ .append($('<span />')
+ .addClass('label label-info'))));
+ },
+
renderMainMenu: _luci2.rpc.declare({
object: 'luci2.ui',
method: 'menu',
$('#mainmenu')
.empty()
- .append(_luci2.globals.mainMenu.render(0, 1));
+ .append(_luci2.globals.mainMenu.render(0, 1))
+ .append(_luci2.ui._render_change_indicator());
}
}),
if (n > 0)
$('#changes')
- .empty()
- .show()
- .append($('<a />')
- .attr('href', '#')
- .addClass('label')
- .addClass('notice')
- .text(_luci2.trcp('Pending configuration changes', '1 change', '%d changes', n).format(n))
- .click(function(ev) {
- _luci2.ui.dialog(_luci2.tr('Staged configuration changes'), html, { style: 'close' });
- ev.preventDefault();
- }));
+ .click(function(ev) {
+ _luci2.ui.dialog(_luci2.tr('Staged configuration changes'), html, {
+ style: 'confirm',
+ confirm: function() {
+ _luci2.uci.apply().then(
+ function(code) { alert('Success with code ' + code); },
+ function(code) { alert('Error with code ' + code); }
+ );
+ }
+ });
+ ev.preventDefault();
+ })
+ .children('span')
+ .show()
+ .text(_luci2.trcp('Pending configuration changes', '1 change', '%d changes', n).format(n));
else
- $('#changes')
- .hide();
+ $('#changes').children('span').hide();
});
},
this.instance = { };
this.dependencies = [ ];
this.rdependency = { };
+ this.events = { };
this.options = _luci2.defaults(options, {
placeholder: '',
return i.top;
},
+ active: function(sid)
+ {
+ return (this.instance[sid] && !this.instance[sid].disabled);
+ },
+
ucipath: function(sid)
{
return {
}
if (rv)
+ {
for (var field in d.self.rdependency)
d.self.rdependency[field].toggle(d.sid);
+ d.self.section.tabtoggle(d.sid);
+ }
+
return rv;
},
validator: function(sid, elem, multi)
{
+ var evdata = {
+ self: this,
+ sid: sid,
+ elem: elem,
+ multi: multi,
+ inst: this.instance[sid],
+ opt: this.options.optional
+ };
+
+ for (var evname in this.events)
+ elem.on(evname, evdata, this.events[evname]);
+
if (typeof(this.options.datatype) == 'undefined' && $.isEmptyObject(this.rdependency))
return elem;
if (typeof(this.options.datatype) == 'string')
{
try {
- vstack = _luci2.cbi.validation.compile(this.options.datatype);
+ evdata.vstack = _luci2.cbi.validation.compile(this.options.datatype);
} catch(e) { };
}
else if (typeof(this.options.datatype) == 'function')
{
var vfunc = this.options.datatype;
- vstack = [ function(elem) {
+ evdata.vstack = [ function(elem) {
var rv = vfunc(this, elem);
if (rv !== true)
validation.message = rv;
}, [ elem ] ];
}
- var evdata = {
- self: this,
- sid: sid,
- elem: elem,
- multi: multi,
- vstack: vstack,
- inst: this.instance[sid],
- opt: this.options.optional
- };
-
if (elem.prop('tagName') == 'SELECT')
{
elem.change(evdata, this._ev_validate);
return (i.disabled || i.error.text() == '');
},
- depends: function(d, v)
+ depends: function(d, v, add)
{
var dep;
if ($.isEmptyObject(dep))
return this;
- this.dependencies.push(dep);
+ if (!add || !this.dependencies.length)
+ this.dependencies.push(dep);
+ else
+ for (var i = 0; i < this.dependencies.length; i++)
+ $.extend(this.dependencies[i], dep);
return this;
},
break;
}
}
- else if (typeof(cmp) == 'string')
+ else if (typeof(cmp) == 'string' || typeof(cmp) == 'number')
{
if (val != cmp)
{
}
return false;
+ },
+
+ on: function(evname, evfunc)
+ {
+ this.events[evname] = evfunc;
+ return this;
}
});
var s = $('<select />')
.addClass('form-control');
- if (this.options.optional)
+ if (this.options.optional && !this.has_empty)
$('<option />')
.attr('value', '')
.text(_luci2.tr('-- Please choose --'))
if (!this.choices)
this.choices = [ ];
+ if (k == '')
+ this.has_empty = true;
+
this.choices.push([k, v || k]);
return this;
}
{
ev.data.select.hide();
ev.data.input.show().focus();
-
- var v = ev.data.input.val();
- ev.data.input.val(' ');
- ev.data.input.val(v);
+ ev.data.input.val('');
}
else if (self.options.optional && s.selectedIndex == 0)
{
{
ev.data.input.val(ev.data.select.val());
}
+
+ ev.stopPropagation();
},
_blur: function(ev)
ev.data.select.empty();
- if (self.options.optional)
+ if (self.options.optional && !self.has_empty)
$('<option />')
.attr('value', '')
.text(_luci2.tr('-- please choose --'))
.appendTo(ev.data.select);
ev.data.input.hide();
- ev.data.select.val(val).show().focus();
+ ev.data.select.val(val).show().blur();
},
_enter: function(ev)
.attr('id', this.id(sid));
var t = $('<input />')
+ .addClass('form-control')
.attr('type', 'text')
.hide()
.appendTo(d);
var s = $('<select />')
+ .addClass('form-control')
.appendTo(d);
var evdata = {
self: this,
- input: this.validator(sid, t),
- select: this.validator(sid, s)
+ input: t,
+ select: s
};
s.change(evdata, this._change);
t.val(this.ucivalue(sid));
t.blur();
+ this.validator(sid, t);
+ this.validator(sid, s);
+
return d;
},
if (!this.choices)
this.choices = [ ];
+ if (k == '')
+ this.has_empty = true;
+
this.choices.push([k, v || k]);
return this;
},
formvalue: function(sid)
{
var rv = [ ];
- var fields = $('#' + this.id(sid) + ' > input');
+ var fields = $('#' + this.id(sid) + ' input');
for (var i = 0; i < fields.length; i++)
if (typeof(fields[i].value) == 'string' && fields[i].value.length)
}
});
+ this.cbi.DeviceList = this.cbi.NetworkList.extend({
+ _ev_focus: function(ev)
+ {
+ var self = ev.data.self;
+ var input = $(this);
+
+ input.parent().prev().prop('checked', true);
+ },
+
+ _ev_blur: function(ev)
+ {
+ ev.which = 10;
+ ev.data.self._ev_keydown.call(this, ev);
+ },
+
+ _ev_keydown: function(ev)
+ {
+ if (ev.which != 10 && ev.which != 13)
+ return;
+
+ var sid = ev.data.sid;
+ var self = ev.data.self;
+ var input = $(this);
+ var ifnames = _luci2.toArray(input.val());
+
+ if (!ifnames.length)
+ return;
+
+ _luci2.NetworkModel.createDevice(ifnames[0]);
+
+ self._redraw(sid, $('#' + self.id(sid)), ifnames[0]);
+ },
+
+ load: function(sid)
+ {
+ return _luci2.NetworkModel.init();
+ },
+
+ _redraw: function(sid, ul, sel)
+ {
+ var id = ul.attr('id');
+ var devs = _luci2.NetworkModel.getDevices();
+ var iface = _luci2.NetworkModel.getInterface(sid);
+ var itype = this.options.multiple ? 'checkbox' : 'radio';
+ var check = { };
+
+ if (!sel)
+ {
+ for (var i = 0; i < devs.length; i++)
+ if (devs[i].isInNetwork(iface))
+ check[devs[i].name()] = true;
+ }
+ else
+ {
+ if (this.options.multiple)
+ check = _luci2.toObject(this.formvalue(sid));
+
+ check[sel] = true;
+ }
+
+ ul.empty();
+
+ for (var i = 0; i < devs.length; i++)
+ {
+ var dev = devs[i];
+
+ if (dev.isBridge() && this.options.bridges === false)
+ continue;
+
+ if (!dev.isBridgeable() && this.options.multiple)
+ continue;
+
+ var badge = $('<span />')
+ .addClass('badge')
+ .append($('<img />').attr('src', dev.icon()))
+ .append(' %s: %s'.format(dev.name(), dev.description()));
+
+ //var ifcs = dev.getInterfaces();
+ //if (ifcs.length)
+ //{
+ // for (var j = 0; j < ifcs.length; j++)
+ // badge.append((j ? ', ' : ' (') + ifcs[j].name());
+ //
+ // badge.append(')');
+ //}
+
+ $('<li />')
+ .append($('<label />')
+ .addClass(itype + ' inline')
+ .append($('<input />')
+ .attr('name', itype + id)
+ .attr('type', itype)
+ .attr('value', dev.name())
+ .prop('checked', !!check[dev.name()]))
+ .append(badge))
+ .appendTo(ul);
+ }
+
+
+ $('<li />')
+ .append($('<label />')
+ .attr('for', 'custom' + id)
+ .addClass(itype + ' inline')
+ .append($('<input />')
+ .attr('name', itype + id)
+ .attr('type', itype)
+ .attr('value', ''))
+ .append($('<span />')
+ .addClass('badge')
+ .append($('<input />')
+ .attr('id', 'custom' + id)
+ .attr('type', 'text')
+ .attr('placeholder', _luci2.tr('Custom device …'))
+ .on('focus', { self: this, sid: sid }, this._ev_focus)
+ .on('blur', { self: this, sid: sid }, this._ev_blur)
+ .on('keydown', { self: this, sid: sid }, this._ev_keydown))))
+ .appendTo(ul);
+
+ if (!this.options.multiple)
+ {
+ $('<li />')
+ .append($('<label />')
+ .addClass(itype + ' inline text-muted')
+ .append($('<input />')
+ .attr('name', itype + id)
+ .attr('type', itype)
+ .attr('value', '')
+ .prop('checked', $.isEmptyObject(check)))
+ .append(_luci2.tr('unspecified')))
+ .appendTo(ul);
+ }
+ },
+
+ widget: function(sid)
+ {
+ var id = this.id(sid);
+ var ul = $('<ul />')
+ .attr('id', id)
+ .addClass('list-unstyled');
+
+ this._redraw(sid, ul);
+
+ return ul;
+ },
+
+ save: function(sid)
+ {
+ if (this.instance[sid].disabled)
+ return;
+
+ var ifnames = this.formvalue(sid);
+ //if (!ifnames)
+ // return;
+
+ var iface = _luci2.NetworkModel.getInterface(sid);
+ if (!iface)
+ return;
+
+ iface.setDevices($.isArray(ifnames) ? ifnames : [ ifnames ]);
+ }
+ });
+
this.cbi.AbstractSection = this.ui.AbstractWidget.extend({
id: function()
return w;
},
+ tabtoggle: function(sid)
+ {
+ for (var i = 0; i < this.tabs.length; i++)
+ {
+ var tab = this.tabs[i];
+ var elem = $('#' + this.id('nodetab', sid, tab.id));
+ var empty = true;
+
+ for (var j = 0; j < tab.fields.length; j++)
+ {
+ if (tab.fields[j].active(sid))
+ {
+ empty = false;
+ break;
+ }
+ }
+
+ if (empty && elem.is(':visible'))
+ elem.fadeOut();
+ else if (!empty)
+ elem.fadeIn();
+ }
+ },
+
ucipackages: function(pkg)
{
for (var i = 0; i < this.tabs.length; i++)
validate: function()
{
- this.error_count = 0;
-
+ var errors = 0;
var as = this.sections();
for (var i = 0; i < as.length; i++)
var invals = this.validate_section(as[i]['.name']);
if (invals > 0)
- this.error_count += invals;
+ errors += invals;
}
var badge = $('#' + this.id('sectiontab')).children('span:first');
- if (this.error_count > 0)
+ if (errors > 0)
badge.show()
- .text(this.error_count)
- .attr('title', _luci2.trp('1 Error', '%d Errors', this.error_count).format(this.error_count));
+ .text(errors)
+ .attr('title', _luci2.trp('1 Error', '%d Errors', errors).format(errors));
else
badge.hide();
- return (this.error_count == 0);
+ return (errors == 0);
}
});
this.options = options;
this.tabs = [ ];
this.fields = { };
- this.error_count = 0;
this.active_panel = 0;
this.active_tab = { };
},
if (typeof(cb) == 'function')
for (var i = 0; i < s2.length; i++)
- cb.apply(this, [ s2[i] ]);
+ cb.call(this, s2[i]);
return s2;
},
if (cur == tab_index)
tabh.addClass('active');
+ if (!tab.fields.length)
+ tabh.hide();
+
return tabh;
},
return body;
},
+ _render_footer: function()
+ {
+ return $('<div />')
+ .addClass('panel panel-default panel-body text-right')
+ .append($('<div />')
+ .addClass('btn-group')
+ .append(_luci2.ui.button(_luci2.tr('Save & Apply'), 'primary')
+ .click({ self: this }, function(ev) { }))
+ .append(_luci2.ui.button(_luci2.tr('Save'), 'default')
+ .click({ self: this }, function(ev) { ev.data.self.send(); }))
+ .append(_luci2.ui.button(_luci2.tr('Reset'), 'default')
+ .click({ self: this }, function(ev) { ev.data.self.insertInto(ev.data.self.target); })));
+ },
+
render: function()
{
var map = $('<form />');
map.append(this._render_body());
if (this.options.pageaction !== false)
- {
- map.append($('<div />')
- .addClass('panel panel-default panel-body text-right')
- .append($('<div />')
- .addClass('btn-group')
- .append(_luci2.ui.button(_luci2.tr('Save & Apply'), 'primary')
- .click({ self: this }, function(ev) { }))
- .append(_luci2.ui.button(_luci2.tr('Save'), 'default')
- .click({ self: this }, function(ev) { ev.data.self.send(); }))
- .append(_luci2.ui.button(_luci2.tr('Reset'), 'default')
- .click({ self: this }, function(ev) { ev.data.self.insertInto(ev.data.self.target); }))));
- }
+ map.append(this._render_footer());
return map;
},
});
}
});
+
+ this.cbi.Modal = this.cbi.Map.extend({
+ _render_footer: function()
+ {
+ return $('<div />')
+ .addClass('btn-group')
+ .append(_luci2.ui.button(_luci2.tr('Save & Apply'), 'primary')
+ .click({ self: this }, function(ev) { }))
+ .append(_luci2.ui.button(_luci2.tr('Save'), 'default')
+ .click({ self: this }, function(ev) { ev.data.self.send(); }))
+ .append(_luci2.ui.button(_luci2.tr('Cancel'), 'default')
+ .click({ self: this }, function(ev) { _luci2.ui.dialog(false); }));
+ },
+
+ render: function()
+ {
+ var modal = _luci2.ui.dialog(this.label('caption'), null, { wide: true });
+ var map = $('<form />');
+
+ var desc = this.label('description');
+ if (desc)
+ map.append($('<p />').text(desc));
+
+ map.append(this._render_body());
+
+ modal.find('.modal-body').append(map);
+ modal.find('.modal-footer').append(this._render_footer());
+
+ return modal;
+ },
+
+ redraw: function()
+ {
+ this.render();
+ this.finish();
+ },
+
+ show: function()
+ {
+ var self = this;
+
+ _luci2.ui.loading(true);
+
+ return self.load().then(function() {
+ self.render();
+ self.finish();
+
+ _luci2.ui.loading(false);
+ });
+ }
+ });
};