luci-base: harmonize JS class naming and requesting

- Make builtin classes available via `require` to allow view code to
   request external and internal classes in a consistent manner without
   having to know which classes are builtin and which not

 - Make base classes request any used class explicitely instead of
   relying on implicitly set up L.{dom,view,Poll,Request,Class} aliases

 - Consistently convert class names to lower case in JSdoc to match
   the names used in `require` statements

 - Deprecate L.{dom,view,Poll,Request,Class} aliases

Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
Jo-Philipp Wich 2020-04-02 21:40:50 +02:00
parent 994e1f3890
commit 711f759278
8 changed files with 1310 additions and 1254 deletions

View file

@ -1,10 +1,12 @@
'use strict'; 'use strict';
'require ui'; 'require ui';
'require uci'; 'require uci';
'require dom';
'require baseclass';
var scope = this; var scope = this;
var CBIJSONConfig = Class.extend({ var CBIJSONConfig = baseclass.extend({
__init__: function(data) { __init__: function(data) {
data = Object.assign({}, data); data = Object.assign({}, data);
@ -171,7 +173,7 @@ var CBIJSONConfig = Class.extend({
} }
}); });
var CBINode = Class.extend({ var CBINode = baseclass.extend({
__init__: function(title, description) { __init__: function(title, description) {
this.title = title || ''; this.title = title || '';
this.description = description || ''; this.description = description || '';
@ -330,12 +332,12 @@ var CBIMap = CBINode.extend({
'cbi-dependency-check': L.bind(this.checkDepends, this) 'cbi-dependency-check': L.bind(this.checkDepends, this)
})); }));
L.dom.bindClassInstance(mapEl, this); dom.bindClassInstance(mapEl, this);
return this.renderChildren(null).then(L.bind(function(nodes) { return this.renderChildren(null).then(L.bind(function(nodes) {
var initialRender = !mapEl.firstChild; var initialRender = !mapEl.firstChild;
L.dom.content(mapEl, null); dom.content(mapEl, null);
if (this.title != null && this.title != '') if (this.title != null && this.title != '')
mapEl.appendChild(E('h2', { 'name': 'content' }, this.title)); mapEl.appendChild(E('h2', { 'name': 'content' }, this.title));
@ -344,9 +346,9 @@ var CBIMap = CBINode.extend({
mapEl.appendChild(E('div', { 'class': 'cbi-map-descr' }, this.description)); mapEl.appendChild(E('div', { 'class': 'cbi-map-descr' }, this.description));
if (this.tabbed) if (this.tabbed)
L.dom.append(mapEl, E('div', { 'class': 'cbi-map-tabbed' }, nodes)); dom.append(mapEl, E('div', { 'class': 'cbi-map-tabbed' }, nodes));
else else
L.dom.append(mapEl, nodes); dom.append(mapEl, nodes);
if (!initialRender) { if (!initialRender) {
mapEl.classList.remove('flash'); mapEl.classList.remove('flash');
@ -377,7 +379,7 @@ var CBIMap = CBINode.extend({
elem = this.findElement('data-field', id); elem = this.findElement('data-field', id);
sid = elem ? id.split(/\./)[2] : null; sid = elem ? id.split(/\./)[2] : null;
inst = elem ? L.dom.findClassInstance(elem) : null; inst = elem ? dom.findClassInstance(elem) : null;
return (inst instanceof CBIAbstractValue) ? [ inst, sid ] : null; return (inst instanceof CBIAbstractValue) ? [ inst, sid ] : null;
}, },
@ -764,7 +766,7 @@ var CBIAbstractValue = CBINode.extend({
var node = this.map.findElement('id', this.cbid(section_id)); var node = this.map.findElement('id', this.cbid(section_id));
if (node && node.getAttribute('data-changed') != 'true' && satisified_defval != null && cfgvalue == null) if (node && node.getAttribute('data-changed') != 'true' && satisified_defval != null && cfgvalue == null)
L.dom.callClassMethod(node, 'setValue', satisified_defval); dom.callClassMethod(node, 'setValue', satisified_defval);
this.default = satisified_defval; this.default = satisified_defval;
}, },
@ -790,7 +792,7 @@ var CBIAbstractValue = CBINode.extend({
getUIElement: function(section_id) { getUIElement: function(section_id) {
var node = this.map.findElement('id', this.cbid(section_id)), var node = this.map.findElement('id', this.cbid(section_id)),
inst = node ? L.dom.findClassInstance(node) : null; inst = node ? dom.findClassInstance(node) : null;
return (inst instanceof ui.AbstractElement) ? inst : null; return (inst instanceof ui.AbstractElement) ? inst : null;
}, },
@ -929,7 +931,7 @@ var CBITypedSection = CBIAbstractSection.extend({
createEl.appendChild(E('button', { createEl.appendChild(E('button', {
'class': 'cbi-button cbi-button-add', 'class': 'cbi-button cbi-button-add',
'title': btn_title || _('Add'), 'title': btn_title || _('Add'),
'click': L.ui.createHandlerFn(this, 'handleAdd') 'click': ui.createHandlerFn(this, 'handleAdd')
}, [ btn_title || _('Add') ])); }, [ btn_title || _('Add') ]));
} }
else { else {
@ -938,14 +940,14 @@ var CBITypedSection = CBIAbstractSection.extend({
'class': 'cbi-section-create-name' 'class': 'cbi-section-create-name'
}); });
L.dom.append(createEl, [ dom.append(createEl, [
E('div', {}, nameEl), E('div', {}, nameEl),
E('input', { E('input', {
'class': 'cbi-button cbi-button-add', 'class': 'cbi-button cbi-button-add',
'type': 'submit', 'type': 'submit',
'value': btn_title || _('Add'), 'value': btn_title || _('Add'),
'title': btn_title || _('Add'), 'title': btn_title || _('Add'),
'click': L.ui.createHandlerFn(this, function(ev) { 'click': ui.createHandlerFn(this, function(ev) {
if (nameEl.classList.contains('cbi-input-invalid')) if (nameEl.classList.contains('cbi-input-invalid'))
return; return;
@ -991,7 +993,7 @@ var CBITypedSection = CBIAbstractSection.extend({
'class': 'cbi-button', 'class': 'cbi-button',
'name': 'cbi.rts.%s.%s'.format(config_name, cfgsections[i]), 'name': 'cbi.rts.%s.%s'.format(config_name, cfgsections[i]),
'data-section-id': cfgsections[i], 'data-section-id': cfgsections[i],
'click': L.ui.createHandlerFn(this, 'handleRemove', cfgsections[i]) 'click': ui.createHandlerFn(this, 'handleRemove', cfgsections[i])
}, [ _('Delete') ]))); }, [ _('Delete') ])));
} }
@ -1011,7 +1013,7 @@ var CBITypedSection = CBIAbstractSection.extend({
sectionEl.appendChild(this.renderSectionAdd()); sectionEl.appendChild(this.renderSectionAdd());
L.dom.bindClassInstance(sectionEl, this); dom.bindClassInstance(sectionEl, this);
return sectionEl; return sectionEl;
}, },
@ -1099,7 +1101,7 @@ var CBITableSection = CBITypedSection.extend({
sectionEl.appendChild(this.renderSectionAdd('cbi-tblsection-create')); sectionEl.appendChild(this.renderSectionAdd('cbi-tblsection-create'));
L.dom.bindClassInstance(sectionEl, this); dom.bindClassInstance(sectionEl, this);
return sectionEl; return sectionEl;
}, },
@ -1146,7 +1148,7 @@ var CBITableSection = CBITypedSection.extend({
'title': this.titledesc || _('Go to relevant configuration page') 'title': this.titledesc || _('Go to relevant configuration page')
}, opt.title)); }, opt.title));
else else
L.dom.content(trEl.lastElementChild, opt.title); dom.content(trEl.lastElementChild, opt.title);
} }
if (this.sortable || this.extedit || this.addremove || has_more || has_action) if (this.sortable || this.extedit || this.addremove || has_more || has_action)
@ -1198,7 +1200,7 @@ var CBITableSection = CBITypedSection.extend({
}, E('div')); }, E('div'));
if (this.sortable) { if (this.sortable) {
L.dom.append(tdEl.lastElementChild, [ dom.append(tdEl.lastElementChild, [
E('div', { E('div', {
'title': _('Drag to reorder'), 'title': _('Drag to reorder'),
'class': 'btn cbi-button drag-handle center', 'class': 'btn cbi-button drag-handle center',
@ -1217,7 +1219,7 @@ var CBITableSection = CBITypedSection.extend({
location.href = this.extedit.format(sid); location.href = this.extedit.format(sid);
}, this, section_id); }, this, section_id);
L.dom.append(tdEl.lastElementChild, dom.append(tdEl.lastElementChild,
E('button', { E('button', {
'title': _('Edit'), 'title': _('Edit'),
'class': 'cbi-button cbi-button-edit', 'class': 'cbi-button cbi-button-edit',
@ -1227,11 +1229,11 @@ var CBITableSection = CBITypedSection.extend({
} }
if (more_label) { if (more_label) {
L.dom.append(tdEl.lastElementChild, dom.append(tdEl.lastElementChild,
E('button', { E('button', {
'title': more_label, 'title': more_label,
'class': 'cbi-button cbi-button-edit', 'class': 'cbi-button cbi-button-edit',
'click': L.ui.createHandlerFn(this, 'renderMoreOptionsModal', section_id) 'click': ui.createHandlerFn(this, 'renderMoreOptionsModal', section_id)
}, [ more_label ]) }, [ more_label ])
); );
} }
@ -1239,11 +1241,11 @@ var CBITableSection = CBITypedSection.extend({
if (this.addremove) { if (this.addremove) {
var btn_title = this.titleFn('removebtntitle', section_id); var btn_title = this.titleFn('removebtntitle', section_id);
L.dom.append(tdEl.lastElementChild, dom.append(tdEl.lastElementChild,
E('button', { E('button', {
'title': btn_title || _('Delete'), 'title': btn_title || _('Delete'),
'class': 'cbi-button cbi-button-remove', 'class': 'cbi-button cbi-button-remove',
'click': L.ui.createHandlerFn(this, 'handleRemove', section_id) 'click': ui.createHandlerFn(this, 'handleRemove', section_id)
}, [ btn_title || _('Delete') ]) }, [ btn_title || _('Delete') ])
); );
} }
@ -1262,7 +1264,7 @@ var CBITableSection = CBITypedSection.extend({
return false; return false;
} }
scope.dragState.node = L.dom.parent(scope.dragState.node, '.tr'); scope.dragState.node = dom.parent(scope.dragState.node, '.tr');
ev.dataTransfer.setData('text', 'drag'); ev.dataTransfer.setData('text', 'drag');
ev.target.style.opacity = 0.4; ev.target.style.opacity = 0.4;
}, },
@ -1336,14 +1338,14 @@ var CBITableSection = CBITypedSection.extend({
}, },
handleModalCancel: function(modalMap, ev) { handleModalCancel: function(modalMap, ev) {
return Promise.resolve(L.ui.hideModal()); return Promise.resolve(ui.hideModal());
}, },
handleModalSave: function(modalMap, ev) { handleModalSave: function(modalMap, ev) {
return modalMap.save() return modalMap.save()
.then(L.bind(this.map.load, this.map)) .then(L.bind(this.map.load, this.map))
.then(L.bind(this.map.reset, this.map)) .then(L.bind(this.map.reset, this.map))
.then(L.ui.hideModal) .then(ui.hideModal)
.catch(function() {}); .catch(function() {});
}, },
@ -1397,16 +1399,16 @@ var CBITableSection = CBITypedSection.extend({
} }
return Promise.resolve(this.addModalOptions(s, section_id, ev)).then(L.bind(m.render, m)).then(L.bind(function(nodes) { return Promise.resolve(this.addModalOptions(s, section_id, ev)).then(L.bind(m.render, m)).then(L.bind(function(nodes) {
L.ui.showModal(title, [ ui.showModal(title, [
nodes, nodes,
E('div', { 'class': 'right' }, [ E('div', { 'class': 'right' }, [
E('button', { E('button', {
'class': 'btn', 'class': 'btn',
'click': L.ui.createHandlerFn(this, 'handleModalCancel', m) 'click': ui.createHandlerFn(this, 'handleModalCancel', m)
}, [ _('Dismiss') ]), ' ', }, [ _('Dismiss') ]), ' ',
E('button', { E('button', {
'class': 'cbi-button cbi-button-positive important', 'class': 'cbi-button cbi-button-positive important',
'click': L.ui.createHandlerFn(this, 'handleModalSave', m) 'click': ui.createHandlerFn(this, 'handleModalSave', m)
}, [ _('Save') ]) }, [ _('Save') ])
]) ])
], 'cbi-modal'); ], 'cbi-modal');
@ -1555,7 +1557,7 @@ var CBINamedSection = CBIAbstractSection.extend({
E('div', { 'class': 'cbi-section-remove right' }, E('div', { 'class': 'cbi-section-remove right' },
E('button', { E('button', {
'class': 'cbi-button', 'class': 'cbi-button',
'click': L.ui.createHandlerFn(this, 'handleRemove') 'click': ui.createHandlerFn(this, 'handleRemove')
}, [ _('Delete') ]))); }, [ _('Delete') ])));
} }
@ -1570,11 +1572,11 @@ var CBINamedSection = CBIAbstractSection.extend({
sectionEl.appendChild( sectionEl.appendChild(
E('button', { E('button', {
'class': 'cbi-button cbi-button-add', 'class': 'cbi-button cbi-button-add',
'click': L.ui.createHandlerFn(this, 'handleAdd') 'click': ui.createHandlerFn(this, 'handleAdd')
}, [ _('Add') ])); }, [ _('Add') ]));
} }
L.dom.bindClassInstance(sectionEl, this); dom.bindClassInstance(sectionEl, this);
return sectionEl; return sectionEl;
}, },
@ -1598,7 +1600,7 @@ var CBIValue = CBIAbstractValue.extend({
this.keylist.push(String(key)); this.keylist.push(String(key));
this.vallist = this.vallist || []; this.vallist = this.vallist || [];
this.vallist.push(L.dom.elem(val) ? val : String(val != null ? val : key)); this.vallist.push(dom.elem(val) ? val : String(val != null ? val : key));
}, },
render: function(option_index, section_id, in_table) { render: function(option_index, section_id, in_table) {
@ -1669,7 +1671,7 @@ var CBIValue = CBIAbstractValue.extend({
(optionEl.lastChild || optionEl).appendChild(nodes); (optionEl.lastChild || optionEl).appendChild(nodes);
if (!in_table && typeof(this.description) === 'string' && this.description !== '') if (!in_table && typeof(this.description) === 'string' && this.description !== '')
L.dom.append(optionEl.lastChild || optionEl, dom.append(optionEl.lastChild || optionEl,
E('div', { 'class': 'cbi-value-description' }, this.description)); E('div', { 'class': 'cbi-value-description' }, this.description));
if (depend_list && depend_list.length) if (depend_list && depend_list.length)
@ -1678,7 +1680,7 @@ var CBIValue = CBIAbstractValue.extend({
optionEl.addEventListener('widget-change', optionEl.addEventListener('widget-change',
L.bind(this.map.checkDepends, this.map)); L.bind(this.map.checkDepends, this.map));
L.dom.bindClassInstance(optionEl, this); dom.bindClassInstance(optionEl, this);
return optionEl; return optionEl;
}, },
@ -1877,7 +1879,7 @@ var CBIDummyValue = CBIValue.extend({
if (this.href) if (this.href)
outputEl.appendChild(E('a', { 'href': this.href })); outputEl.appendChild(E('a', { 'href': this.href }));
L.dom.append(outputEl.lastChild || outputEl, dom.append(outputEl.lastChild || outputEl,
this.rawhtml ? value : [ value ]); this.rawhtml ? value : [ value ]);
return E([ return E([
@ -1900,10 +1902,10 @@ var CBIButtonValue = CBIValue.extend({
btn_title = this.titleFn('inputtitle', section_id) || this.titleFn('title', section_id); btn_title = this.titleFn('inputtitle', section_id) || this.titleFn('title', section_id);
if (value !== false) if (value !== false)
L.dom.content(outputEl, [ dom.content(outputEl, [
E('button', { E('button', {
'class': 'cbi-button cbi-button-%s'.format(this.inputstyle || 'button'), 'class': 'cbi-button cbi-button-%s'.format(this.inputstyle || 'button'),
'click': L.ui.createHandlerFn(this, function(section_id, ev) { 'click': ui.createHandlerFn(this, function(section_id, ev) {
if (this.onclick) if (this.onclick)
return this.onclick(ev, section_id); return this.onclick(ev, section_id);
@ -1913,7 +1915,7 @@ var CBIButtonValue = CBIValue.extend({
}, [ btn_title ]) }, [ btn_title ])
]); ]);
else else
L.dom.content(outputEl, ' - '); dom.content(outputEl, ' - ');
return E([ return E([
outputEl, outputEl,
@ -1995,7 +1997,7 @@ var CBISectionValue = CBIValue.extend({
formvalue: function() { return null } formvalue: function() { return null }
}); });
return L.Class.extend({ return baseclass.extend({
Map: CBIMap, Map: CBIMap,
JSONMap: CBIJSONMap, JSONMap: CBIJSONMap,
AbstractSection: CBIAbstractSection, AbstractSection: CBIAbstractSection,

View file

@ -1,5 +1,7 @@
'use strict'; 'use strict';
'require rpc'; 'require rpc';
'require request';
'require baseclass';
/** /**
* @typedef {Object} FileStatEntry * @typedef {Object} FileStatEntry
@ -152,7 +154,7 @@ function handleCgiIoReply(res) {
* To import the class in views, use `'require fs'`, to import it in * To import the class in views, use `'require fs'`, to import it in
* external JavaScript, use `L.require("fs").then(...)`. * external JavaScript, use `L.require("fs").then(...)`.
*/ */
var FileSystem = L.Class.extend(/** @lends LuCI.fs.prototype */ { var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
/** /**
* Obtains a listing of the specified directory. * Obtains a listing of the specified directory.
* *
@ -357,7 +359,7 @@ var FileSystem = L.Class.extend(/** @lends LuCI.fs.prototype */ {
var postdata = 'sessionid=%s&path=%s' var postdata = 'sessionid=%s&path=%s'
.format(encodeURIComponent(L.env.sessionid), encodeURIComponent(path)); .format(encodeURIComponent(L.env.sessionid), encodeURIComponent(path));
return L.Request.post(L.env.cgi_base + '/cgi-download', postdata, { return request.post(L.env.cgi_base + '/cgi-download', postdata, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: (type == 'blob') ? 'blob' : 'text' responseType: (type == 'blob') ? 'blob' : 'text'
}).then(handleCgiIoReply.bind({ type: type })); }).then(handleCgiIoReply.bind({ type: type }));
@ -417,7 +419,7 @@ var FileSystem = L.Class.extend(/** @lends LuCI.fs.prototype */ {
var postdata = 'sessionid=%s&command=%s' var postdata = 'sessionid=%s&command=%s'
.format(encodeURIComponent(L.env.sessionid), cmdstr); .format(encodeURIComponent(L.env.sessionid), cmdstr);
return L.Request.post(L.env.cgi_base + '/cgi-exec', postdata, { return request.post(L.env.cgi_base + '/cgi-exec', postdata, {
headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
responseType: (type == 'blob') ? 'blob' : 'text' responseType: (type == 'blob') ? 'blob' : 'text'
}).then(handleCgiIoReply.bind({ type: type })); }).then(handleCgiIoReply.bind({ type: type }));

File diff suppressed because it is too large Load diff

View file

@ -2,6 +2,8 @@
'require uci'; 'require uci';
'require rpc'; 'require rpc';
'require validation'; 'require validation';
'require baseclass';
'require firewall';
var proto_errors = { var proto_errors = {
CONNECT_FAILED: _('Connection attempt failed'), CONNECT_FAILED: _('Connection attempt failed'),
@ -632,18 +634,18 @@ function enumerateNetworks() {
var Hosts, Network, Protocol, Device, WifiDevice, WifiNetwork; var Hosts, Network, Protocol, Device, WifiDevice, WifiNetwork;
/** /**
* @class * @class network
* @memberof LuCI * @memberof LuCI
* @hideconstructor * @hideconstructor
* @classdesc * @classdesc
* *
* The `LuCI.Network` class combines data from multiple `ubus` apis to * The `LuCI.network` class combines data from multiple `ubus` apis to
* provide an abstraction of the current network configuration state. * provide an abstraction of the current network configuration state.
* *
* It provides methods to enumerate interfaces and devices, to query * It provides methods to enumerate interfaces and devices, to query
* current configuration details and to manipulate settings. * current configuration details and to manipulate settings.
*/ */
Network = L.Class.extend(/** @lends LuCI.Network.prototype */ { Network = baseclass.extend(/** @lends LuCI.network.prototype */ {
/** /**
* Converts the given prefix size in bits to a netmask. * Converts the given prefix size in bits to a netmask.
* *
@ -686,8 +688,8 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
* such as the used key management protocols, active ciphers and * such as the used key management protocols, active ciphers and
* protocol versions. * protocol versions.
* *
* @typedef {Object<string, boolean|Array<number|string>>} LuCI.Network.WifiEncryption * @typedef {Object<string, boolean|Array<number|string>>} LuCI.network.WifiEncryption
* @memberof LuCI.Network * @memberof LuCI.network
* *
* @property {boolean} enabled * @property {boolean} enabled
* Specifies whether any kind of encryption, such as `WEP` or `WPA` is * Specifies whether any kind of encryption, such as `WEP` or `WPA` is
@ -721,13 +723,13 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
*/ */
/** /**
* Converts a given {@link LuCI.Network.WifiEncryption encryption entry} * Converts a given {@link LuCI.network.WifiEncryption encryption entry}
* into a human readable string such as `mixed WPA/WPA2 PSK (TKIP, CCMP)` * into a human readable string such as `mixed WPA/WPA2 PSK (TKIP, CCMP)`
* or `WPA3 SAE (CCMP)`. * or `WPA3 SAE (CCMP)`.
* *
* @method * @method
* *
* @param {LuCI.Network.WifiEncryption} encryption * @param {LuCI.network.WifiEncryption} encryption
* The wireless encryption entry to convert. * The wireless encryption entry to convert.
* *
* @returns {null|string} * @returns {null|string}
@ -749,7 +751,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
}, },
/** /**
* Instantiates the given {@link LuCI.Network.Protocol Protocol} backend, * Instantiates the given {@link LuCI.network.Protocol Protocol} backend,
* optionally using the given network name. * optionally using the given network name.
* *
* @param {string} protoname * @param {string} protoname
@ -761,7 +763,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
* but it is allowed to omit it, e.g. to query protocol capabilities * but it is allowed to omit it, e.g. to query protocol capabilities
* without the need for an existing interface. * without the need for an existing interface.
* *
* @returns {null|LuCI.Network.Protocol} * @returns {null|LuCI.network.Protocol}
* Returns the instantiated protocol backend class or `null` if the given * Returns the instantiated protocol backend class or `null` if the given
* protocol isn't known. * protocol isn't known.
*/ */
@ -774,10 +776,10 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
}, },
/** /**
* Obtains instances of all known {@link LuCI.Network.Protocol Protocol} * Obtains instances of all known {@link LuCI.network.Protocol Protocol}
* backend classes. * backend classes.
* *
* @returns {Array<LuCI.Network.Protocol>} * @returns {Array<LuCI.network.Protocol>}
* Returns an array of protocol class instances. * Returns an array of protocol class instances.
*/ */
getProtocols: function() { getProtocols: function() {
@ -790,7 +792,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
}, },
/** /**
* Registers a new {@link LuCI.Network.Protocol Protocol} subclass * Registers a new {@link LuCI.network.Protocol Protocol} subclass
* with the given methods and returns the resulting subclass value. * with the given methods and returns the resulting subclass value.
* *
* This functions internally calls * This functions internally calls
@ -804,7 +806,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
* The member methods and values of the new `Protocol` subclass to * The member methods and values of the new `Protocol` subclass to
* be passed to {@link LuCI.Class.extend Class.extend()}. * be passed to {@link LuCI.Class.extend Class.extend()}.
* *
* @returns {LuCI.Network.Protocol} * @returns {LuCI.network.Protocol}
* Returns the new `Protocol` subclass. * Returns the new `Protocol` subclass.
*/ */
registerProtocol: function(protoname, methods) { registerProtocol: function(protoname, methods) {
@ -893,7 +895,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
* An object of uci option values to set on the new network or to * An object of uci option values to set on the new network or to
* update in an existing, empty network. * update in an existing, empty network.
* *
* @returns {Promise<null|LuCI.Network.Protocol>} * @returns {Promise<null|LuCI.network.Protocol>}
* Returns a promise resolving to the `Protocol` subclass instance * Returns a promise resolving to the `Protocol` subclass instance
* describing the added network or resolving to `null` if the name * describing the added network or resolving to `null` if the name
* was invalid or if a non-empty network of the given name already * was invalid or if a non-empty network of the given name already
@ -925,15 +927,15 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
}, },
/** /**
* Get a {@link LuCI.Network.Protocol Protocol} instance describing * Get a {@link LuCI.network.Protocol Protocol} instance describing
* the network with the given name. * the network with the given name.
* *
* @param {string} name * @param {string} name
* The logical interface name of the network get, e.g. `lan` or `wan`. * The logical interface name of the network get, e.g. `lan` or `wan`.
* *
* @returns {Promise<null|LuCI.Network.Protocol>} * @returns {Promise<null|LuCI.network.Protocol>}
* Returns a promise resolving to a * Returns a promise resolving to a
* {@link LuCI.Network.Protocol Protocol} subclass instance describing * {@link LuCI.network.Protocol Protocol} subclass instance describing
* the network or `null` if the network did not exist. * the network or `null` if the network did not exist.
*/ */
getNetwork: function(name) { getNetwork: function(name) {
@ -956,9 +958,9 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
/** /**
* Gets an array containing all known networks. * Gets an array containing all known networks.
* *
* @returns {Promise<Array<LuCI.Network.Protocol>>} * @returns {Promise<Array<LuCI.network.Protocol>>}
* Returns a promise resolving to a name-sorted array of * Returns a promise resolving to a name-sorted array of
* {@link LuCI.Network.Protocol Protocol} subclass instances * {@link LuCI.network.Protocol Protocol} subclass instances
* describing all known networks. * describing all known networks.
*/ */
getNetworks: function() { getNetworks: function() {
@ -981,8 +983,9 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
var requireFirewall = Promise.resolve(L.require('firewall')).catch(function() {}), var requireFirewall = Promise.resolve(L.require('firewall')).catch(function() {}),
network = this.instantiateNetwork(name); network = this.instantiateNetwork(name);
return Promise.all([ requireFirewall, initNetworkState() ]).then(function() { return Promise.all([ requireFirewall, initNetworkState() ]).then(function(res) {
var uciInterface = uci.get('network', name); var uciInterface = uci.get('network', name),
firewall = res[0];
if (uciInterface != null && uciInterface['.type'] == 'interface') { if (uciInterface != null && uciInterface['.type'] == 'interface') {
return Promise.resolve(network ? network.deleteConfiguration() : null).then(function() { return Promise.resolve(network ? network.deleteConfiguration() : null).then(function() {
@ -1017,8 +1020,8 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
uci.unset('wireless', s['.name'], 'network'); uci.unset('wireless', s['.name'], 'network');
}); });
if (L.firewall) if (firewall)
return L.firewall.deleteNetwork(name).then(function() { return true }); return firewall.deleteNetwork(name).then(function() { return true });
return true; return true;
}).catch(function() { }).catch(function() {
@ -1096,13 +1099,13 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
}, },
/** /**
* Get a {@link LuCI.Network.Device Device} instance describing the * Get a {@link LuCI.network.Device Device} instance describing the
* given network device. * given network device.
* *
* @param {string} name * @param {string} name
* The name of the network device to get, e.g. `eth0` or `br-lan`. * The name of the network device to get, e.g. `eth0` or `br-lan`.
* *
* @returns {Promise<null|LuCI.Network.Device>} * @returns {Promise<null|LuCI.network.Device>}
* Returns a promise resolving to the `Device` instance describing * Returns a promise resolving to the `Device` instance describing
* the network device or `null` if the given device name could not * the network device or `null` if the given device name could not
* be found. * be found.
@ -1126,7 +1129,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
/** /**
* Get a sorted list of all found network devices. * Get a sorted list of all found network devices.
* *
* @returns {Promise<Array<LuCI.Network.Device>>} * @returns {Promise<Array<LuCI.network.Device>>}
* Returns a promise resolving to a sorted array of `Device` class * Returns a promise resolving to a sorted array of `Device` class
* instances describing the network devices found on the system. * instances describing the network devices found on the system.
*/ */
@ -1251,14 +1254,14 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
}, },
/** /**
* Get a {@link LuCI.Network.WifiDevice WifiDevice} instance describing * Get a {@link LuCI.network.WifiDevice WifiDevice} instance describing
* the given wireless radio. * the given wireless radio.
* *
* @param {string} devname * @param {string} devname
* The configuration name of the wireless radio to lookup, e.g. `radio0` * The configuration name of the wireless radio to lookup, e.g. `radio0`
* for the first mac80211 phy on the system. * for the first mac80211 phy on the system.
* *
* @returns {Promise<null|LuCI.Network.WifiDevice>} * @returns {Promise<null|LuCI.network.WifiDevice>}
* Returns a promise resolving to the `WifiDevice` instance describing * Returns a promise resolving to the `WifiDevice` instance describing
* the underlying radio device or `null` if the wireless radio could not * the underlying radio device or `null` if the wireless radio could not
* be found. * be found.
@ -1277,7 +1280,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
/** /**
* Obtain a list of all configured radio devices. * Obtain a list of all configured radio devices.
* *
* @returns {Promise<Array<LuCI.Network.WifiDevice>>} * @returns {Promise<Array<LuCI.network.WifiDevice>>}
* Returns a promise resolving to an array of `WifiDevice` instances * Returns a promise resolving to an array of `WifiDevice` instances
* describing the wireless radios configured in the system. * describing the wireless radios configured in the system.
* The order of the array corresponds to the order of the radios in * The order of the array corresponds to the order of the radios in
@ -1298,7 +1301,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
}, },
/** /**
* Get a {@link LuCI.Network.WifiNetwork WifiNetwork} instance describing * Get a {@link LuCI.network.WifiNetwork WifiNetwork} instance describing
* the given wireless network. * the given wireless network.
* *
* @param {string} netname * @param {string} netname
@ -1307,7 +1310,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
* or a Linux network device name like `wlan0` which is resolved to the * or a Linux network device name like `wlan0` which is resolved to the
* corresponding configuration section through `ubus` runtime information. * corresponding configuration section through `ubus` runtime information.
* *
* @returns {Promise<null|LuCI.Network.WifiNetwork>} * @returns {Promise<null|LuCI.network.WifiNetwork>}
* Returns a promise resolving to the `WifiNetwork` instance describing * Returns a promise resolving to the `WifiNetwork` instance describing
* the wireless network or `null` if the corresponding network could not * the wireless network or `null` if the corresponding network could not
* be found. * be found.
@ -1318,10 +1321,10 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
}, },
/** /**
* Get an array of all {@link LuCI.Network.WifiNetwork WifiNetwork} * Get an array of all {@link LuCI.network.WifiNetwork WifiNetwork}
* instances describing the wireless networks present on the system. * instances describing the wireless networks present on the system.
* *
* @returns {Promise<Array<LuCI.Network.WifiNetwork>>} * @returns {Promise<Array<LuCI.network.WifiNetwork>>}
* Returns a promise resolving to an array of `WifiNetwork` instances * Returns a promise resolving to an array of `WifiNetwork` instances
* describing the wireless networks. The array will be empty if no networks * describing the wireless networks. The array will be empty if no networks
* are found. * are found.
@ -1351,7 +1354,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
* must at least contain a `device` property which is set to the radio * must at least contain a `device` property which is set to the radio
* name the new network belongs to. * name the new network belongs to.
* *
* @returns {Promise<null|LuCI.Network.WifiNetwork>} * @returns {Promise<null|LuCI.network.WifiNetwork>}
* Returns a promise resolving to a `WifiNetwork` instance describing * Returns a promise resolving to a `WifiNetwork` instance describing
* the newly added wireless network or `null` if the given options * the newly added wireless network or `null` if the given options
* were invalid or if the associated radio device could not be found. * were invalid or if the associated radio device could not be found.
@ -1472,7 +1475,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
* This function looks up all networks having a default `0.0.0.0/0` route * This function looks up all networks having a default `0.0.0.0/0` route
* and returns them as array. * and returns them as array.
* *
* @returns {Promise<Array<LuCI.Network.Protocol>>} * @returns {Promise<Array<LuCI.network.Protocol>>}
* Returns a promise resolving to an array of `Protocol` subclass * Returns a promise resolving to an array of `Protocol` subclass
* instances describing the found default route interfaces. * instances describing the found default route interfaces.
*/ */
@ -1497,7 +1500,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
* This function looks up all networks having a default `::/0` route * This function looks up all networks having a default `::/0` route
* and returns them as array. * and returns them as array.
* *
* @returns {Promise<Array<LuCI.Network.Protocol>>} * @returns {Promise<Array<LuCI.network.Protocol>>}
* Returns a promise resolving to an array of `Protocol` subclass * Returns a promise resolving to an array of `Protocol` subclass
* instances describing the found IPv6 default route interfaces. * instances describing the found IPv6 default route interfaces.
*/ */
@ -1521,7 +1524,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
* connections and external port labels of a switch. * connections and external port labels of a switch.
* *
* @typedef {Object<string, Object|Array>} SwitchTopology * @typedef {Object<string, Object|Array>} SwitchTopology
* @memberof LuCI.Network * @memberof LuCI.network
* *
* @property {Object<number, string>} netdevs * @property {Object<number, string>} netdevs
* The `netdevs` property points to an object describing the CPU port * The `netdevs` property points to an object describing the CPU port
@ -1543,11 +1546,11 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
/** /**
* Returns the topologies of all swconfig switches found on the system. * Returns the topologies of all swconfig switches found on the system.
* *
* @returns {Promise<Object<string, LuCI.Network.SwitchTopology>>} * @returns {Promise<Object<string, LuCI.network.SwitchTopology>>}
* Returns a promise resolving to an object containing the topologies * Returns a promise resolving to an object containing the topologies
* of each switch. The object keys correspond to the name of the switches * of each switch. The object keys correspond to the name of the switches
* such as `switch0`, the values are * such as `switch0`, the values are
* {@link LuCI.Network.SwitchTopology SwitchTopology} objects describing * {@link LuCI.network.SwitchTopology SwitchTopology} objects describing
* the layout. * the layout.
*/ */
getSwitchTopologies: function() { getSwitchTopologies: function() {
@ -1638,7 +1641,7 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
/** /**
* Obtains the the network device name of the given object. * Obtains the the network device name of the given object.
* *
* @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} obj * @param {LuCI.network.Protocol|LuCI.network.Device|LuCI.network.WifiDevice|LuCI.network.WifiNetwork|string} obj
* The object to get the device name from. * The object to get the device name from.
* *
* @returns {null|string} * @returns {null|string}
@ -1667,10 +1670,10 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
* *
* This function aggregates information from various sources such as * This function aggregates information from various sources such as
* DHCP lease databases, ARP and IPv6 neighbour entries, wireless * DHCP lease databases, ARP and IPv6 neighbour entries, wireless
* association list etc. and returns a {@link LuCI.Network.Hosts Hosts} * association list etc. and returns a {@link LuCI.network.Hosts Hosts}
* class instance describing the found hosts. * class instance describing the found hosts.
* *
* @returns {Promise<LuCI.Network.Hosts>} * @returns {Promise<LuCI.network.Hosts>}
* Returns a `Hosts` instance describing host known on the system. * Returns a `Hosts` instance describing host known on the system.
*/ */
getHostHints: function() { getHostHints: function() {
@ -1682,15 +1685,15 @@ Network = L.Class.extend(/** @lends LuCI.Network.prototype */ {
/** /**
* @class * @class
* @memberof LuCI.Network * @memberof LuCI.network
* @hideconstructor * @hideconstructor
* @classdesc * @classdesc
* *
* The `LuCI.Network.Hosts` class encapsulates host information aggregated * The `LuCI.network.Hosts` class encapsulates host information aggregated
* from multiple sources and provides convenience functions to access the * from multiple sources and provides convenience functions to access the
* host information by different criteria. * host information by different criteria.
*/ */
Hosts = L.Class.extend(/** @lends LuCI.Network.Hosts.prototype */ { Hosts = baseclass.extend(/** @lends LuCI.network.Hosts.prototype */ {
__init__: function(hosts) { __init__: function(hosts) {
this.hosts = hosts; this.hosts = hosts;
}, },
@ -1848,7 +1851,7 @@ Hosts = L.Class.extend(/** @lends LuCI.Network.Hosts.prototype */ {
/** /**
* @class * @class
* @memberof LuCI.Network * @memberof LuCI.network
* @hideconstructor * @hideconstructor
* @classdesc * @classdesc
* *
@ -1856,7 +1859,7 @@ Hosts = L.Class.extend(/** @lends LuCI.Network.Hosts.prototype */ {
* subclasses which describe logical UCI networks defined by `config * subclasses which describe logical UCI networks defined by `config
* interface` sections in `/etc/config/network`. * interface` sections in `/etc/config/network`.
*/ */
Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ { Protocol = baseclass.extend(/** @lends LuCI.network.Protocol.prototype */ {
__init__: function(name) { __init__: function(name) {
this.sid = name; this.sid = name;
}, },
@ -1933,7 +1936,7 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
* Get the name of this network protocol class. * Get the name of this network protocol class.
* *
* This function will be overwritten by subclasses created by * This function will be overwritten by subclasses created by
* {@link LuCI.Network#registerProtocol Network.registerProtocol()}. * {@link LuCI.network#registerProtocol Network.registerProtocol()}.
* *
* @abstract * @abstract
* @returns {string} * @returns {string}
@ -1967,7 +1970,7 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
* Get the type of the underlying interface. * Get the type of the underlying interface.
* *
* This function actually is a convenience wrapper around * This function actually is a convenience wrapper around
* `proto.get("type")` and is mainly used by other `LuCI.Network` code * `proto.get("type")` and is mainly used by other `LuCI.network` code
* to check whether the interface is declared as bridge in UCI. * to check whether the interface is declared as bridge in UCI.
* *
* @returns {null|string} * @returns {null|string}
@ -2251,7 +2254,7 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
* *
* This function will translate the found error codes to human readable * This function will translate the found error codes to human readable
* messages using the descriptions registered by * messages using the descriptions registered by
* {@link LuCI.Network#registerErrorCode Network.registerErrorCode()} * {@link LuCI.network#registerErrorCode Network.registerErrorCode()}
* and fall back to `"Unknown error (%s)"` where `%s` is replaced by the * and fall back to `"Unknown error (%s)"` where `%s` is replaced by the
* error code in case no translation can be found. * error code in case no translation can be found.
* *
@ -2302,23 +2305,6 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
return null; return null;
}, },
/**
* Check function for the protocol handler if a new interface is createable.
*
* This function should be overwritten by protocol specific subclasses.
*
* @abstract
*
* @param {string} ifname
* The name of the interface to be created.
*
* @returns {Promise<null|string>}
* Returns `null` if new interface is createable, else returns (error) message.
*/
isCreateable: function(ifname) {
return Promise.resolve(null);
},
/** /**
* Checks whether the protocol functionality is installed. * Checks whether the protocol functionality is installed.
* *
@ -2452,10 +2438,10 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
/** /**
* Add the given network device to the logical interface. * Add the given network device to the logical interface.
* *
* @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} device * @param {LuCI.network.Protocol|LuCI.network.Device|LuCI.network.WifiDevice|LuCI.network.WifiNetwork|string} device
* The object or device name to add to the logical interface. In case the * The object or device name to add to the logical interface. In case the
* given argument is not a string, it is resolved though the * given argument is not a string, it is resolved though the
* {@link LuCI.Network#getIfnameOf Network.getIfnameOf()} function. * {@link LuCI.network#getIfnameOf Network.getIfnameOf()} function.
* *
* @returns {boolean} * @returns {boolean}
* Returns `true` if the device name has been added or `false` if any * Returns `true` if the device name has been added or `false` if any
@ -2479,10 +2465,10 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
/** /**
* Remove the given network device from the logical interface. * Remove the given network device from the logical interface.
* *
* @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} device * @param {LuCI.network.Protocol|LuCI.network.Device|LuCI.network.WifiDevice|LuCI.network.WifiNetwork|string} device
* The object or device name to remove from the logical interface. In case * The object or device name to remove from the logical interface. In case
* the given argument is not a string, it is resolved though the * the given argument is not a string, it is resolved though the
* {@link LuCI.Network#getIfnameOf Network.getIfnameOf()} function. * {@link LuCI.network#getIfnameOf Network.getIfnameOf()} function.
* *
* @returns {boolean} * @returns {boolean}
* Returns `true` if the device name has been added or `false` if any * Returns `true` if the device name has been added or `false` if any
@ -2512,7 +2498,7 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
* Returns the Linux network device associated with this logical * Returns the Linux network device associated with this logical
* interface. * interface.
* *
* @returns {LuCI.Network.Device} * @returns {LuCI.network.Device}
* Returns a `Network.Device` class instance representing the * Returns a `Network.Device` class instance representing the
* expected Linux network device according to the configuration. * expected Linux network device according to the configuration.
*/ */
@ -2520,7 +2506,7 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
if (this.isVirtual()) { if (this.isVirtual()) {
var ifname = '%s-%s'.format(this.getProtocol(), this.sid); var ifname = '%s-%s'.format(this.getProtocol(), this.sid);
_state.isTunnel[this.getProtocol() + '-' + this.sid] = true; _state.isTunnel[this.getProtocol() + '-' + this.sid] = true;
return L.network.instantiateDevice(ifname, this); return Network.prototype.instantiateDevice(ifname, this);
} }
else if (this.isBridge()) { else if (this.isBridge()) {
var ifname = 'br-%s'.format(this.sid); var ifname = 'br-%s'.format(this.sid);
@ -2532,12 +2518,12 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
for (var i = 0; i < ifnames.length; i++) { for (var i = 0; i < ifnames.length; i++) {
var m = ifnames[i].match(/^([^:/]+)/); var m = ifnames[i].match(/^([^:/]+)/);
return ((m && m[1]) ? L.network.instantiateDevice(m[1], this) : null); return ((m && m[1]) ? Network.prototype.instantiateDevice(m[1], this) : null);
} }
ifname = getWifiNetidByNetname(this.sid); ifname = getWifiNetidByNetname(this.sid);
return (ifname != null ? L.network.instantiateDevice(ifname[0], this) : null); return (ifname != null ? Network.prototype.instantiateDevice(ifname[0], this) : null);
} }
}, },
@ -2545,33 +2531,33 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
* Returns the layer 2 linux network device currently associated * Returns the layer 2 linux network device currently associated
* with this logical interface. * with this logical interface.
* *
* @returns {LuCI.Network.Device} * @returns {LuCI.network.Device}
* Returns a `Network.Device` class instance representing the Linux * Returns a `Network.Device` class instance representing the Linux
* network device currently associated with the logical interface. * network device currently associated with the logical interface.
*/ */
getL2Device: function() { getL2Device: function() {
var ifname = this._ubus('device'); var ifname = this._ubus('device');
return (ifname != null ? L.network.instantiateDevice(ifname, this) : null); return (ifname != null ? Network.prototype.instantiateDevice(ifname, this) : null);
}, },
/** /**
* Returns the layer 3 linux network device currently associated * Returns the layer 3 linux network device currently associated
* with this logical interface. * with this logical interface.
* *
* @returns {LuCI.Network.Device} * @returns {LuCI.network.Device}
* Returns a `Network.Device` class instance representing the Linux * Returns a `Network.Device` class instance representing the Linux
* network device currently associated with the logical interface. * network device currently associated with the logical interface.
*/ */
getL3Device: function() { getL3Device: function() {
var ifname = this._ubus('l3_device'); var ifname = this._ubus('l3_device');
return (ifname != null ? L.network.instantiateDevice(ifname, this) : null); return (ifname != null ? Network.prototype.instantiateDevice(ifname, this) : null);
}, },
/** /**
* Returns a list of network sub-devices associated with this logical * Returns a list of network sub-devices associated with this logical
* interface. * interface.
* *
* @returns {null|Array<LuCI.Network.Device>} * @returns {null|Array<LuCI.network.Device>}
* Returns an array of of `Network.Device` class instances representing * Returns an array of of `Network.Device` class instances representing
* the sub-devices attached to this logical interface or `null` if the * the sub-devices attached to this logical interface or `null` if the
* logical interface does not support sub-devices, e.g. because it is * logical interface does not support sub-devices, e.g. because it is
@ -2591,7 +2577,7 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
var m = ifnames[i].match(/^([^:/]+)/); var m = ifnames[i].match(/^([^:/]+)/);
if (m != null) if (m != null)
rv.push(L.network.instantiateDevice(m[1], this)); rv.push(Network.prototype.instantiateDevice(m[1], this));
} }
var uciWifiIfaces = uci.sections('wireless', 'wifi-iface'); var uciWifiIfaces = uci.sections('wireless', 'wifi-iface');
@ -2609,7 +2595,7 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
var netid = getWifiNetidBySid(uciWifiIfaces[i]['.name']); var netid = getWifiNetidBySid(uciWifiIfaces[i]['.name']);
if (netid != null) if (netid != null)
rv.push(L.network.instantiateDevice(netid[0], this)); rv.push(Network.prototype.instantiateDevice(netid[0], this));
} }
} }
@ -2622,10 +2608,10 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
* Checks whether this logical interface contains the given device * Checks whether this logical interface contains the given device
* object. * object.
* *
* @param {LuCI.Network.Protocol|LuCI.Network.Device|LuCI.Network.WifiDevice|LuCI.Network.WifiNetwork|string} device * @param {LuCI.network.Protocol|LuCI.network.Device|LuCI.network.WifiDevice|LuCI.network.WifiNetwork|string} device
* The object or device name to check. In case the given argument is not * The object or device name to check. In case the given argument is not
* a string, it is resolved though the * a string, it is resolved though the
* {@link LuCI.Network#getIfnameOf Network.getIfnameOf()} function. * {@link LuCI.network#getIfnameOf Network.getIfnameOf()} function.
* *
* @returns {boolean} * @returns {boolean}
* Returns `true` when this logical interface contains the given network * Returns `true` when this logical interface contains the given network
@ -2684,14 +2670,14 @@ Protocol = L.Class.extend(/** @lends LuCI.Network.Protocol.prototype */ {
/** /**
* @class * @class
* @memberof LuCI.Network * @memberof LuCI.network
* @hideconstructor * @hideconstructor
* @classdesc * @classdesc
* *
* A `Network.Device` class instance represents an underlying Linux network * A `Network.Device` class instance represents an underlying Linux network
* device and allows querying device details such as packet statistics or MTU. * device and allows querying device details such as packet statistics or MTU.
*/ */
Device = L.Class.extend(/** @lends LuCI.Network.Device.prototype */ { Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
__init__: function(ifname, network) { __init__: function(ifname, network) {
var wif = getWifiSidByIfname(ifname); var wif = getWifiSidByIfname(ifname);
@ -2871,7 +2857,7 @@ Device = L.Class.extend(/** @lends LuCI.Network.Device.prototype */ {
/** /**
* Get the associated bridge ports of the device. * Get the associated bridge ports of the device.
* *
* @returns {null|Array<LuCI.Network.Device>} * @returns {null|Array<LuCI.network.Device>}
* Returns an array of `Network.Device` instances representing the ports * Returns an array of `Network.Device` instances representing the ports
* (slave interfaces) of the bridge or `null` when this device isn't * (slave interfaces) of the bridge or `null` when this device isn't
* a Linux bridge. * a Linux bridge.
@ -2884,7 +2870,7 @@ Device = L.Class.extend(/** @lends LuCI.Network.Device.prototype */ {
return null; return null;
for (var i = 0; i < br.ifnames.length; i++) for (var i = 0; i < br.ifnames.length; i++)
rv.push(L.network.instantiateDevice(br.ifnames[i].name)); rv.push(Network.prototype.instantiateDevice(br.ifnames[i].name));
rv.sort(deviceSort); rv.sort(deviceSort);
@ -3000,7 +2986,7 @@ Device = L.Class.extend(/** @lends LuCI.Network.Device.prototype */ {
/** /**
* Get the primary logical interface this device is assigned to. * Get the primary logical interface this device is assigned to.
* *
* @returns {null|LuCI.Network.Protocol} * @returns {null|LuCI.network.Protocol}
* Returns a `Network.Protocol` instance representing the logical * Returns a `Network.Protocol` instance representing the logical
* interface this device is attached to or `null` if it is not * interface this device is attached to or `null` if it is not
* assigned to any logical interface. * assigned to any logical interface.
@ -3012,7 +2998,7 @@ Device = L.Class.extend(/** @lends LuCI.Network.Device.prototype */ {
/** /**
* Get the logical interfaces this device is assigned to. * Get the logical interfaces this device is assigned to.
* *
* @returns {Array<LuCI.Network.Protocol>} * @returns {Array<LuCI.network.Protocol>}
* Returns an array of `Network.Protocol` instances representing the * Returns an array of `Network.Protocol` instances representing the
* logical interfaces this device is assigned to. * logical interfaces this device is assigned to.
*/ */
@ -3035,7 +3021,7 @@ Device = L.Class.extend(/** @lends LuCI.Network.Device.prototype */ {
/** /**
* Get the related wireless network this device is related to. * Get the related wireless network this device is related to.
* *
* @returns {null|LuCI.Network.WifiNetwork} * @returns {null|LuCI.network.WifiNetwork}
* Returns a `Network.WifiNetwork` instance representing the wireless * Returns a `Network.WifiNetwork` instance representing the wireless
* network corresponding to this network device or `null` if this device * network corresponding to this network device or `null` if this device
* is not a wireless device. * is not a wireless device.
@ -3047,7 +3033,7 @@ Device = L.Class.extend(/** @lends LuCI.Network.Device.prototype */ {
/** /**
* @class * @class
* @memberof LuCI.Network * @memberof LuCI.network
* @hideconstructor * @hideconstructor
* @classdesc * @classdesc
* *
@ -3055,7 +3041,7 @@ Device = L.Class.extend(/** @lends LuCI.Network.Device.prototype */ {
* present on the system and provides wireless capability information as * present on the system and provides wireless capability information as
* well as methods for enumerating related wireless networks. * well as methods for enumerating related wireless networks.
*/ */
WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ { WifiDevice = baseclass.extend(/** @lends LuCI.network.WifiDevice.prototype */ {
__init__: function(name, radiostate) { __init__: function(name, radiostate) {
var uciWifiDevice = uci.get('wireless', name); var uciWifiDevice = uci.get('wireless', name);
@ -3207,8 +3193,8 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
* A wireless scan result object describes a neighbouring wireless * A wireless scan result object describes a neighbouring wireless
* network found in the vincinity. * network found in the vincinity.
* *
* @typedef {Object<string, number|string|LuCI.Network.WifiEncryption>} WifiScanResult * @typedef {Object<string, number|string|LuCI.network.WifiEncryption>} WifiScanResult
* @memberof LuCI.Network * @memberof LuCI.network
* *
* @property {string} ssid * @property {string} ssid
* The SSID / Mesh ID of the network. * The SSID / Mesh ID of the network.
@ -3233,7 +3219,7 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
* The maximum possible quality level of the signal, can be used in * The maximum possible quality level of the signal, can be used in
* conjunction with `quality` to calculate a quality percentage. * conjunction with `quality` to calculate a quality percentage.
* *
* @property {LuCI.Network.WifiEncryption} encryption * @property {LuCI.network.WifiEncryption} encryption
* The encryption used by the wireless network. * The encryption used by the wireless network.
*/ */
@ -3241,7 +3227,7 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
* Trigger a wireless scan on this radio device and obtain a list of * Trigger a wireless scan on this radio device and obtain a list of
* nearby networks. * nearby networks.
* *
* @returns {Promise<Array<LuCI.Network.WifiScanResult>>} * @returns {Promise<Array<LuCI.network.WifiScanResult>>}
* Returns a promise resolving to an array of scan result objects * Returns a promise resolving to an array of scan result objects
* describing the networks found in the vincinity. * describing the networks found in the vincinity.
*/ */
@ -3272,14 +3258,14 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
* or a Linux network device name like `wlan0` which is resolved to the * or a Linux network device name like `wlan0` which is resolved to the
* corresponding configuration section through `ubus` runtime information. * corresponding configuration section through `ubus` runtime information.
* *
* @returns {Promise<LuCI.Network.WifiNetwork>} * @returns {Promise<LuCI.network.WifiNetwork>}
* Returns a promise resolving to a `Network.WifiNetwork` instance * Returns a promise resolving to a `Network.WifiNetwork` instance
* representing the wireless network and rejecting with `null` if * representing the wireless network and rejecting with `null` if
* the given network could not be found or is not associated with * the given network could not be found or is not associated with
* this radio device. * this radio device.
*/ */
getWifiNetwork: function(network) { getWifiNetwork: function(network) {
return L.network.getWifiNetwork(network).then(L.bind(function(networkInstance) { return Network.prototype.getWifiNetwork(network).then(L.bind(function(networkInstance) {
var uciWifiIface = (networkInstance.sid ? uci.get('wireless', networkInstance.sid) : null); var uciWifiIface = (networkInstance.sid ? uci.get('wireless', networkInstance.sid) : null);
if (uciWifiIface == null || uciWifiIface['.type'] != 'wifi-iface' || uciWifiIface.device != this.sid) if (uciWifiIface == null || uciWifiIface['.type'] != 'wifi-iface' || uciWifiIface.device != this.sid)
@ -3292,13 +3278,13 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
/** /**
* Get all wireless networks associated with this wireless radio device. * Get all wireless networks associated with this wireless radio device.
* *
* @returns {Promise<Array<LuCI.Network.WifiNetwork>>} * @returns {Promise<Array<LuCI.network.WifiNetwork>>}
* Returns a promise resolving to an array of `Network.WifiNetwork` * Returns a promise resolving to an array of `Network.WifiNetwork`
* instances respresenting the wireless networks associated with this * instances respresenting the wireless networks associated with this
* radio device. * radio device.
*/ */
getWifiNetworks: function() { getWifiNetworks: function() {
return L.network.getWifiNetworks().then(L.bind(function(networks) { return Network.prototype.getWifiNetworks().then(L.bind(function(networks) {
var rv = []; var rv = [];
for (var i = 0; i < networks.length; i++) for (var i = 0; i < networks.length; i++)
@ -3316,7 +3302,7 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
* @param {Object<string, string|string[]>} [options] * @param {Object<string, string|string[]>} [options]
* The options to set for the newly added wireless network. * The options to set for the newly added wireless network.
* *
* @returns {Promise<null|LuCI.Network.WifiNetwork>} * @returns {Promise<null|LuCI.network.WifiNetwork>}
* Returns a promise resolving to a `WifiNetwork` instance describing * Returns a promise resolving to a `WifiNetwork` instance describing
* the newly added wireless network or `null` if the given options * the newly added wireless network or `null` if the given options
* were invalid. * were invalid.
@ -3327,7 +3313,7 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
options.device = this.sid; options.device = this.sid;
return L.network.addWifiNetwork(options); return Network.prototype.addWifiNetwork(options);
}, },
/** /**
@ -3370,7 +3356,7 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
/** /**
* @class * @class
* @memberof LuCI.Network * @memberof LuCI.network
* @hideconstructor * @hideconstructor
* @classdesc * @classdesc
* *
@ -3379,7 +3365,7 @@ WifiDevice = L.Class.extend(/** @lends LuCI.Network.WifiDevice.prototype */ {
* the runtime state of the network. Most radio devices support multiple * the runtime state of the network. Most radio devices support multiple
* such networks in parallel. * such networks in parallel.
*/ */
WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ { WifiNetwork = baseclass.extend(/** @lends LuCI.network.WifiNetwork.prototype */ {
__init__: function(sid, radioname, radiostate, netid, netstate, hostapd) { __init__: function(sid, radioname, radiostate, netid, netstate, hostapd) {
this.sid = sid; this.sid = sid;
this.netid = netid; this.netid = netid;
@ -3562,7 +3548,7 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
/** /**
* Get the corresponding wifi radio device. * Get the corresponding wifi radio device.
* *
* @returns {null|LuCI.Network.WifiDevice} * @returns {null|LuCI.network.WifiDevice}
* Returns a `Network.WifiDevice` instance representing the corresponding * Returns a `Network.WifiDevice` instance representing the corresponding
* wifi radio device or `null` if the related radio device could not be * wifi radio device or `null` if the related radio device could not be
* found. * found.
@ -3573,7 +3559,7 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
if (radioname == null) if (radioname == null)
return Promise.reject(); return Promise.reject();
return L.network.getWifiDevice(radioname); return Network.prototype.getWifiDevice(radioname);
}, },
/** /**
@ -3685,8 +3671,8 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
* A wireless peer entry describes the properties of a remote wireless * A wireless peer entry describes the properties of a remote wireless
* peer associated with a local network. * peer associated with a local network.
* *
* @typedef {Object<string, boolean|number|string|LuCI.Network.WifiRateEntry>} WifiPeerEntry * @typedef {Object<string, boolean|number|string|LuCI.network.WifiRateEntry>} WifiPeerEntry
* @memberof LuCI.Network * @memberof LuCI.network
* *
* @property {string} mac * @property {string} mac
* The MAC address (BSSID). * The MAC address (BSSID).
@ -3784,10 +3770,10 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
* - `DEEP SLEEP` * - `DEEP SLEEP`
* - `UNKNOWN` * - `UNKNOWN`
* *
* @property {LuCI.Network.WifiRateEntry} rx * @property {LuCI.network.WifiRateEntry} rx
* Describes the receiving wireless rate from the peer. * Describes the receiving wireless rate from the peer.
* *
* @property {LuCI.Network.WifiRateEntry} tx * @property {LuCI.network.WifiRateEntry} tx
* Describes the transmitting wireless rate to the peer. * Describes the transmitting wireless rate to the peer.
*/ */
@ -3796,7 +3782,7 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
* transmission rate to or from a peer. * transmission rate to or from a peer.
* *
* @typedef {Object<string, boolean|number>} WifiRateEntry * @typedef {Object<string, boolean|number>} WifiRateEntry
* @memberof LuCI.Network * @memberof LuCI.network
* *
* @property {number} [drop_misc] * @property {number} [drop_misc]
* The amount of received misc. packages that have been dropped, e.g. * The amount of received misc. packages that have been dropped, e.g.
@ -3853,7 +3839,7 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
/** /**
* Fetch the list of associated peers. * Fetch the list of associated peers.
* *
* @returns {Promise<Array<LuCI.Network.WifiPeerEntry>>} * @returns {Promise<Array<LuCI.network.WifiPeerEntry>>}
* Returns a promise resolving to an array of wireless peers associated * Returns a promise resolving to an array of wireless peers associated
* with this network. * with this network.
*/ */
@ -4041,7 +4027,7 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
/** /**
* Get the primary logical interface this wireless network is attached to. * Get the primary logical interface this wireless network is attached to.
* *
* @returns {null|LuCI.Network.Protocol} * @returns {null|LuCI.network.Protocol}
* Returns a `Network.Protocol` instance representing the logical * Returns a `Network.Protocol` instance representing the logical
* interface or `null` if this network is not attached to any logical * interface or `null` if this network is not attached to any logical
* interface. * interface.
@ -4053,7 +4039,7 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
/** /**
* Get the logical interfaces this wireless network is attached to. * Get the logical interfaces this wireless network is attached to.
* *
* @returns {Array<LuCI.Network.Protocol>} * @returns {Array<LuCI.network.Protocol>}
* Returns an array of `Network.Protocol` instances representing the * Returns an array of `Network.Protocol` instances representing the
* logical interfaces this wireless network is attached to. * logical interfaces this wireless network is attached to.
*/ */
@ -4067,7 +4053,7 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
if (uciInterface == null || uciInterface['.type'] != 'interface') if (uciInterface == null || uciInterface['.type'] != 'interface')
continue; continue;
networks.push(L.network.instantiateNetwork(networkNames[i])); networks.push(Network.prototype.instantiateNetwork(networkNames[i]));
} }
networks.sort(networkSort); networks.sort(networkSort);
@ -4078,12 +4064,12 @@ WifiNetwork = L.Class.extend(/** @lends LuCI.Network.WifiNetwork.prototype */ {
/** /**
* Get the associated Linux network device. * Get the associated Linux network device.
* *
* @returns {LuCI.Network.Device} * @returns {LuCI.network.Device}
* Returns a `Network.Device` instance representing the Linux network * Returns a `Network.Device` instance representing the Linux network
* device associted with this wireless network. * device associted with this wireless network.
*/ */
getDevice: function() { getDevice: function() {
return L.network.instantiateDevice(this.getIfname()); return Network.prototype.instantiateDevice(this.getIfname());
}, },
/** /**

View file

@ -1,4 +1,6 @@
'use strict'; 'use strict';
'require baseclass';
'require request';
var rpcRequestID = 1, var rpcRequestID = 1,
rpcSessionID = L.env.sessionid || '00000000000000000000000000000000', rpcSessionID = L.env.sessionid || '00000000000000000000000000000000',
@ -14,7 +16,7 @@ var rpcRequestID = 1,
* The `LuCI.rpc` class provides high level ubus JSON-RPC abstractions * The `LuCI.rpc` class provides high level ubus JSON-RPC abstractions
* and means for listing and invoking remove RPC methods. * and means for listing and invoking remove RPC methods.
*/ */
return L.Class.extend(/** @lends LuCI.rpc.prototype */ { return baseclass.extend(/** @lends LuCI.rpc.prototype */ {
/* privates */ /* privates */
call: function(req, cb, nobatch) { call: function(req, cb, nobatch) {
var q = ''; var q = '';
@ -35,7 +37,7 @@ return L.Class.extend(/** @lends LuCI.rpc.prototype */ {
q += '/%s.%s'.format(req.params[1], req.params[2]); q += '/%s.%s'.format(req.params[1], req.params[2]);
} }
return L.Request.post(rpcBaseURL + q, req, { return request.post(rpcBaseURL + q, req, {
timeout: (L.env.rpctimeout || 20) * 1000, timeout: (L.env.rpctimeout || 20) * 1000,
nobatch: nobatch, nobatch: nobatch,
credentials: true credentials: true

View file

@ -1,5 +1,6 @@
'use strict'; 'use strict';
'require rpc'; 'require rpc';
'require baseclass';
/** /**
* @class uci * @class uci
@ -12,7 +13,7 @@
* manipulation layer on top to allow for synchroneous operations on * manipulation layer on top to allow for synchroneous operations on
* UCI configuration data. * UCI configuration data.
*/ */
return L.Class.extend(/** @lends LuCI.uci.prototype */ { return baseclass.extend(/** @lends LuCI.uci.prototype */ {
__init__: function() { __init__: function() {
this.state = { this.state = {
newidx: 0, newidx: 0,

View file

@ -1,7 +1,11 @@
'use strict'; 'use strict';
'require validation';
'require baseclass';
'require request';
'require poll';
'require dom';
'require rpc'; 'require rpc';
'require uci'; 'require uci';
'require validation';
'require fs'; 'require fs';
var modalDiv = null, var modalDiv = null,
@ -29,7 +33,7 @@ var modalDiv = null,
* it in external JavaScript, use `L.require("ui").then(...)` and access the * it in external JavaScript, use `L.require("ui").then(...)` and access the
* `AbstractElement` property of the class instance value. * `AbstractElement` property of the class instance value.
*/ */
var UIElement = L.Class.extend(/** @lends LuCI.ui.AbstractElement.prototype */ { var UIElement = baseclass.extend(/** @lends LuCI.ui.AbstractElement.prototype */ {
/** /**
* @typedef {Object} InitOptions * @typedef {Object} InitOptions
* @memberof LuCI.ui.AbstractElement * @memberof LuCI.ui.AbstractElement
@ -48,7 +52,7 @@ var UIElement = L.Class.extend(/** @lends LuCI.ui.AbstractElement.prototype */ {
* @property {string} [datatype=string] * @property {string} [datatype=string]
* An expression describing the input data validation constraints. * An expression describing the input data validation constraints.
* It defaults to `string` which will allow any value. * It defaults to `string` which will allow any value.
* See{@link LuCI.validation} for details on the expression format. * See {@link LuCI.validation} for details on the expression format.
* *
* @property {function} [validator] * @property {function} [validator]
* Specifies a custom validator function which is invoked after the * Specifies a custom validator function which is invoked after the
@ -69,7 +73,7 @@ var UIElement = L.Class.extend(/** @lends LuCI.ui.AbstractElement.prototype */ {
* an array of strings or `null` for unset values. * an array of strings or `null` for unset values.
*/ */
getValue: function() { getValue: function() {
if (L.dom.matches(this.node, 'select') || L.dom.matches(this.node, 'input')) if (dom.matches(this.node, 'select') || dom.matches(this.node, 'input'))
return this.node.value; return this.node.value;
return null; return null;
@ -87,7 +91,7 @@ var UIElement = L.Class.extend(/** @lends LuCI.ui.AbstractElement.prototype */ {
* or `null` values. * or `null` values.
*/ */
setValue: function(value) { setValue: function(value) {
if (L.dom.matches(this.node, 'select') || L.dom.matches(this.node, 'input')) if (dom.matches(this.node, 'select') || dom.matches(this.node, 'input'))
this.node.value = value; this.node.value = value;
}, },
@ -185,7 +189,7 @@ var UIElement = L.Class.extend(/** @lends LuCI.ui.AbstractElement.prototype */ {
if (!datatype && !validate) if (!datatype && !validate)
return; return;
this.vfunc = L.ui.addValidator.apply(L.ui, [ this.vfunc = UI.prototype.addValidator.apply(UI.prototype, [
targetNode, datatype || 'string', targetNode, datatype || 'string',
optional, validate optional, validate
].concat(events)); ].concat(events));
@ -347,7 +351,7 @@ var UITextfield = UIElement.extend(/** @lends LuCI.ui.Textfield.prototype */ {
this.setUpdateEvents(inputEl, 'keyup', 'blur'); this.setUpdateEvents(inputEl, 'keyup', 'blur');
this.setChangeEvents(inputEl, 'change'); this.setChangeEvents(inputEl, 'change');
L.dom.bindClassInstance(frameEl, this); dom.bindClassInstance(frameEl, this);
return frameEl; return frameEl;
}, },
@ -463,7 +467,7 @@ var UITextarea = UIElement.extend(/** @lends LuCI.ui.Textarea.prototype */ {
this.setUpdateEvents(inputEl, 'keyup', 'blur'); this.setUpdateEvents(inputEl, 'keyup', 'blur');
this.setChangeEvents(inputEl, 'change'); this.setChangeEvents(inputEl, 'change');
L.dom.bindClassInstance(frameEl, this); dom.bindClassInstance(frameEl, this);
return frameEl; return frameEl;
}, },
@ -568,7 +572,7 @@ var UICheckbox = UIElement.extend(/** @lends LuCI.ui.Checkbox.prototype */ {
this.setUpdateEvents(frameEl.lastElementChild.previousElementSibling, 'click', 'blur'); this.setUpdateEvents(frameEl.lastElementChild.previousElementSibling, 'click', 'blur');
this.setChangeEvents(frameEl.lastElementChild.previousElementSibling, 'change'); this.setChangeEvents(frameEl.lastElementChild.previousElementSibling, 'change');
L.dom.bindClassInstance(frameEl, this); dom.bindClassInstance(frameEl, this);
return frameEl; return frameEl;
}, },
@ -763,7 +767,7 @@ var UISelect = UIElement.extend(/** @lends LuCI.ui.Select.prototype */ {
} }
} }
L.dom.bindClassInstance(frameEl, this); dom.bindClassInstance(frameEl, this);
return frameEl; return frameEl;
}, },
@ -929,7 +933,7 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
* expression. Only applicable when `create` is `true`. * expression. Only applicable when `create` is `true`.
*/ */
__init__: function(value, choices, options) { __init__: function(value, choices, options) {
if (!L.isObject(choices)) if (typeof(choices) != 'object')
choices = {}; choices = {};
if (!Array.isArray(value)) if (!Array.isArray(value))
@ -976,7 +980,7 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
for (var i = 0; i < keys.length; i++) { for (var i = 0; i < keys.length; i++) {
var label = this.choices[keys[i]]; var label = this.choices[keys[i]];
if (L.dom.elem(label)) if (dom.elem(label))
label = label.cloneNode(true); label = label.cloneNode(true);
sb.lastElementChild.appendChild(E('li', { sb.lastElementChild.appendChild(E('li', {
@ -995,7 +999,7 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
}); });
if (this.options.datatype || this.options.validate) if (this.options.datatype || this.options.validate)
L.ui.addValidator(createEl, this.options.datatype || 'string', UI.prototype.addValidator(createEl, this.options.datatype || 'string',
true, this.options.validate, 'blur', 'keyup'); true, this.options.validate, 'blur', 'keyup');
sb.lastElementChild.appendChild(E('li', { 'data-value': '-' }, createEl)); sb.lastElementChild.appendChild(E('li', { 'data-value': '-' }, createEl));
@ -1079,7 +1083,7 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
else else
sb.removeAttribute('empty'); sb.removeAttribute('empty');
L.dom.content(more, (ndisplay == this.options.display_items) dom.content(more, (ndisplay == this.options.display_items)
? (this.options.select_placeholder || this.options.placeholder) : '···'); ? (this.options.select_placeholder || this.options.placeholder) : '···');
@ -1118,7 +1122,7 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
this.setUpdateEvents(sb, 'cbi-dropdown-open', 'cbi-dropdown-close'); this.setUpdateEvents(sb, 'cbi-dropdown-open', 'cbi-dropdown-close');
this.setChangeEvents(sb, 'cbi-dropdown-change', 'cbi-dropdown-close'); this.setChangeEvents(sb, 'cbi-dropdown-change', 'cbi-dropdown-close');
L.dom.bindClassInstance(sb, this); dom.bindClassInstance(sb, this);
return sb; return sb;
}, },
@ -1343,7 +1347,7 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
else else
sb.removeAttribute('empty'); sb.removeAttribute('empty');
L.dom.content(more, (ndisplay === this.options.display_items) dom.content(more, (ndisplay === this.options.display_items)
? (this.options.select_placeholder || this.options.placeholder) : '···'); ? (this.options.select_placeholder || this.options.placeholder) : '···');
} }
else { else {
@ -2017,7 +2021,7 @@ var UIComboButton = UIDropdown.extend(/** @lends LuCI.ui.ComboButton.prototype *
var sb = ev.currentTarget, var sb = ev.currentTarget,
t = ev.target; t = ev.target;
if (sb.hasAttribute('open') || L.dom.matches(t, '.cbi-dropdown > span.open')) if (sb.hasAttribute('open') || dom.matches(t, '.cbi-dropdown > span.open'))
return UIDropdown.prototype.handleClick.apply(this, arguments); return UIDropdown.prototype.handleClick.apply(this, arguments);
if (this.options.click) if (this.options.click)
@ -2133,14 +2137,14 @@ var UIDynamicList = UIElement.extend(/** @lends LuCI.ui.DynamicList.prototype */
dl.lastElementChild.appendChild(E('div', { 'class': 'btn cbi-button cbi-button-add' }, '+')); dl.lastElementChild.appendChild(E('div', { 'class': 'btn cbi-button cbi-button-add' }, '+'));
if (this.options.datatype || this.options.validate) if (this.options.datatype || this.options.validate)
L.ui.addValidator(inputEl, this.options.datatype || 'string', UI.prototype.addValidator(inputEl, this.options.datatype || 'string',
true, this.options.validate, 'blur', 'keyup'); true, this.options.validate, 'blur', 'keyup');
} }
for (var i = 0; i < this.values.length; i++) { for (var i = 0; i < this.values.length; i++) {
var label = this.choices ? this.choices[this.values[i]] : null; var label = this.choices ? this.choices[this.values[i]] : null;
if (L.dom.elem(label)) if (dom.elem(label))
label = label.cloneNode(true); label = label.cloneNode(true);
this.addItem(dl, this.values[i], label); this.addItem(dl, this.values[i], label);
@ -2160,7 +2164,7 @@ var UIDynamicList = UIElement.extend(/** @lends LuCI.ui.DynamicList.prototype */
this.setUpdateEvents(dl, 'cbi-dynlist-change'); this.setUpdateEvents(dl, 'cbi-dynlist-change');
this.setChangeEvents(dl, 'cbi-dynlist-change'); this.setChangeEvents(dl, 'cbi-dynlist-change');
L.dom.bindClassInstance(dl, this); dom.bindClassInstance(dl, this);
return dl; return dl;
}, },
@ -2372,7 +2376,7 @@ var UIDynamicList = UIElement.extend(/** @lends LuCI.ui.DynamicList.prototype */
*/ */
addChoices: function(values, labels) { addChoices: function(values, labels) {
var dl = this.node.lastElementChild.firstElementChild; var dl = this.node.lastElementChild.firstElementChild;
L.dom.callClassMethod(dl, 'addChoices', values, labels); dom.callClassMethod(dl, 'addChoices', values, labels);
}, },
/** /**
@ -2385,7 +2389,7 @@ var UIDynamicList = UIElement.extend(/** @lends LuCI.ui.DynamicList.prototype */
*/ */
clearChoices: function() { clearChoices: function() {
var dl = this.node.lastElementChild.firstElementChild; var dl = this.node.lastElementChild.firstElementChild;
L.dom.callClassMethod(dl, 'clearChoices'); dom.callClassMethod(dl, 'clearChoices');
} }
}); });
@ -2439,7 +2443,7 @@ var UIHiddenfield = UIElement.extend(/** @lends LuCI.ui.Hiddenfield.prototype */
bind: function(hiddenEl) { bind: function(hiddenEl) {
this.node = hiddenEl; this.node = hiddenEl;
L.dom.bindClassInstance(hiddenEl, this); dom.bindClassInstance(hiddenEl, this);
return hiddenEl; return hiddenEl;
}, },
@ -2535,7 +2539,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
this.setUpdateEvents(browserEl, 'cbi-fileupload-select', 'cbi-fileupload-cancel'); this.setUpdateEvents(browserEl, 'cbi-fileupload-select', 'cbi-fileupload-cancel');
this.setChangeEvents(browserEl, 'cbi-fileupload-select', 'cbi-fileupload-cancel'); this.setChangeEvents(browserEl, 'cbi-fileupload-select', 'cbi-fileupload-cancel');
L.dom.bindClassInstance(browserEl, this); dom.bindClassInstance(browserEl, this);
return browserEl; return browserEl;
}, },
@ -2558,7 +2562,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
return this.bind(E('div', { 'id': this.options.id }, [ return this.bind(E('div', { 'id': this.options.id }, [
E('button', { E('button', {
'class': 'btn', 'class': 'btn',
'click': L.ui.createHandlerFn(this, 'handleFileBrowser') 'click': UI.prototype.createHandlerFn(this, 'handleFileBrowser')
}, label), }, label),
E('div', { E('div', {
'class': 'cbi-filebrowser' 'class': 'cbi-filebrowser'
@ -2657,7 +2661,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
data.append('filename', path + '/' + filename); data.append('filename', path + '/' + filename);
data.append('filedata', fileinput.files[0]); data.append('filedata', fileinput.files[0]);
return L.Request.post(L.env.cgi_base + '/cgi-upload', data, { return request.post(L.env.cgi_base + '/cgi-upload', data, {
progress: L.bind(function(btn, ev) { progress: L.bind(function(btn, ev) {
btn.firstChild.data = '%.2f%%'.format((ev.loaded / ev.total) * 100); btn.firstChild.data = '%.2f%%'.format((ev.loaded / ev.total) * 100);
}, this, ev.target) }, this, ev.target)
@ -2689,7 +2693,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
hidden = this.node.lastElementChild; hidden = this.node.lastElementChild;
if (path == hidden.value) { if (path == hidden.value) {
L.dom.content(button, _('Select file…')); dom.content(button, _('Select file…'));
hidden.value = ''; hidden.value = '';
} }
@ -2741,7 +2745,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
E('div', {}, E('input', { 'type': 'text', 'placeholder': _('Filename') })), E('div', {}, E('input', { 'type': 'text', 'placeholder': _('Filename') })),
E('button', { E('button', {
'class': 'btn cbi-button-save', 'class': 'btn cbi-button-save',
'click': L.ui.createHandlerFn(this, 'handleUpload', path, list), 'click': UI.prototype.createHandlerFn(this, 'handleUpload', path, list),
'disabled': true 'disabled': true
}, [ _('Upload file') ]) }, [ _('Upload file') ])
]) ])
@ -2778,7 +2782,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
E('a', { E('a', {
'href': '#', 'href': '#',
'style': selected ? 'font-weight:bold' : null, 'style': selected ? 'font-weight:bold' : null,
'click': L.ui.createHandlerFn(this, 'handleSelect', 'click': UI.prototype.createHandlerFn(this, 'handleSelect',
entrypath, list[i].type != 'directory' ? list[i] : null) entrypath, list[i].type != 'directory' ? list[i] : null)
}, '%h'.format(list[i].name)) }, '%h'.format(list[i].name))
]), ]),
@ -2794,11 +2798,11 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
E('div', [ E('div', [
selected ? E('button', { selected ? E('button', {
'class': 'btn', 'class': 'btn',
'click': L.ui.createHandlerFn(this, 'handleReset') 'click': UI.prototype.createHandlerFn(this, 'handleReset')
}, [ _('Deselect') ]) : '', }, [ _('Deselect') ]) : '',
this.options.enable_remove ? E('button', { this.options.enable_remove ? E('button', {
'class': 'btn cbi-button-negative', 'class': 'btn cbi-button-negative',
'click': L.ui.createHandlerFn(this, 'handleDelete', entrypath, list[i]) 'click': UI.prototype.createHandlerFn(this, 'handleDelete', entrypath, list[i])
}, [ _('Delete') ]) : '' }, [ _('Delete') ]) : ''
]) ])
])); ]));
@ -2812,16 +2816,16 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
for (var i = 0; i < dirs.length; i++) { for (var i = 0; i < dirs.length; i++) {
cur = cur ? cur + '/' + dirs[i] : dirs[i]; cur = cur ? cur + '/' + dirs[i] : dirs[i];
L.dom.append(breadcrumb, [ dom.append(breadcrumb, [
i ? ' » ' : '', i ? ' » ' : '',
E('a', { E('a', {
'href': '#', 'href': '#',
'click': L.ui.createHandlerFn(this, 'handleSelect', cur || '/', null) 'click': UI.prototype.createHandlerFn(this, 'handleSelect', cur || '/', null)
}, dirs[i] != '' ? '%h'.format(dirs[i]) : E('em', '(root)')), }, dirs[i] != '' ? '%h'.format(dirs[i]) : E('em', '(root)')),
]); ]);
} }
L.dom.content(container, [ dom.content(container, [
breadcrumb, breadcrumb,
rows, rows,
E('div', { 'class': 'right' }, [ E('div', { 'class': 'right' }, [
@ -2829,7 +2833,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
E('a', { E('a', {
'href': '#', 'href': '#',
'class': 'btn', 'class': 'btn',
'click': L.ui.createHandlerFn(this, 'handleCancel') 'click': UI.prototype.createHandlerFn(this, 'handleCancel')
}, _('Cancel')) }, _('Cancel'))
]), ]),
]); ]);
@ -2854,18 +2858,18 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
hidden = this.node.lastElementChild; hidden = this.node.lastElementChild;
hidden.value = ''; hidden.value = '';
L.dom.content(button, _('Select file…')); dom.content(button, _('Select file…'));
this.handleCancel(ev); this.handleCancel(ev);
}, },
/** @private */ /** @private */
handleSelect: function(path, fileStat, ev) { handleSelect: function(path, fileStat, ev) {
var browser = L.dom.parent(ev.target, '.cbi-filebrowser'), var browser = dom.parent(ev.target, '.cbi-filebrowser'),
ul = browser.querySelector('ul'); ul = browser.querySelector('ul');
if (fileStat == null) { if (fileStat == null) {
L.dom.content(ul, E('em', { 'class': 'spinning' }, _('Loading directory contents…'))); dom.content(ul, E('em', { 'class': 'spinning' }, _('Loading directory contents…')));
L.resolveDefault(fs.list(path), []).then(L.bind(this.renderListing, this, browser, path)); L.resolveDefault(fs.list(path), []).then(L.bind(this.renderListing, this, browser, path));
} }
else { else {
@ -2874,7 +2878,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
path = this.canonicalizePath(path); path = this.canonicalizePath(path);
L.dom.content(button, [ dom.content(button, [
this.iconForType(fileStat.type), this.iconForType(fileStat.type),
' %s (%1000mB)'.format(this.truncatePath(path), fileStat.size) ' %s (%1000mB)'.format(this.truncatePath(path), fileStat.size)
]); ]);
@ -2901,7 +2905,7 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
return L.resolveDefault(fs.list(path), []).then(L.bind(function(button, browser, path, list) { return L.resolveDefault(fs.list(path), []).then(L.bind(function(button, browser, path, list) {
document.querySelectorAll('.cbi-filebrowser.open').forEach(function(browserEl) { document.querySelectorAll('.cbi-filebrowser.open').forEach(function(browserEl) {
L.dom.findClassInstance(browserEl).handleCancel(ev); dom.findClassInstance(browserEl).handleCancel(ev);
}); });
button.style.display = 'none'; button.style.display = 'none';
@ -2932,14 +2936,14 @@ var UIFileUpload = UIElement.extend(/** @lends LuCI.ui.FileUpload.prototype */ {
* To import the class in views, use `'require ui'`, to import it in * To import the class in views, use `'require ui'`, to import it in
* external JavaScript, use `L.require("ui").then(...)`. * external JavaScript, use `L.require("ui").then(...)`.
*/ */
return L.Class.extend(/** @lends LuCI.ui.prototype */ { var UI = baseclass.extend(/** @lends LuCI.ui.prototype */ {
__init__: function() { __init__: function() {
modalDiv = document.body.appendChild( modalDiv = document.body.appendChild(
L.dom.create('div', { id: 'modal_overlay' }, dom.create('div', { id: 'modal_overlay' },
L.dom.create('div', { class: 'modal', role: 'dialog', 'aria-modal': true }))); dom.create('div', { class: 'modal', role: 'dialog', 'aria-modal': true })));
tooltipDiv = document.body.appendChild( tooltipDiv = document.body.appendChild(
L.dom.create('div', { class: 'cbi-tooltip' })); dom.create('div', { class: 'cbi-tooltip' }));
/* setup old aliases */ /* setup old aliases */
L.showModal = this.showModal; L.showModal = this.showModal;
@ -2977,7 +2981,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
* @param {*} contents * @param {*} contents
* The contents to add to the modal dialog. This should be a DOM node or * The contents to add to the modal dialog. This should be a DOM node or
* a document fragment in most cases. The value is passed as-is to the * a document fragment in most cases. The value is passed as-is to the
* `L.dom.content()` function - refer to its documentation for applicable * `dom.content()` function - refer to its documentation for applicable
* values. * values.
* *
* @param {...string} [classes] * @param {...string} [classes]
@ -2995,8 +2999,8 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
for (var i = 2; i < arguments.length; i++) for (var i = 2; i < arguments.length; i++)
dlg.classList.add(arguments[i]); dlg.classList.add(arguments[i]);
L.dom.content(dlg, L.dom.create('h4', {}, title)); dom.content(dlg, dom.create('h4', {}, title));
L.dom.append(dlg, children); dom.append(dlg, children);
document.body.classList.add('modal-overlay-active'); document.body.classList.add('modal-overlay-active');
@ -3092,7 +3096,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
* @param {*} contents * @param {*} contents
* The contents to add to the notification banner. This should be a DOM * The contents to add to the notification banner. This should be a DOM
* node or a document fragment in most cases. The value is passed as-is * node or a document fragment in most cases. The value is passed as-is
* to the `L.dom.content()` function - refer to its documentation for * to the `dom.content()` function - refer to its documentation for
* applicable values. * applicable values.
* *
* @param {...string} [classes] * @param {...string} [classes]
@ -3119,7 +3123,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
'class': 'btn', 'class': 'btn',
'style': 'margin-left:auto; margin-top:auto', 'style': 'margin-left:auto; margin-top:auto',
'click': function(ev) { 'click': function(ev) {
L.dom.parent(ev.target, '.alert-message').classList.add('fade-out'); dom.parent(ev.target, '.alert-message').classList.add('fade-out');
}, },
}, [ _('Dismiss') ]) }, [ _('Dismiss') ])
@ -3127,9 +3131,9 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
]); ]);
if (title != null) if (title != null)
L.dom.append(msg.firstElementChild, E('h4', {}, title)); dom.append(msg.firstElementChild, E('h4', {}, title));
L.dom.append(msg.firstElementChild, children); dom.append(msg.firstElementChild, children);
for (var i = 2; i < arguments.length; i++) for (var i = 2; i < arguments.length; i++)
msg.classList.add(arguments[i]); msg.classList.add(arguments[i]);
@ -3271,11 +3275,11 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
])); ]));
if ((i+2) < items.length) if ((i+2) < items.length)
children.push(L.dom.elem(sep) ? sep.cloneNode(true) : sep); children.push(dom.elem(sep) ? sep.cloneNode(true) : sep);
} }
} }
L.dom.content(node, children); dom.content(node, children);
return node; return node;
}, },
@ -3295,7 +3299,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
* external JavaScript, use `L.require("ui").then(...)` and access the * external JavaScript, use `L.require("ui").then(...)` and access the
* `tabs` property of the class instance value. * `tabs` property of the class instance value.
*/ */
tabs: L.Class.singleton(/* @lends LuCI.ui.tabs.prototype */ { tabs: baseclass.singleton(/* @lends LuCI.ui.tabs.prototype */ {
/** @private */ /** @private */
init: function() { init: function() {
var groups = [], prevGroup = null, currGroup = null; var groups = [], prevGroup = null, currGroup = null;
@ -3303,7 +3307,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
document.querySelectorAll('[data-tab]').forEach(function(tab) { document.querySelectorAll('[data-tab]').forEach(function(tab) {
var parent = tab.parentNode; var parent = tab.parentNode;
if (L.dom.matches(tab, 'li') && L.dom.matches(parent, 'ul.cbi-tabmenu')) if (dom.matches(tab, 'li') && dom.matches(parent, 'ul.cbi-tabmenu'))
return; return;
if (!parent.hasAttribute('data-tab-group')) if (!parent.hasAttribute('data-tab-group'))
@ -3421,7 +3425,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
* Returns `true` if the pane is empty, else `false`. * Returns `true` if the pane is empty, else `false`.
*/ */
isEmptyPane: function(pane) { isEmptyPane: function(pane) {
return L.dom.isEmpty(pane, function(n) { return n.classList.contains('cbi-tab-descr') }); return dom.isEmpty(pane, function(n) { return n.classList.contains('cbi-tab-descr') });
}, },
/** @private */ /** @private */
@ -3530,11 +3534,11 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
}); });
group.childNodes.forEach(function(pane) { group.childNodes.forEach(function(pane) {
if (L.dom.matches(pane, '[data-tab]')) { if (dom.matches(pane, '[data-tab]')) {
if (pane.getAttribute('data-tab') === name) { if (pane.getAttribute('data-tab') === name) {
pane.setAttribute('data-tab-active', 'true'); pane.setAttribute('data-tab-active', 'true');
pane.dispatchEvent(new CustomEvent('cbi-tab-active', { detail: { tab: name } })); pane.dispatchEvent(new CustomEvent('cbi-tab-active', { detail: { tab: name } }));
L.ui.tabs.setActiveTabId(pane, index); UI.prototype.tabs.setActiveTabId(pane, index);
} }
else { else {
pane.setAttribute('data-tab-active', 'false'); pane.setAttribute('data-tab-active', 'false');
@ -3576,7 +3580,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
*/ */
uploadFile: function(path, progressStatusNode) { uploadFile: function(path, progressStatusNode) {
return new Promise(function(resolveFn, rejectFn) { return new Promise(function(resolveFn, rejectFn) {
L.ui.showModal(_('Uploading file…'), [ UI.prototype.showModal(_('Uploading file…'), [
E('p', _('Please select the file to upload.')), E('p', _('Please select the file to upload.')),
E('div', { 'style': 'display:flex' }, [ E('div', { 'style': 'display:flex' }, [
E('div', { 'class': 'left', 'style': 'flex:1' }, [ E('div', { 'class': 'left', 'style': 'flex:1' }, [
@ -3584,7 +3588,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
type: 'file', type: 'file',
style: 'display:none', style: 'display:none',
change: function(ev) { change: function(ev) {
var modal = L.dom.parent(ev.target, '.modal'), var modal = dom.parent(ev.target, '.modal'),
body = modal.querySelector('p'), body = modal.querySelector('p'),
upload = modal.querySelector('.cbi-button-action.important'), upload = modal.querySelector('.cbi-button-action.important'),
file = ev.currentTarget.files[0]; file = ev.currentTarget.files[0];
@ -3592,7 +3596,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
if (file == null) if (file == null)
return; return;
L.dom.content(body, [ dom.content(body, [
E('ul', {}, [ E('ul', {}, [
E('li', {}, [ '%s: %s'.format(_('Name'), file.name.replace(/^.*[\\\/]/, '')) ]), E('li', {}, [ '%s: %s'.format(_('Name'), file.name.replace(/^.*[\\\/]/, '')) ]),
E('li', {}, [ '%s: %1024mB'.format(_('Size'), file.size) ]) E('li', {}, [ '%s: %1024mB'.format(_('Size'), file.size) ])
@ -3614,7 +3618,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
E('button', { E('button', {
'class': 'btn', 'class': 'btn',
'click': function() { 'click': function() {
L.ui.hideModal(); UI.prototype.hideModal();
rejectFn(new Error('Upload has been cancelled')); rejectFn(new Error('Upload has been cancelled'));
} }
}, [ _('Cancel') ]), }, [ _('Cancel') ]),
@ -3623,14 +3627,14 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
'class': 'btn cbi-button-action important', 'class': 'btn cbi-button-action important',
'disabled': true, 'disabled': true,
'click': function(ev) { 'click': function(ev) {
var input = L.dom.parent(ev.target, '.modal').querySelector('input[type="file"]'); var input = dom.parent(ev.target, '.modal').querySelector('input[type="file"]');
if (!input.files[0]) if (!input.files[0])
return; return;
var progress = E('div', { 'class': 'cbi-progressbar', 'title': '0%' }, E('div', { 'style': 'width:0' })); var progress = E('div', { 'class': 'cbi-progressbar', 'title': '0%' }, E('div', { 'style': 'width:0' }));
L.ui.showModal(_('Uploading file…'), [ progress ]); UI.prototype.showModal(_('Uploading file…'), [ progress ]);
var data = new FormData(); var data = new FormData();
@ -3640,7 +3644,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
var filename = input.files[0].name; var filename = input.files[0].name;
L.Request.post(L.env.cgi_base + '/cgi-upload', data, { request.post(L.env.cgi_base + '/cgi-upload', data, {
timeout: 0, timeout: 0,
progress: function(pev) { progress: function(pev) {
var percent = (pev.loaded / pev.total) * 100; var percent = (pev.loaded / pev.total) * 100;
@ -3654,10 +3658,10 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
}).then(function(res) { }).then(function(res) {
var reply = res.json(); var reply = res.json();
L.ui.hideModal(); UI.prototype.hideModal();
if (L.isObject(reply) && reply.failure) { if (L.isObject(reply) && reply.failure) {
L.ui.addNotification(null, E('p', _('Upload request failed: %s').format(reply.message))); UI.prototype.addNotification(null, E('p', _('Upload request failed: %s').format(reply.message)));
rejectFn(new Error(reply.failure)); rejectFn(new Error(reply.failure));
} }
else { else {
@ -3665,7 +3669,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
resolveFn(reply); resolveFn(reply);
} }
}, function(err) { }, function(err) {
L.ui.hideModal(); UI.prototype.hideModal();
rejectFn(err); rejectFn(err);
}); });
} }
@ -3726,7 +3730,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
var ipaddrs = arguments.length ? arguments : [ window.location.host ]; var ipaddrs = arguments.length ? arguments : [ window.location.host ];
window.setTimeout(L.bind(function() { window.setTimeout(L.bind(function() {
L.Poll.add(L.bind(function() { poll.add(L.bind(function() {
var tasks = [], reachable = false; var tasks = [], reachable = false;
for (var i = 0; i < 2; i++) for (var i = 0; i < 2; i++)
@ -3736,7 +3740,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
return Promise.all(tasks).then(function() { return Promise.all(tasks).then(function() {
if (reachable) { if (reachable) {
L.Poll.stop(); poll.stop();
window.location = reachable; window.location = reachable;
} }
}); });
@ -3758,7 +3762,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
* external JavaScript, use `L.require("ui").then(...)` and access the * external JavaScript, use `L.require("ui").then(...)` and access the
* `changes` property of the class instance value. * `changes` property of the class instance value.
*/ */
changes: L.Class.singleton(/* @lends LuCI.ui.changes.prototype */ { changes: baseclass.singleton(/* @lends LuCI.ui.changes.prototype */ {
init: function() { init: function() {
if (!L.env.sessionid) if (!L.env.sessionid)
return; return;
@ -3791,7 +3795,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
} }
if (n > 0) { if (n > 0) {
L.dom.content(i, [ _('Unsaved Changes'), ': ', n ]); dom.content(i, [ _('Unsaved Changes'), ': ', n ]);
i.classList.add('flash'); i.classList.add('flash');
i.style.display = ''; i.style.display = '';
document.dispatchEvent(new CustomEvent('uci-new-changes')); document.dispatchEvent(new CustomEvent('uci-new-changes'));
@ -3850,7 +3854,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
*/ */
displayChanges: function() { displayChanges: function() {
var list = E('div', { 'class': 'uci-change-list' }), var list = E('div', { 'class': 'uci-change-list' }),
dlg = L.ui.showModal(_('Configuration') + ' / ' + _('Changes'), [ dlg = UI.prototype.showModal(_('Configuration') + ' / ' + _('Changes'), [
E('div', { 'class': 'cbi-section' }, [ E('div', { 'class': 'cbi-section' }, [
E('strong', _('Legend:')), E('strong', _('Legend:')),
E('div', { 'class': 'uci-change-legend' }, [ E('div', { 'class': 'uci-change-legend' }, [
@ -3866,7 +3870,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
E('div', { 'class': 'right' }, [ E('div', { 'class': 'right' }, [
E('button', { E('button', {
'class': 'btn', 'class': 'btn',
'click': L.ui.hideModal 'click': UI.prototype.hideModal
}, [ _('Dismiss') ]), ' ', }, [ _('Dismiss') ]), ' ',
E('button', { E('button', {
'class': 'cbi-button cbi-button-positive important', 'class': 'cbi-button cbi-button-positive important',
@ -3919,24 +3923,24 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
/** @private */ /** @private */
displayStatus: function(type, content) { displayStatus: function(type, content) {
if (type) { if (type) {
var message = L.ui.showModal('', ''); var message = UI.prototype.showModal('', '');
message.classList.add('alert-message'); message.classList.add('alert-message');
DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/)); DOMTokenList.prototype.add.apply(message.classList, type.split(/\s+/));
if (content) if (content)
L.dom.content(message, content); dom.content(message, content);
if (!this.was_polling) { if (!this.was_polling) {
this.was_polling = L.Request.poll.active(); this.was_polling = request.poll.active();
L.Request.poll.stop(); request.poll.stop();
} }
} }
else { else {
L.ui.hideModal(); UI.prototype.hideModal();
if (this.was_polling) if (this.was_polling)
L.Request.poll.start(); request.poll.start();
} }
}, },
@ -3949,21 +3953,21 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
var call = function(r, data, duration) { var call = function(r, data, duration) {
if (r.status === 204) { if (r.status === 204) {
L.ui.changes.displayStatus('warning', [ UI.prototype.changes.displayStatus('warning', [
E('h4', _('Configuration changes have been rolled back!')), E('h4', _('Configuration changes have been rolled back!')),
E('p', _('The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, perform an unchecked configuration apply. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.').format(L.env.apply_rollback)), E('p', _('The device could not be reached within %d seconds after applying the pending changes, which caused the configuration to be rolled back for safety reasons. If you believe that the configuration changes are correct nonetheless, perform an unchecked configuration apply. Alternatively, you can dismiss this warning and edit changes before attempting to apply again, or revert all pending changes to keep the currently working configuration state.').format(L.env.apply_rollback)),
E('div', { 'class': 'right' }, [ E('div', { 'class': 'right' }, [
E('button', { E('button', {
'class': 'btn', 'class': 'btn',
'click': L.bind(L.ui.changes.displayStatus, L.ui.changes, false) 'click': L.bind(UI.prototype.changes.displayStatus, UI.prototype.changes, false)
}, [ _('Dismiss') ]), ' ', }, [ _('Dismiss') ]), ' ',
E('button', { E('button', {
'class': 'btn cbi-button-action important', 'class': 'btn cbi-button-action important',
'click': L.bind(L.ui.changes.revert, L.ui.changes) 'click': L.bind(UI.prototype.changes.revert, UI.prototype.changes)
}, [ _('Revert changes') ]), ' ', }, [ _('Revert changes') ]), ' ',
E('button', { E('button', {
'class': 'btn cbi-button-negative important', 'class': 'btn cbi-button-negative important',
'click': L.bind(L.ui.changes.apply, L.ui.changes, false) 'click': L.bind(UI.prototype.changes.apply, UI.prototype.changes, false)
}, [ _('Apply unchecked') ]) }, [ _('Apply unchecked') ])
]) ])
]); ]);
@ -3973,7 +3977,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0); var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0);
window.setTimeout(function() { window.setTimeout(function() {
L.Request.request(L.url('admin/uci/confirm'), { request.request(L.url('admin/uci/confirm'), {
method: 'post', method: 'post',
timeout: L.env.apply_timeout * 1000, timeout: L.env.apply_timeout * 1000,
query: { sid: L.env.sessionid, token: L.env.token } query: { sid: L.env.sessionid, token: L.env.token }
@ -4004,19 +4008,19 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
var call = function(r, data, duration) { var call = function(r, data, duration) {
if (Date.now() >= deadline) { if (Date.now() >= deadline) {
window.clearTimeout(tt); window.clearTimeout(tt);
L.ui.changes.rollback(checked); UI.prototype.changes.rollback(checked);
return; return;
} }
else if (r && (r.status === 200 || r.status === 204)) { else if (r && (r.status === 200 || r.status === 204)) {
document.dispatchEvent(new CustomEvent('uci-applied')); document.dispatchEvent(new CustomEvent('uci-applied'));
L.ui.changes.setIndicator(0); UI.prototype.changes.setIndicator(0);
L.ui.changes.displayStatus('notice', UI.prototype.changes.displayStatus('notice',
E('p', _('Configuration changes applied.'))); E('p', _('Configuration changes applied.')));
window.clearTimeout(tt); window.clearTimeout(tt);
window.setTimeout(function() { window.setTimeout(function() {
//L.ui.changes.displayStatus(false); //UI.prototype.changes.displayStatus(false);
window.location = window.location.href.split('#')[0]; window.location = window.location.href.split('#')[0];
}, L.env.apply_display * 1000); }, L.env.apply_display * 1000);
@ -4025,10 +4029,10 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0); var delay = isNaN(duration) ? 0 : Math.max(1000 - duration, 0);
window.setTimeout(function() { window.setTimeout(function() {
L.Request.request(L.url('admin/uci/confirm'), { request.request(L.url('admin/uci/confirm'), {
method: 'post', method: 'post',
timeout: L.env.apply_timeout * 1000, timeout: L.env.apply_timeout * 1000,
query: L.ui.changes.confirm_auth query: UI.prototype.changes.confirm_auth
}).then(call, call); }).then(call, call);
}, delay); }, delay);
}; };
@ -4036,7 +4040,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
var tick = function() { var tick = function() {
var now = Date.now(); var now = Date.now();
L.ui.changes.displayStatus('notice spinning', UI.prototype.changes.displayStatus('notice spinning',
E('p', _('Applying configuration changes… %ds') E('p', _('Applying configuration changes… %ds')
.format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0)))); .format(Math.max(Math.floor((deadline - Date.now()) / 1000), 0))));
@ -4077,32 +4081,32 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
this.displayStatus('notice spinning', this.displayStatus('notice spinning',
E('p', _('Starting configuration apply…'))); E('p', _('Starting configuration apply…')));
L.Request.request(L.url('admin/uci', checked ? 'apply_rollback' : 'apply_unchecked'), { request.request(L.url('admin/uci', checked ? 'apply_rollback' : 'apply_unchecked'), {
method: 'post', method: 'post',
query: { sid: L.env.sessionid, token: L.env.token } query: { sid: L.env.sessionid, token: L.env.token }
}).then(function(r) { }).then(function(r) {
if (r.status === (checked ? 200 : 204)) { if (r.status === (checked ? 200 : 204)) {
var tok = null; try { tok = r.json(); } catch(e) {} var tok = null; try { tok = r.json(); } catch(e) {}
if (checked && tok !== null && typeof(tok) === 'object' && typeof(tok.token) === 'string') if (checked && tok !== null && typeof(tok) === 'object' && typeof(tok.token) === 'string')
L.ui.changes.confirm_auth = tok; UI.prototype.changes.confirm_auth = tok;
L.ui.changes.confirm(checked, Date.now() + L.env.apply_rollback * 1000); UI.prototype.changes.confirm(checked, Date.now() + L.env.apply_rollback * 1000);
} }
else if (checked && r.status === 204) { else if (checked && r.status === 204) {
L.ui.changes.displayStatus('notice', UI.prototype.changes.displayStatus('notice',
E('p', _('There are no changes to apply'))); E('p', _('There are no changes to apply')));
window.setTimeout(function() { window.setTimeout(function() {
L.ui.changes.displayStatus(false); UI.prototype.changes.displayStatus(false);
}, L.env.apply_display * 1000); }, L.env.apply_display * 1000);
} }
else { else {
L.ui.changes.displayStatus('warning', UI.prototype.changes.displayStatus('warning',
E('p', _('Apply request failed with status <code>%h</code>') E('p', _('Apply request failed with status <code>%h</code>')
.format(r.responseText || r.statusText || r.status))); .format(r.responseText || r.statusText || r.status)));
window.setTimeout(function() { window.setTimeout(function() {
L.ui.changes.displayStatus(false); UI.prototype.changes.displayStatus(false);
}, L.env.apply_display * 1000); }, L.env.apply_display * 1000);
} }
}); });
@ -4124,29 +4128,29 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
this.displayStatus('notice spinning', this.displayStatus('notice spinning',
E('p', _('Reverting configuration…'))); E('p', _('Reverting configuration…')));
L.Request.request(L.url('admin/uci/revert'), { request.request(L.url('admin/uci/revert'), {
method: 'post', method: 'post',
query: { sid: L.env.sessionid, token: L.env.token } query: { sid: L.env.sessionid, token: L.env.token }
}).then(function(r) { }).then(function(r) {
if (r.status === 200) { if (r.status === 200) {
document.dispatchEvent(new CustomEvent('uci-reverted')); document.dispatchEvent(new CustomEvent('uci-reverted'));
L.ui.changes.setIndicator(0); UI.prototype.changes.setIndicator(0);
L.ui.changes.displayStatus('notice', UI.prototype.changes.displayStatus('notice',
E('p', _('Changes have been reverted.'))); E('p', _('Changes have been reverted.')));
window.setTimeout(function() { window.setTimeout(function() {
//L.ui.changes.displayStatus(false); //UI.prototype.changes.displayStatus(false);
window.location = window.location.href.split('#')[0]; window.location = window.location.href.split('#')[0];
}, L.env.apply_display * 1000); }, L.env.apply_display * 1000);
} }
else { else {
L.ui.changes.displayStatus('warning', UI.prototype.changes.displayStatus('warning',
E('p', _('Revert request failed with status <code>%h</code>') E('p', _('Revert request failed with status <code>%h</code>')
.format(r.statusText || r.status))); .format(r.statusText || r.status)));
window.setTimeout(function() { window.setTimeout(function() {
L.ui.changes.displayStatus(false); UI.prototype.changes.displayStatus(false);
}, L.env.apply_display * 1000); }, L.env.apply_display * 1000);
} }
}); });
@ -4197,7 +4201,7 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
events.push('blur', 'keyup'); events.push('blur', 'keyup');
try { try {
var cbiValidator = L.validation.create(field, type, optional, vfunc), var cbiValidator = validation.create(field, type, optional, vfunc),
validatorFn = cbiValidator.validate.bind(cbiValidator); validatorFn = cbiValidator.validate.bind(cbiValidator);
for (var i = 0; i < events.length; i++) for (var i = 0; i < events.length; i++)
@ -4278,3 +4282,5 @@ return L.Class.extend(/** @lends LuCI.ui.prototype */ {
Hiddenfield: UIHiddenfield, Hiddenfield: UIHiddenfield,
FileUpload: UIFileUpload FileUpload: UIFileUpload
}); });
return UI;

View file

@ -1,6 +1,7 @@
'use strict'; 'use strict';
'require baseclass';
var Validator = L.Class.extend({ var Validator = baseclass.extend({
__name__: 'Validation', __name__: 'Validation',
__init__: function(field, type, optional, vfunc, validatorFactory) { __init__: function(field, type, optional, vfunc, validatorFactory) {
@ -81,7 +82,7 @@ var Validator = L.Class.extend({
}); });
var ValidatorFactory = L.Class.extend({ var ValidatorFactory = baseclass.extend({
__name__: 'ValidatorFactory', __name__: 'ValidatorFactory',
create: function(field, type, optional, vfunc) { create: function(field, type, optional, vfunc) {