nodes = document.querySelectorAll('[data-strings]');
- for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
+ for (let i = 0, node; (node = nodes[i]) !== undefined; i++) {
var str = JSON.parse(node.getAttribute('data-strings'));
for (var key in str) {
for (var key2 in str[key]) {
nodes = document.querySelectorAll('[data-depends]');
- for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
+ for (let i = 0, node; (node = nodes[i]) !== undefined; i++) {
var index = parseInt(node.getAttribute('data-index'), 10);
var depends = JSON.parse(node.getAttribute('data-depends'));
if (!isNaN(index) && depends.length > 0) {
- for (var alt = 0; alt < depends.length; alt++)
+ for (let alt = 0; alt < depends.length; alt++)
cbi_d_add(node, depends[alt], index);
}
}
nodes = document.querySelectorAll('[data-update]');
- for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
+ for (let i = 0, node; (node = nodes[i]) !== undefined; i++) {
var events = node.getAttribute('data-update').split(' ');
- for (var j = 0, event; (event = events[j]) !== undefined; j++)
+ for (let j = 0, event; (event = events[j]) !== undefined; j++)
node.addEventListener(event, cbi_d_update);
}
nodes = document.querySelectorAll('[data-choices]');
- for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
- var choices = JSON.parse(node.getAttribute('data-choices')),
+ for (let i = 0, node; (node = nodes[i]) !== undefined; i++) {
+ let choices = JSON.parse(node.getAttribute('data-choices')),
options = {};
- for (var j = 0; j < choices[0].length; j++)
+ for (let j = 0; j < choices[0].length; j++)
options[choices[0][j]] = choices[1][j];
var def = (node.getAttribute('data-optional') === 'true')
nodes = document.querySelectorAll('[data-dynlist]');
- for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
- var choices = JSON.parse(node.getAttribute('data-dynlist')),
+ for (let i = 0, node; (node = nodes[i]) !== undefined; i++) {
+ let choices = JSON.parse(node.getAttribute('data-dynlist')),
values = JSON.parse(node.getAttribute('data-values') || '[]'),
options = null;
if (choices[0] && choices[0].length) {
options = {};
- for (var j = 0; j < choices[0].length; j++)
+ for (let j = 0; j < choices[0].length; j++)
options[choices[0][j]] = choices[1][j];
}
- var dl = new L.ui.DynamicList(values, options, {
+ let dl = new L.ui.DynamicList(values, options, {
name: node.getAttribute('data-prefix'),
sort: choices[0],
datatype: choices[2],
placeholder: node.getAttribute('data-placeholder')
});
- var n = dl.render();
+ let n = dl.render();
n.addEventListener('cbi-dynlist-change', cbi_d_update);
node.parentNode.replaceChild(n, node);
}
nodes = document.querySelectorAll('[data-type]');
- for (var i = 0, node; (node = nodes[i]) !== undefined; i++) {
+ for (let i = 0, node; (node = nodes[i]) !== undefined; i++) {
cbi_validate_field(node, node.getAttribute('data-optional') === 'true',
node.getAttribute('data-type'));
}
});
document.querySelectorAll('.cbi-section-remove > input[name^="cbi.rts"]').forEach(function(i) {
- var handler = function(ev) {
+ let handler = function(ev) {
var bits = this.name.split(/\./),
section = document.getElementById('cbi-' + bits[2] + '-' + bits[3]);
var tasks = [];
document.querySelectorAll('[data-ui-widget]').forEach(function(node) {
- var args = JSON.parse(node.getAttribute('data-ui-widget') || '[]'),
+ let args = JSON.parse(node.getAttribute('data-ui-widget') || '[]'),
widget = new (Function.prototype.bind.apply(L.ui[args[0]], args)),
markup = widget.render();
node.classList.remove('cbi-rowstyle-2');
node.classList.add((n++ % 2) ? 'cbi-rowstyle-2' : 'cbi-rowstyle-1');
- if (/-([^\-]+)$/.test(node.id))
+ if (/-([^-]+)$/.test(node.id))
ids.push(RegExp.$1);
}
}
var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/;
var a = b = [], numSubstitutions = 0, numMatches = 0;
- while (a = re.exec(str)) {
+ while ((a = re.exec(str)) !== null) {
var m = a[1];
var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5];
var pPrecision = a[6], pType = a[7];
if (pMinLength) {
subst = subst.toString();
- for (var i = subst.length; i < pMinLength; i++)
+ for (let i = subst.length; i < pMinLength; i++)
if (pJustify == '-')
subst = subst + ' ';
else
*/
function isElem(e) { return L.dom.elem(e) }
-/**
- * Parse an HTML string into a DOM element.
- * @param {string} s - HTML string.
- * @returns {HTMLElement} Parsed DOM element.
- */
-function toElem(s) { return L.dom.parse(s) }
-
/**
* Test whether node matches a CSS selector.
* @param {Node} node - Node to test.
t.update(data, placeholder);
}
-/**
- * Show a modal dialog with the given title and children content.
- * @deprecated
- * @param {string} title - Title of the modal.
- * @param {HTMLElement|Array<HTMLElement>} children - Content of the modal.
- * @returns {Promise} Promise that resolves when modal shown or when closed depending on L.showModal.
- */
-function showModal(title, children)
-{
- return L.showModal(title, children);
-}
-
-/**
- * Hide any currently shown modal dialog.
- * @deprecated
- * @returns {*} Return value forwarded from L.hideModal.
- */
-function hideModal()
-{
- return L.hideModal();
-}
-
document.addEventListener('DOMContentLoaded', function() {
document.addEventListener('validation-failure', function(ev) {
getZones: function() {
return initFirewallState().then(function() {
- var sections = uci.sections('firewall', 'zone'),
- zones = [];
+ const sections = uci.sections('firewall', 'zone');
+ const zones = [];
- for (var i = 0; i < sections.length; i++)
- zones.push(new Zone(sections[i]['.name']));
+ for (let s of sections)
+ zones.push(new Zone(s['.name']));
zones.sort(function(a, b) { return a.getName() > b.getName() });
getZoneByNetwork: function(network) {
return initFirewallState().then(function() {
- var sections = uci.sections('firewall', 'zone');
+ const sections = uci.sections('firewall', 'zone');
- for (var i = 0; i < sections.length; i++)
- if (L.toArray(sections[i].network).indexOf(network) != -1)
- return new Zone(sections[i]['.name']);
+ for (let s of sections)
+ if (L.toArray(s.network).indexOf(network) != -1)
+ return new Zone(s['.name']);
return null;
});
deleteZone: function(name) {
return initFirewallState().then(function() {
- var section = uci.get('firewall', name),
- found = false;
+ const section = uci.get('firewall', name);
+ let found = false;
if (section != null && section['.type'] == 'zone') {
found = true;
uci.remove('firewall', section['.name']);
}
else if (name != null) {
- var sections = uci.sections('firewall', 'zone');
+ const sections = uci.sections('firewall', 'zone');
- for (var i = 0; i < sections.length; i++) {
- if (sections[i].name != name)
+ for (let s of sections) {
+ if (s.name != name)
continue;
found = true;
- uci.remove('firewall', sections[i]['.name']);
+ uci.remove('firewall', s['.name']);
}
}
if (found == true) {
- sections = uci.sections('firewall');
+ const sections = uci.sections('firewall');
- for (var i = 0; i < sections.length; i++) {
- if (sections[i]['.type'] != 'rule' &&
- sections[i]['.type'] != 'redirect' &&
- sections[i]['.type'] != 'forwarding')
+ for (let s of sections) {
+ if (s['.type'] != 'rule' &&
+ s['.type'] != 'redirect' &&
+ s['.type'] != 'forwarding')
continue;
- if (sections[i].src == name || sections[i].dest == name)
- uci.remove('firewall', sections[i]['.name']);
+ if (s.src == name || s.dest == name)
+ uci.remove('firewall', s['.name']);
}
}
if (lookupZone(newName) != null)
return false;
- var sections = uci.sections('firewall', 'zone'),
- found = false;
+ const sections = uci.sections('firewall', 'zone');
+ let found = false;
- for (var i = 0; i < sections.length; i++) {
- if (sections[i].name != oldName)
+ for (let s of sections) {
+ if (s.name != oldName)
continue;
- uci.set('firewall', sections[i]['.name'], 'name', newName);
+ uci.set('firewall', s['.name'], 'name', newName);
found = true;
}
if (found == true) {
- sections = uci.sections('firewall');
+ const sections = uci.sections('firewall');
- for (var i = 0; i < sections.length; i++) {
- if (sections[i]['.type'] != 'rule' &&
- sections[i]['.type'] != 'redirect' &&
- sections[i]['.type'] != 'forwarding')
+ for (let s of sections) {
+ if (s['.type'] != 'rule' &&
+ s['.type'] != 'redirect' &&
+ s['.type'] != 'forwarding')
continue;
- if (sections[i].src == oldName)
- uci.set('firewall', sections[i]['.name'], 'src', newName);
+ if (s.src == oldName)
+ uci.set('firewall', s['.name'], 'src', newName);
- if (sections[i].dest == oldName)
- uci.set('firewall', sections[i]['.name'], 'dest', newName);
+ if (s.dest == oldName)
+ uci.set('firewall', s['.name'], 'dest', newName);
}
}
* entities decoded.
*/
stripTags(s) {
- if (typeof(s) == 'string' && !s.match(/[<>\&]/))
+ if (typeof(s) == 'string' && !s.match(/[<>&]/))
return s;
const x = dom.elem(s) ? s : dom.parse(`<div>${s}</div>`);
index = i;
});
- ev.currentTarget.parentNode.querySelectorAll('tr.cbi-section-table-row').forEach(L.bind((tr, i) => {
+ ev.currentTarget.parentNode.querySelectorAll('tr.cbi-section-table-row').forEach(L.bind((tr) => {
const sid = tr.getAttribute('data-sid');
const opt = tr.childNodes[index].getAttribute('data-name');
let val = this.cfgvalue(sid, opt);
*/
function handleRpcReply(expect, rc) {
if (typeof(rc) == 'number' && rc != 0) {
- var e = new Error(rpc.getStatusText(rc)); e.name = rpcErrors[rc] || 'Error';
+ let e = new Error(rpc.getStatusText(rc)); e.name = rpcErrors[rc] || 'Error';
throw e;
}
if (expect) {
- var type = Object.prototype.toString;
+ const type = Object.prototype.toString;
- for (var key in expect) {
+ for (let key in expect) {
if (rc != null && key != '')
rc = rc[key];
if (rc == null || type.call(rc) != type.call(expect[key])) {
- var e = new Error(_('Unexpected reply data format')); e.name = 'TypeError';
+ let e = new Error(_('Unexpected reply data format')); e.name = 'TypeError';
throw e;
}
* Class declaration and inheritance helper
*/
- const toCamelCase = s => s.replace(/(?:^|[\. -])(.)/g, (m0, m1) => m1.toUpperCase());
+ const toCamelCase = s => s.replace(/(?:^|[. -])(.)/g, (m0, m1) => m1.toUpperCase());
/**
* @class
if (setenv.base_url == null)
this.error('InternalError', 'Cannot find url of luci.js');
- setenv.cgi_base = setenv.scriptname.replace(/\/[^\/]+$/, '');
+ setenv.cgi_base = setenv.scriptname.replace(/\/[^/]+$/, '');
Object.assign(env, setenv);
return;
}
- const radioEls = frameEl.querySelectorAll('input[type="radio"]');
+ const radioEls = this.node.querySelectorAll('input[type="radio"]');
for (let i = 0; i < radioEls.length; i++)
radioEls[i].checked = (radioEls[i].value == value);
}
const ul = sb.querySelector('ul');
const more = sb.appendChild(E('span', { class: 'more', tabindex: -1 }, '···'));
- const open = sb.appendChild(E('span', { class: 'open', tabindex: -1 }, '▾'));
+ sb.appendChild(E('span', { class: 'open', tabindex: -1 }, '▾'));
const canary = sb.appendChild(E('div'));
const create = sb.querySelector(this.options.create_query);
let ndisplay = this.options.display_items;
* @param {Node} sb
*/
openDropdown(sb) {
- const st = window.getComputedStyle(sb, null);
const ul = sb.querySelector('ul');
const li = ul.querySelectorAll('li');
const fl = findParent(sb, '.cbi-value-field');
});
dl.addEventListener('touchstart', (e) => {
- const touch = e.touches[0];
const target = e.target.closest('.item');
if (target) {
draggedItem = target;
canonicalizePath(path) {
return path.replace(/\/{2,}/g, '/') // Collapse multiple slashes
.replace(/\/\.(\/|$)/g, '/') // Remove `/.`
- .replace(/[^\/]+\/\.\.(\/|$)/g, '/') // Resolve `/..`
+ .replace(/[^/]+\/\.\.(\/|$)/g, '/') // Resolve `/..`
.replace(/\/$/g, (m, o, s) => s.length > 1 ? '' : '/'); // Remove trailing `/` only if not root
},
* @returns {Promise}
*/
handleDelete(path, fileStat, ev) {
- const parent = path.replace(/\/[^\/]+$/, '') ?? '/';
+ const parent = path.replace(/\/[^/]+$/, '') ?? '/';
const name = path.replace(/^.+\//, '');
let msg;
const nameinput = ev.target.parentNode.querySelector('input[type="text"]');
const uploadbtn = ev.target.parentNode.querySelector('button.cbi-button-save');
- nameinput.value = ev.target.value.replace(/^.+[\/\\]/, '');
+ nameinput.value = ev.target.value.replace(/^.+[/\\]/, '');
uploadbtn.disabled = false;
}
}),
handleFileBrowser(ev) {
const button = ev.target;
const browser = button.nextElementSibling;
- let path = this.stat ? this.stat.path.replace(/\/[^\/]+$/, '') : (this.options.initial_directory ?? this.options.root_directory);
+ let path = this.stat ? this.stat.path.replace(/\/[^/]+$/, '') : (this.options.initial_directory ?? this.options.root_directory);
if (path.indexOf(this.options.root_directory) != 0)
path = this.options.root_directory;
const trow = table.appendChild(E('tr', { 'class': 'tr placeholder' }));
const td = trow.appendChild(E('td', { 'class': 'td' }, placeholder));
- if (typeof(captionClasses) == 'object')
- DOMTokenList.prototype.add.apply(td.classList, L.toArray(captionClasses[0]));
+ if (typeof(options.captionClasses) == 'object')
+ DOMTokenList.prototype.add.apply(td.classList, L.toArray(options.captionClasses[0]));
}
DOMTokenList.prototype.add.apply(table.classList, L.toArray(options.classes));
for (let i = 0; i < items.length; i += 2) {
if (items[i+1] !== null && items[i+1] !== undefined) {
const sep = separators[(i/2) % separators.length];
- const cld = [];
children.push(E('span', { class: 'nowrap' }, [
items[i] ? E('strong', `${items[i]}: `) : '',
const menu = E('ul', { 'class': 'cbi-tabmenu' });
const group = panes[0].parentNode;
- const groupId = +group.getAttribute('data-tab-group');
let selected = null;
if (group.getAttribute('data-initialized') === 'true')
return;
- for (let i = 0, pane; pane = panes[i]; i++) {
+ let pane;
+ for (let i = 0; i < panes.length; i++) {
+ pane = panes[i];
const name = pane.getAttribute('data-tab');
const title = pane.getAttribute('data-tab-title');
const active = pane.getAttribute('data-tab-active') === 'true';
*/
getActiveTabId(pane) {
const path = this.getPathForPane(pane);
- return +this.getActiveTabState().paths[path] ?? 0;
+ const p = +(this.getActiveTabState().paths[path]);
+ return p ?? 0;
},
/**
const name = tab.getAttribute('data-tab');
const menu = tab.parentNode;
const group = menu.nextElementSibling;
- const groupId = +group.getAttribute('data-tab-group');
let index = 0;
ev.preventDefault();
for (let i = 0; i < 2; i++)
for (let j = 0; j < ipaddrs.length; j++)
tasks.push(this.pingDevice(i ? 'https' : 'http', ipaddrs[j])
- .then(ev => { reachable = ev.target.src.replace(/^(https?:\/\/[^\/]+).*$/, '$1/') }, () => {}));
+ .then(ev => { reachable = ev.target.src.replace(/^(https?:\/\/[^/]+).*$/, '$1/') }, () => {}));
return Promise.all(tasks).then(() => {
if (reachable) {
_('valid IPv6 network'));
},
- iprange(negative) {
- return this.assert(this.apply('iprange4', null, [negative]) || this.apply('iprange6', null, [negative]),
+ iprange() {
+ return this.assert(this.apply('iprange4', null, []) || this.apply('iprange6', null, []),
_('valid IP address range'));
},
- iprange4(negative) {
+ iprange4() {
const m = this.value.split('-');
return this.assert(m.length == 2 && arrayle(this.factory.parseIPv4(m[0]), this.factory.parseIPv4(m[1])),
_('valid IPv4 address range'));
},
- iprange6(negative) {
+ iprange6() {
const m = this.value.split('-');
return this.assert(m.length == 2 && arrayle(this.factory.parseIPv6(m[0]), this.factory.parseIPv6(m[1])),
_('valid IPv6 address range'));
},
ipaddrport(bracket) {
- const m4 = this.value.match(/^([^\[\]:]+):(\d+)$/);
- const m6 = this.value.match((bracket == 1) ? /^\[(.+)\]:(\d+)$/ : /^([^\[\]]+):(\d+)$/);
+ const m4 = this.value.match(/^([^[\]:]+):(\d+)$/);
+ const m6 = this.value.match((bracket == 1) ? /^\[(.+)\]:(\d+)$/ : /^([^[\]]+):(\d+)$/);
if (m4)
return this.assert(this.apply('ip4addr', m4[1], [true]) && this.apply('port', m4[2]),
if (v == '.' || v == '..')
return this.assert(false, _('valid network device name, not "." or ".."'));
- return this.assert(v.match(/^[^:\/%\s]{1,15}$/), _('valid network device name between 1 and 15 characters not containing ":", "/", "%" or spaces'));
+ return this.assert(v.match(/^[^:/%\s]{1,15}$/), _('valid network device name between 1 and 15 characters not containing ":", "/", "%" or spaces'));
},
range(min, max) {
},
phonedigit() {
- return this.assert(this.value.match(/^[0-9\*#!\.]+$/),
+ return this.assert(this.value.match(/^[0-9*#!.]+$/),
_('valid phone digit (0-9, "*", "#", "!" or ".")'));
},