luci-base: js linting fixes / ES6 treatment

prevent global variable bleed and reduce size somewhat

-cbi
-network
-protocol/static
-rpc
-tools/widgets
-tools/views
-uci

protocols

Signed-off-by: Paul Donald <newtwen+github@gmail.com>
This commit is contained in:
Paul Donald
2026-02-14 19:46:15 +01:00
parent 394d92e042
commit 50a8d5325d
12 changed files with 996 additions and 1045 deletions

View File

@@ -19,8 +19,8 @@
* defined here are registered as global `window.*` symbols. * defined here are registered as global `window.*` symbols.
* @module LuCI.cbi * @module LuCI.cbi
*/ */
var cbi_d = []; const cbi_d = [];
var cbi_strings = { path: {}, label: {} }; const cbi_strings = { path: {}, label: {} };
/** /**
* Read signed 8-bit integer from a byte array at the given offset. * Read signed 8-bit integer from a byte array at the given offset.
@@ -29,7 +29,7 @@ var cbi_strings = { path: {}, label: {} };
* @returns {number} Signed 8-bit value (returned as unsigned number). * @returns {number} Signed 8-bit value (returned as unsigned number).
*/ */
function s8(bytes, off) { function s8(bytes, off) {
var n = bytes[off]; const n = bytes[off];
return (n > 0x7F) ? (n - 256) >>> 0 : n; return (n > 0x7F) ? (n - 256) >>> 0 : n;
} }
@@ -53,10 +53,10 @@ function sfh(s) {
if (s === null || s.length === 0) if (s === null || s.length === 0)
return null; return null;
var bytes = []; const bytes = [];
for (var i = 0; i < s.length; i++) { for (let i = 0; i < s.length; i++) {
var ch = s.charCodeAt(i); let ch = s.charCodeAt(i);
// Handle surrogate pairs // Handle surrogate pairs
if (ch >= 0xD800 && ch <= 0xDBFF && i + 1 < s.length) { if (ch >= 0xD800 && ch <= 0xDBFF && i + 1 < s.length) {
@@ -86,9 +86,9 @@ function sfh(s) {
if (!bytes.length) if (!bytes.length)
return null; return null;
var hash = (bytes.length >>> 0), let hash = (bytes.length >>> 0);
len = (bytes.length >>> 2), let len = (bytes.length >>> 2);
off = 0, tmp; let off = 0, tmp;
while (len--) { while (len--) {
hash += u16(bytes, off); hash += u16(bytes, off);
@@ -300,7 +300,7 @@ function cbi_d_update() {
* placeholders with interactive widgets. * placeholders with interactive widgets.
*/ */
function cbi_init() { function cbi_init() {
var nodes; let nodes;
document.querySelectorAll('.cbi-dropdown').forEach(function(node) { document.querySelectorAll('.cbi-dropdown').forEach(function(node) {
cbi_dropdown_init(node); cbi_dropdown_init(node);
@@ -309,65 +309,65 @@ function cbi_init() {
nodes = document.querySelectorAll('[data-strings]'); nodes = document.querySelectorAll('[data-strings]');
for (let i = 0, node; (node = nodes[i]) !== undefined; i++) { for (let n of nodes) {
var str = JSON.parse(node.getAttribute('data-strings')); const str = JSON.parse(n.getAttribute('data-strings'));
for (var key in str) { for (let key in str) {
for (var key2 in str[key]) { for (let key2 in str[key]) {
var dst = cbi_strings[key] || (cbi_strings[key] = { }); const dst = cbi_strings[key] || (cbi_strings[key] = { });
dst[key2] = str[key][key2]; dst[key2] = str[key][key2];
} }
} }
} }
nodes = document.querySelectorAll('[data-depends]'); nodes = document.querySelectorAll('[data-depends]');
for (let i = 0, node; (node = nodes[i]) !== undefined; i++) { for (let n of nodes) {
var index = parseInt(node.getAttribute('data-index'), 10); const index = parseInt(n.getAttribute('data-index'), 10);
var depends = JSON.parse(node.getAttribute('data-depends')); const depends = JSON.parse(n.getAttribute('data-depends'));
if (!isNaN(index) && depends.length > 0) { if (!isNaN(index) && depends.length > 0) {
for (let alt = 0; alt < depends.length; alt++) for (let a of depends)
cbi_d_add(node, depends[alt], index); cbi_d_add(n, depends[a], index);
} }
} }
nodes = document.querySelectorAll('[data-update]'); nodes = document.querySelectorAll('[data-update]');
for (let i = 0, node; (node = nodes[i]) !== undefined; i++) { for (let n of nodes) {
var events = node.getAttribute('data-update').split(' '); const events = n.getAttribute('data-update').split(' ');
for (let j = 0, event; (event = events[j]) !== undefined; j++) for (let ev of events)
node.addEventListener(event, cbi_d_update); n.addEventListener(ev, cbi_d_update);
} }
nodes = document.querySelectorAll('[data-choices]'); nodes = document.querySelectorAll('[data-choices]');
for (let i = 0, node; (node = nodes[i]) !== undefined; i++) { for (let node of nodes) {
let choices = JSON.parse(node.getAttribute('data-choices')), const choices = JSON.parse(node.getAttribute('data-choices'));
options = {}; const options = {};
for (let j = 0; j < choices[0].length; j++) for (let j = 0; j < choices[0].length; j++)
options[choices[0][j]] = choices[1][j]; options[choices[0][j]] = choices[1][j];
var def = (node.getAttribute('data-optional') === 'true') const def = (node.getAttribute('data-optional') === 'true')
? node.placeholder || '' : null; ? node.placeholder || '' : null;
var cb = new L.ui.Combobox(node.value, options, { const cb = new L.ui.Combobox(node.value, options, {
name: node.getAttribute('name'), name: node.getAttribute('name'),
sort: choices[0], sort: choices[0],
select_placeholder: def || _('-- Please choose --'), select_placeholder: def || _('-- Please choose --'),
custom_placeholder: node.getAttribute('data-manual') || _('-- custom --') custom_placeholder: node.getAttribute('data-manual') || _('-- custom --')
}); });
var n = cb.render(); const n = cb.render();
n.addEventListener('cbi-dropdown-change', cbi_d_update); n.addEventListener('cbi-dropdown-change', cbi_d_update);
node.parentNode.replaceChild(n, node); node.parentNode.replaceChild(n, node);
} }
nodes = document.querySelectorAll('[data-dynlist]'); nodes = document.querySelectorAll('[data-dynlist]');
for (let i = 0, node; (node = nodes[i]) !== undefined; i++) { for (let node of nodes) {
let choices = JSON.parse(node.getAttribute('data-dynlist')), const choices = JSON.parse(node.getAttribute('data-dynlist'));
values = JSON.parse(node.getAttribute('data-values') || '[]'), const values = JSON.parse(node.getAttribute('data-values') || '[]');
options = null; let options = null;
if (choices[0] && choices[0].length) { if (choices[0] && choices[0].length) {
options = {}; options = {};
@@ -391,7 +391,7 @@ function cbi_init() {
nodes = document.querySelectorAll('[data-type]'); nodes = document.querySelectorAll('[data-type]');
for (let i = 0, node; (node = nodes[i]) !== undefined; i++) { for (let node of nodes) {
cbi_validate_field(node, node.getAttribute('data-optional') === 'true', cbi_validate_field(node, node.getAttribute('data-optional') === 'true',
node.getAttribute('data-type')); node.getAttribute('data-type'));
} }
@@ -415,9 +415,9 @@ function cbi_init() {
var tasks = []; var tasks = [];
document.querySelectorAll('[data-ui-widget]').forEach(function(node) { document.querySelectorAll('[data-ui-widget]').forEach(function(node) {
let args = JSON.parse(node.getAttribute('data-ui-widget') || '[]'), const args = JSON.parse(node.getAttribute('data-ui-widget') || '[]');
widget = new (Function.prototype.bind.apply(L.ui[args[0]], args)), const widget = new (Function.prototype.bind.apply(L.ui[args[0]], args));
markup = widget.render(); const markup = widget.render();
tasks.push(Promise.resolve(markup).then(function(markup) { tasks.push(Promise.resolve(markup).then(function(markup) {
markup.addEventListener('widget-change', cbi_d_update); markup.addEventListener('widget-change', cbi_d_update);
@@ -441,8 +441,8 @@ function cbi_validate_form(form, errmsg)
return true; return true;
if (form.cbi_validators) { if (form.cbi_validators) {
for (var i = 0; i < form.cbi_validators.length; i++) { for (let fv of form.cbi_validators) {
var validator = form.cbi_validators[i]; const validator = fv;
if (!validator() && errmsg) { if (!validator() && errmsg) {
alert(errmsg); alert(errmsg);
@@ -584,10 +584,10 @@ function cbi_row_swap(elem, up, store)
*/ */
function cbi_tag_last(container) function cbi_tag_last(container)
{ {
var last; let last;
for (var i = 0; i < container.childNodes.length; i++) { for (let cn of container.childNodes) {
var c = container.childNodes[i]; var c = cn;
if (matchesElem(c, 'div')) { if (matchesElem(c, 'div')) {
c.classList.remove('cbi-value-last'); c.classList.remove('cbi-value-last');
last = c; last = c;
@@ -608,7 +608,7 @@ function cbi_tag_last(container)
*/ */
function cbi_submit(elem, name, value, action) function cbi_submit(elem, name, value, action)
{ {
var form = elem.form || findParent(elem, 'form'); const form = elem.form || findParent(elem, 'form');
if (!form) if (!form)
return false; return false;
@@ -644,8 +644,8 @@ String.prototype.format = function()
if (!RegExp) if (!RegExp)
return; return;
var html_esc = [/&/g, '&#38;', /"/g, '&#34;', /'/g, '&#39;', /</g, '&#60;', />/g, '&#62;']; const html_esc = [/&/g, '&#38;', /"/g, '&#34;', /'/g, '&#39;', /</g, '&#60;', />/g, '&#62;'];
var quot_esc = [/"/g, '&#34;', /'/g, '&#39;']; const quot_esc = [/"/g, '&#34;', /'/g, '&#39;'];
/** /**
* Escape a string. * Escape a string.
@@ -656,7 +656,7 @@ String.prototype.format = function()
* @returns {string} * @returns {string}
*/ */
function esc(s, r) { function esc(s, r) {
var t = typeof(s); const t = typeof(s);
if (s == null || t === 'object' || t === 'function') if (s == null || t === 'object' || t === 'function')
return ''; return '';
@@ -664,32 +664,31 @@ String.prototype.format = function()
if (t !== 'string') if (t !== 'string')
s = String(s); s = String(s);
for (var i = 0; i < r.length; i += 2) for (let i = 0; i < r.length; i += 2)
s = s.replace(r[i], r[i+1]); s = s.replace(r[i], r[i+1]);
return s; return s;
} }
var str = this; let str = this;
var out = ''; let subst, n, pad;
var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/; let out = '';
var a = b = [], numSubstitutions = 0, numMatches = 0; const re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/;
let a = [], numSubstitutions = 0;
while ((a = re.exec(str)) !== null) { while ((a = re.exec(str)) !== null) {
var m = a[1]; const m = a[1];
var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5]; let leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5];
var pPrecision = a[6], pType = a[7]; let pPrecision = a[6], pType = a[7];
numMatches++;
if (pType == '%') { if (pType == '%') {
subst = '%'; subst = '%';
} }
else { else {
if (numSubstitutions < arguments.length) { if (numSubstitutions < arguments.length) {
var param = arguments[numSubstitutions++]; let param = arguments[numSubstitutions++];
var pad = ''; pad = '';
if (pPad && pPad.substr(0,1) == "'") if (pPad && pPad.substr(0,1) == "'")
pad = leftpart.substr(1,1); pad = leftpart.substr(1,1);
else if (pPad) else if (pPad)
@@ -697,19 +696,11 @@ String.prototype.format = function()
else else
pad = ' '; pad = ' ';
var justifyRight = true;
if (pJustify && pJustify === "-")
justifyRight = false;
var minLength = -1;
if (pMinLength)
minLength = +pMinLength;
var precision = -1; var precision = -1;
if (pPrecision && pType == 'f') if (pPrecision && pType == 'f')
precision = +pPrecision.substring(1); precision = +pPrecision.substring(1);
var subst = param; subst = param;
switch(pType) { switch(pType) {
case 'b': case 'b':
@@ -725,7 +716,7 @@ String.prototype.format = function()
break; break;
case 'u': case 'u':
var n = +param || 0; n = +param || 0;
subst = Math.floor((n < 0) ? 0x100000000 + n : n).toFixed(0); subst = Math.floor((n < 0) ? 0x100000000 + n : n).toFixed(0);
break; break;
@@ -845,10 +836,10 @@ String.prototype.nobr = function()
*/ */
String.format = function() String.format = function()
{ {
var a = [ ]; const a = [ ];
for (var i = 1; i < arguments.length; i++) for (let ar of arguments)
a.push(arguments[i]); a.push(ar);
return ''.format.apply(arguments[0], a); return ''.format.apply(arguments[0], a);
} }
@@ -862,10 +853,10 @@ String.format = function()
*/ */
String.nobr = function() String.nobr = function()
{ {
var a = [ ]; const a = [ ];
for (var i = 1; i < arguments.length; i++) for (let ar of arguments)
a.push(arguments[i]); a.push(ar);
return ''.nobr.apply(arguments[0], a); return ''.nobr.apply(arguments[0], a);
} }
@@ -927,7 +918,7 @@ function cbi_dropdown_init(sb) {
if (sb && L.dom.findClassInstance(sb) instanceof L.ui.Dropdown) if (sb && L.dom.findClassInstance(sb) instanceof L.ui.Dropdown)
return; return;
var dl = new L.ui.Dropdown(sb, null, { name: sb.getAttribute('name') }); const dl = new L.ui.Dropdown(sb, null, { name: sb.getAttribute('name') });
return dl.bind(sb); return dl.bind(sb);
} }
@@ -938,12 +929,12 @@ function cbi_dropdown_init(sb) {
* @param {string} [placeholder] - Placeholder text when empty. * @param {string} [placeholder] - Placeholder text when empty.
*/ */
function cbi_update_table(table, data, placeholder) { function cbi_update_table(table, data, placeholder) {
var target = isElem(table) ? table : document.querySelector(table); const target = isElem(table) ? table : document.querySelector(table);
if (!isElem(target)) if (!isElem(target))
return; return;
var t = L.dom.findClassInstance(target); let t = L.dom.findClassInstance(target);
if (!(t instanceof L.ui.Table)) { if (!(t instanceof L.ui.Table)) {
t = new L.ui.Table(target); t = new L.ui.Table(target);

View File

@@ -33,52 +33,43 @@
* @property {string} [stderr] - The stderr produced by the command, if any * @property {string} [stderr] - The stderr produced by the command, if any
*/ */
var callFileList, callFileStat, callFileRead, callFileWrite, callFileRemove, const callFileList = rpc.declare({
callFileExec, callFileMD5;
callFileList = rpc.declare({
object: 'file', object: 'file',
method: 'list', method: 'list',
params: [ 'path' ] params: [ 'path' ]
}); });
callFileStat = rpc.declare({ const callFileStat = rpc.declare({
object: 'file', object: 'file',
method: 'stat', method: 'stat',
params: [ 'path' ] params: [ 'path' ]
}); });
callFileRead = rpc.declare({ const callFileRead = rpc.declare({
object: 'file', object: 'file',
method: 'read', method: 'read',
params: [ 'path' ] params: [ 'path' ]
}); });
callFileWrite = rpc.declare({ const callFileWrite = rpc.declare({
object: 'file', object: 'file',
method: 'write', method: 'write',
params: [ 'path', 'data', 'mode' ] params: [ 'path', 'data', 'mode' ]
}); });
callFileRemove = rpc.declare({ const callFileRemove = rpc.declare({
object: 'file', object: 'file',
method: 'remove', method: 'remove',
params: [ 'path' ] params: [ 'path' ]
}); });
callFileExec = rpc.declare({ const callFileExec = rpc.declare({
object: 'file', object: 'file',
method: 'exec', method: 'exec',
params: [ 'command', 'params', 'env' ] params: [ 'command', 'params', 'env' ]
}); });
callFileMD5 = rpc.declare({ const rpcErrors = [
object: 'file',
method: 'md5',
params: [ 'path' ]
});
var rpcErrors = [
null, null,
'InvalidCommandError', 'InvalidCommandError',
'InvalidArgumentError', 'InvalidArgumentError',
@@ -183,7 +174,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* Returns a promise resolving to an array of stat detail objects or * Returns a promise resolving to an array of stat detail objects or
* rejecting with an error stating the failure reason. * rejecting with an error stating the failure reason.
*/ */
list: function(path) { list(path) {
return callFileList(path).then(handleRpcReply.bind(this, { entries: [] })); return callFileList(path).then(handleRpcReply.bind(this, { entries: [] }));
}, },
@@ -197,7 +188,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* Returns a promise resolving to a stat detail object or * Returns a promise resolving to a stat detail object or
* rejecting with an error stating the failure reason. * rejecting with an error stating the failure reason.
*/ */
stat: function(path) { stat(path) {
return callFileStat(path).then(handleRpcReply.bind(this, { '': {} })); return callFileStat(path).then(handleRpcReply.bind(this, { '': {} }));
}, },
@@ -212,7 +203,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* Returns a promise resolving to a string containing the file contents or * Returns a promise resolving to a string containing the file contents or
* rejecting with an error stating the failure reason. * rejecting with an error stating the failure reason.
*/ */
read: function(path) { read(path) {
return callFileRead(path).then(handleRpcReply.bind(this, { data: '' })); return callFileRead(path).then(handleRpcReply.bind(this, { data: '' }));
}, },
@@ -238,7 +229,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* Returns a promise resolving to `0` or rejecting with an error stating * Returns a promise resolving to `0` or rejecting with an error stating
* the failure reason. * the failure reason.
*/ */
write: function(path, data, mode) { write(path, data, mode) {
data = (data != null) ? String(data) : ''; data = (data != null) ? String(data) : '';
mode = (mode != null) ? mode : 420; // 0644 mode = (mode != null) ? mode : 420; // 0644
return callFileWrite(path, data, mode).then(handleRpcReply.bind(this, { '': 0 })); return callFileWrite(path, data, mode).then(handleRpcReply.bind(this, { '': 0 }));
@@ -254,7 +245,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* Returns a promise resolving to `0` or rejecting with an error stating * Returns a promise resolving to `0` or rejecting with an error stating
* the failure reason. * the failure reason.
*/ */
remove: function(path) { remove(path) {
return callFileRemove(path).then(handleRpcReply.bind(this, { '': 0 })); return callFileRemove(path).then(handleRpcReply.bind(this, { '': 0 }));
}, },
@@ -283,7 +274,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* Returns a promise resolving to an object describing the execution * Returns a promise resolving to an object describing the execution
* results or rejecting with an error stating the failure reason. * results or rejecting with an error stating the failure reason.
*/ */
exec: function(command, params, env) { exec(command, params, env) {
if (!Array.isArray(params)) if (!Array.isArray(params))
params = null; params = null;
@@ -311,7 +302,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* Returns a promise resolving to the file contents or the empty string * Returns a promise resolving to the file contents or the empty string
* on failure. * on failure.
*/ */
trimmed: function(path) { trimmed(path) {
return L.resolveDefault(this.read(path), '').then(function(s) { return L.resolveDefault(this.read(path), '').then(function(s) {
return s.trim(); return s.trim();
}); });
@@ -332,7 +323,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* Returns a promise resolving to an array containing the stripped lines * Returns a promise resolving to an array containing the stripped lines
* of the given file or `[]` on failure. * of the given file or `[]` on failure.
*/ */
lines: function(path) { lines(path) {
return L.resolveDefault(this.read(path), '').then(function(s) { return L.resolveDefault(this.read(path), '').then(function(s) {
var lines = []; var lines = [];
@@ -373,7 +364,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* to the specified type or rejecting with an error stating the failure * to the specified type or rejecting with an error stating the failure
* reason. * reason.
*/ */
read_direct: function(path, type) { read_direct(path, type) {
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));
@@ -430,7 +421,7 @@ var FileSystem = baseclass.extend(/** @lends LuCI.fs.prototype */ {
* according to the specified type or rejecting with an error stating the * according to the specified type or rejecting with an error stating the
* failure reason. * failure reason.
*/ */
exec_direct: function(command, params, type, latin1, stderr, responseProgress) { exec_direct(command, params, type, latin1, stderr, responseProgress) {
var cmdstr = String(command) var cmdstr = String(command)
.replace(/\\/g, '\\\\').replace(/(\s)/g, '\\$1'); .replace(/\\/g, '\\\\').replace(/(\s)/g, '\\$1');

File diff suppressed because it is too large Load Diff

View File

@@ -67,9 +67,9 @@ function calculateBroadcast(s, use_cfgvalue) {
* @returns {boolean} * @returns {boolean}
*/ */
function validateBroadcast(section_id, value) { function validateBroadcast(section_id, value) {
var opt = this.map.lookupOption('broadcast', section_id), const opt = this.map.lookupOption('broadcast', section_id);
node = opt ? this.map.findElement('id', opt[0].cbid(section_id)) : null, const node = opt ? this.map.findElement('id', opt[0].cbid(section_id)) : null;
addr = node ? calculateBroadcast(this.section, false) : null; const addr = node ? calculateBroadcast(this.section, false) : null;
if (node != null) { if (node != null) {
if (addr != null) if (addr != null)
@@ -83,38 +83,37 @@ function validateBroadcast(section_id, value) {
return network.registerProtocol('static', { return network.registerProtocol('static', {
CBIIPValue: form.Value.extend({ CBIIPValue: form.Value.extend({
handleSwitch: function(section_id, option_index, ev) { handleSwitch(section_id, option_index, ev) {
var maskopt = this.map.lookupOption('netmask', section_id); const maskopt = this.map.lookupOption('netmask', section_id);
if (maskopt == null || !this.isValid(section_id)) if (maskopt == null || !this.isValid(section_id))
return; return;
var maskval = maskopt[0].formvalue(section_id), const maskval = maskopt[0].formvalue(section_id);
addrval = this.formvalue(section_id), const addrval = this.formvalue(section_id);
prefix = maskval ? network.maskToPrefix(maskval) : 32; const prefix = maskval ? network.maskToPrefix(maskval) : 32;
if (prefix == null) if (prefix == null)
return; return;
this.datatype = 'or(cidr4,ipmask4)'; this.datatype = 'or(cidr4,ipmask4)';
var parent = L.dom.parent(ev.target, '.cbi-value-field'); let parent = L.dom.parent(ev.target, '.cbi-value-field');
L.dom.content(parent, form.DynamicList.prototype.renderWidget.apply(this, [ L.dom.content(parent, form.DynamicList.prototype.renderWidget.apply(this, [
section_id, section_id,
option_index, option_index,
addrval ? '%s/%d'.format(addrval, prefix) : '' addrval ? '%s/%d'.format(addrval, prefix) : ''
])); ]));
var masknode = this.map.findElement('id', maskopt[0].cbid(section_id)); const masknode = this.map.findElement('id', maskopt[0].cbid(section_id));
if (masknode) { if (masknode) {
parent = L.dom.parent(masknode, '.cbi-value'); parent = L.dom.parent(masknode, '.cbi-value');
parent.parentNode.removeChild(parent); parent.parentNode.removeChild(parent);
} }
}, },
renderWidget: function(section_id, option_index, cfgvalue) { renderWidget(section_id, option_index, cfgvalue) {
var maskopt = this.map.lookupOption('netmask', section_id), const widget = isCIDR(cfgvalue) ? 'DynamicList' : 'Value';
widget = isCIDR(cfgvalue) ? 'DynamicList' : 'Value';
if (widget == 'DynamicList') { if (widget == 'DynamicList') {
this.datatype = 'or(cidr4,ipmask4)'; this.datatype = 'or(cidr4,ipmask4)';
@@ -124,7 +123,7 @@ return network.registerProtocol('static', {
this.datatype = 'ip4addr("nomask")'; this.datatype = 'ip4addr("nomask")';
} }
var node = form[widget].prototype.renderWidget.apply(this, [ section_id, option_index, cfgvalue ]); const node = form[widget].prototype.renderWidget.apply(this, [ section_id, option_index, cfgvalue ]);
if (widget == 'Value') if (widget == 'Value')
L.dom.append(node, E('button', { L.dom.append(node, E('button', {
@@ -141,7 +140,7 @@ return network.registerProtocol('static', {
}), }),
CBINetmaskValue: form.Value.extend({ CBINetmaskValue: form.Value.extend({
render: function(option_index, section_id, in_table) { render(option_index, section_id, in_table) {
var addropt = this.section.children.filter(function(o) { return o.option == 'ipaddr' })[0], var addropt = this.section.children.filter(function(o) { return o.option == 'ipaddr' })[0],
addrval = addropt ? addropt.cfgvalue(section_id) : null; addrval = addropt ? addropt.cfgvalue(section_id) : null;
@@ -162,7 +161,7 @@ return network.registerProtocol('static', {
CBIGatewayValue: form.Value.extend({ CBIGatewayValue: form.Value.extend({
datatype: 'ip4addr("nomask")', datatype: 'ip4addr("nomask")',
render: function(option_index, section_id, in_table) { render(option_index, section_id, in_table) {
return network.getWANNetworks().then(L.bind(function(wans) { return network.getWANNetworks().then(L.bind(function(wans) {
if (wans.length == 1) { if (wans.length == 1) {
var gwaddr = wans[0].getGatewayAddr(); var gwaddr = wans[0].getGatewayAddr();
@@ -173,13 +172,13 @@ return network.registerProtocol('static', {
}, this)); }, this));
}, },
validate: function(section_id, value) { validate(section_id, value) {
var addropt = this.section.children.filter(function(o) { return o.option == 'ipaddr' })[0], const addropt = this.section.children.filter(function(o) { return o.option == 'ipaddr' })[0];
addrval = addropt ? L.toArray(addropt.cfgvalue(section_id)) : null; const addrval = addropt ? L.toArray(addropt.cfgvalue(section_id)) : null;
if (addrval != null) { if (addrval != null) {
for (var i = 0; i < addrval.length; i++) { for (let a of addrval) {
var addr = addrval[i].split('/')[0]; const addr = a.split('/')[0];
if (value == addr) if (value == addr)
return _('The gateway address must not be a local IP address'); return _('The gateway address must not be a local IP address');
} }
@@ -192,18 +191,18 @@ return network.registerProtocol('static', {
CBIBroadcastValue: form.Value.extend({ CBIBroadcastValue: form.Value.extend({
datatype: 'ip4addr("nomask")', datatype: 'ip4addr("nomask")',
render: function(option_index, section_id, in_table) { render(option_index, section_id, in_table) {
this.placeholder = calculateBroadcast(this.section, true); this.placeholder = calculateBroadcast(this.section, true);
return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]); return form.Value.prototype.render.apply(this, [ option_index, section_id, in_table ]);
} }
}), }),
getI18n: function() { getI18n() {
return _('Static address'); return _('Static address');
}, },
renderFormOptions: function(s) { renderFormOptions(s) {
var o; let o;
s.taboption('general', this.CBIIPValue, 'ipaddr', _('IPv4 address')); s.taboption('general', this.CBIIPValue, 'ipaddr', _('IPv4 address'));
s.taboption('general', this.CBINetmaskValue, 'netmask', _('IPv4 netmask')); s.taboption('general', this.CBINetmaskValue, 'netmask', _('IPv4 netmask'));

View File

@@ -173,7 +173,7 @@ var CBILogreadBox = function(logtag, name) {
'class': 'cbi-input-select', 'class': 'cbi-input-select',
'style': 'margin-bottom:10px', 'style': 'margin-bottom:10px',
}, },
this.facilities.map(([_, val, label]) => this.facilities.map(([ , val, label]) =>
(val == 'any') ? E('option', { value: val, selected: '' }, label) : E('option', { value: val }, label) (val == 'any') ? E('option', { value: val, selected: '' }, label) : E('option', { value: val }, label)
)); ));
@@ -189,7 +189,7 @@ var CBILogreadBox = function(logtag, name) {
'id': 'logSeveritySelect', 'id': 'logSeveritySelect',
'class': 'cbi-input-select', 'class': 'cbi-input-select',
}, },
this.severity.map(([_, val, label]) => this.severity.map(([ , val, label]) =>
(val == 'any') ? E('option', { value: val, selected: '' }, label) : E('option', { value: val }, label) (val == 'any') ? E('option', { value: val, selected: '' }, label) : E('option', { value: val }, label)
)); ));

View File

@@ -43,7 +43,7 @@ function getDevices(network) {
var CBIZoneSelect = form.ListValue.extend({ var CBIZoneSelect = form.ListValue.extend({
__name__: 'CBI.ZoneSelect', __name__: 'CBI.ZoneSelect',
load: function(section_id) { load(section_id) {
return Promise.all([ firewall.getZones(), network.getNetworks() ]).then(L.bind(function(zn) { return Promise.all([ firewall.getZones(), network.getNetworks() ]).then(L.bind(function(zn) {
this.zones = zn[0]; this.zones = zn[0];
this.networks = zn[1]; this.networks = zn[1];
@@ -52,22 +52,22 @@ var CBIZoneSelect = form.ListValue.extend({
}, this)); }, this));
}, },
filter: function(section_id, value) { filter(section_id, value) {
return true; return true;
}, },
lookupZone: function(name) { lookupZone(name) {
return this.zones.filter(function(zone) { return zone.getName() == name })[0]; return this.zones.filter(function(zone) { return zone.getName() == name })[0];
}, },
lookupNetwork: function(name) { lookupNetwork(name) {
return this.networks.filter(function(network) { return network.getName() == name })[0]; return this.networks.filter(function(network) { return network.getName() == name })[0];
}, },
renderWidget: function(section_id, option_index, cfgvalue) { renderWidget(section_id, option_index, cfgvalue) {
var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default), const values = L.toArray((cfgvalue != null) ? cfgvalue : this.default);
isOutputOnly = false, let isOutputOnly = false;
choices = {}; const choices = {};
let datatype_str = 'ucifw4zonename'; let datatype_str = 'ucifw4zonename';
if (!L.hasSystemFeature('firewall4')) if (!L.hasSystemFeature('firewall4'))
datatype_str = `and(${datatype_str},maxlength(11))`; datatype_str = `and(${datatype_str},maxlength(11))`;
@@ -77,10 +77,10 @@ var CBIZoneSelect = form.ListValue.extend({
datatype_str = `list(${datatype_str})`; datatype_str = `list(${datatype_str})`;
if (this.option == 'dest') { if (this.option == 'dest') {
for (var i = 0; i < this.section.children.length; i++) { for (let c of this.section.children) {
var opt = this.section.children[i]; const opt = c;
if (opt.option == 'src') { if (opt.option == 'src') {
var val = opt.cfgvalue(section_id) || opt.default; const val = opt.cfgvalue(section_id) || opt.default;
isOutputOnly = (val == null || val == ''); isOutputOnly = (val == null || val == '');
break; break;
} }
@@ -116,31 +116,30 @@ var CBIZoneSelect = form.ListValue.extend({
]); ]);
} }
for (var i = 0; i < this.zones.length; i++) { for (let zone of this.zones) {
var zone = this.zones[i], const name = zone.getName();
name = zone.getName(), const networks = zone.getNetworks();
networks = zone.getNetworks(), const ifaces = [];
ifaces = [];
if (!this.filter(section_id, name)) if (!this.filter(section_id, name))
continue; continue;
for (var j = 0; j < networks.length; j++) { for (let n of networks) {
var network = this.lookupNetwork(networks[j]); const network = this.lookupNetwork(n);
if (!network) if (!network)
continue; continue;
var span = E('span', { const span = E('span', {
'class': 'ifacebadge' + (network.isUp() ? ' ifacebadge-active' : '') 'class': 'ifacebadge' + (network.isUp() ? ' ifacebadge-active' : '')
}, network.getName() + ': '); }, network.getName() + ': ');
var devices = getDevices(network); const devices = getDevices(network);
for (var k = 0; k < devices.length; k++) { for (let d of devices) {
span.appendChild(E('img', { span.appendChild(E('img', {
'title': devices[k].getI18n(), 'title': d.getI18n(),
'src': L.resource('icons/%s%s.svg'.format(devices[k].getType(), devices[k].isUp() ? '' : '_disabled')) 'src': L.resource('icons/%s%s.svg'.format(d.getType(), d.isUp() ? '' : '_disabled'))
})); }));
} }
@@ -159,7 +158,7 @@ var CBIZoneSelect = form.ListValue.extend({
}, [ E('strong', name) ].concat(ifaces)); }, [ E('strong', name) ].concat(ifaces));
} }
var widget = new ui.Dropdown(values, choices, { const widget = new ui.Dropdown(values, choices, {
id: this.cbid(section_id), id: this.cbid(section_id),
sort: true, sort: true,
multiple: this.multiple, multiple: this.multiple,
@@ -179,19 +178,19 @@ var CBIZoneSelect = form.ListValue.extend({
'</li>' '</li>'
}); });
var elem = widget.render(); const elem = widget.render();
if (this.option == 'src') { if (this.option == 'src') {
elem.addEventListener('cbi-dropdown-change', L.bind(function(ev) { elem.addEventListener('cbi-dropdown-change', L.bind(function(ev) {
var opt = this.map.lookupOption('dest', section_id), const opt = this.map.lookupOption('dest', section_id);
val = ev.detail.instance.getValue(); const val = ev.detail.instance.getValue();
if (opt == null) if (opt == null)
return; return;
var cbid = opt[0].cbid(section_id), const cbid = opt[0].cbid(section_id);
label = document.querySelector('label[for="widget.%s"]'.format(cbid)), const label = document.querySelector('label[for="widget.%s"]'.format(cbid));
node = document.getElementById(cbid); const node = document.getElementById(cbid);
L.dom.content(label, val == '' ? _('Output zone') : _('Destination zone')); L.dom.content(label, val == '' ? _('Output zone') : _('Destination zone'));
@@ -199,8 +198,8 @@ var CBIZoneSelect = form.ListValue.extend({
if (L.dom.callClassMethod(node, 'getValue') == '') if (L.dom.callClassMethod(node, 'getValue') == '')
L.dom.callClassMethod(node, 'setValue', '*'); L.dom.callClassMethod(node, 'setValue', '*');
var emptyval = node.querySelector('[data-value=""]'), const emptyval = node.querySelector('[data-value=""]');
anyval = node.querySelector('[data-value="*"]'); const anyval = node.querySelector('[data-value="*"]');
L.dom.content(anyval.querySelector('span'), E('strong', _('Any zone'))); L.dom.content(anyval.querySelector('span'), E('strong', _('Any zone')));
@@ -234,7 +233,7 @@ var CBIZoneSelect = form.ListValue.extend({
}, this)); }, this));
} }
else if (isOutputOnly) { else if (isOutputOnly) {
var emptyval = elem.querySelector('[data-value=""]'); const emptyval = elem.querySelector('[data-value=""]');
emptyval.parentNode.removeChild(emptyval); emptyval.parentNode.removeChild(emptyval);
} }
@@ -245,7 +244,7 @@ var CBIZoneSelect = form.ListValue.extend({
var CBIZoneForwards = form.DummyValue.extend({ var CBIZoneForwards = form.DummyValue.extend({
__name__: 'CBI.ZoneForwards', __name__: 'CBI.ZoneForwards',
load: function(section_id) { load(section_id) {
return Promise.all([ return Promise.all([
firewall.getDefaults(), firewall.getDefaults(),
firewall.getZones(), firewall.getZones(),
@@ -261,29 +260,29 @@ var CBIZoneForwards = form.DummyValue.extend({
}, this)); }, this));
}, },
renderZone: function(zone) { renderZone(zone) {
var name = zone.getName(), const name = zone.getName();
networks = zone.getNetworks(), const networks = zone.getNetworks();
devices = zone.getDevices(), const devices = zone.getDevices();
subnets = zone.getSubnets(), const subnets = zone.getSubnets();
ifaces = []; const ifaces = [];
for (var j = 0; j < networks.length; j++) { for (let n of networks) {
var network = this.networks.filter(function(net) { return net.getName() == networks[j] })[0]; const network = this.networks.filter(function(net) { return net.getName() == n })[0];
if (!network) if (!network)
continue; continue;
var span = E('span', { const span = E('span', {
'class': 'ifacebadge' + (network.isUp() ? ' ifacebadge-active' : '') 'class': 'ifacebadge' + (network.isUp() ? ' ifacebadge-active' : '')
}, network.getName() + ': '); }, network.getName() + ': ');
var subdevs = getDevices(network); const subdevs = getDevices(network);
for (var k = 0; k < subdevs.length && subdevs[k]; k++) { for (let s of subdevs) {
span.appendChild(E('img', { span.appendChild(E('img', {
'title': subdevs[k].getI18n(), 'title': s.getI18n(),
'src': L.resource('icons/%s%s.svg'.format(subdevs[k].getType(), subdevs[k].isUp() ? '' : '_disabled')) 'src': L.resource('icons/%s%s.svg'.format(s.getType(), s.isUp() ? '' : '_disabled'))
})); }));
} }
@@ -293,18 +292,18 @@ var CBIZoneForwards = form.DummyValue.extend({
ifaces.push(span); ifaces.push(span);
} }
for (var i = 0; i < devices.length; i++) { for (let d of devices) {
var device = this.devices.filter(function(dev) { return dev.getName() == devices[i] })[0], const device = this.devices.filter(function(dev) { return dev.getName() == d })[0];
title = device ? device.getI18n() : _('Absent Interface'), const title = device ? device.getI18n() : _('Absent Interface');
type = device ? device.getType() : 'ethernet', const type = device ? device.getType() : 'ethernet';
up = device ? device.isUp() : false; const up = device ? device.isUp() : false;
ifaces.push(E('span', { 'class': 'ifacebadge' }, [ ifaces.push(E('span', { 'class': 'ifacebadge' }, [
E('img', { E('img', {
'title': title, 'title': title,
'src': L.resource('icons/%s%s.svg'.format(type, up ? '' : '_disabled')) 'src': L.resource('icons/%s%s.svg'.format(type, up ? '' : '_disabled'))
}), }),
device ? device.getName() : devices[i] device ? device.getName() : d
])); ]));
} }
@@ -323,18 +322,18 @@ var CBIZoneForwards = form.DummyValue.extend({
]); ]);
}, },
renderWidget: function(section_id, option_index, cfgvalue) { renderWidget(section_id, option_index, cfgvalue) {
var value = (cfgvalue != null) ? cfgvalue : this.default, const value = (cfgvalue != null) ? cfgvalue : this.default;
zone = this.zones.filter(function(z) { return z.getName() == value })[0]; const zone = this.zones.filter(function(z) { return z.getName() == value })[0];
if (!zone) if (!zone)
return E([]); return E([]);
var forwards = zone.getForwardingsBy('src'), const forwards = zone.getForwardingsBy('src');
dzones = []; const dzones = [];
for (var i = 0; i < forwards.length; i++) { for (var i = 0; i < forwards.length; i++) {
var dzone = forwards[i].getDestinationZone(); const dzone = forwards[i].getDestinationZone();
if (!dzone) if (!dzone)
continue; continue;
@@ -464,7 +463,7 @@ const CBIIPSelect = form.ListValue.extend({
var CBINetworkSelect = form.ListValue.extend({ var CBINetworkSelect = form.ListValue.extend({
__name__: 'CBI.NetworkSelect', __name__: 'CBI.NetworkSelect',
load: function(section_id) { load(section_id) {
return network.getNetworks().then(L.bind(function(networks) { return network.getNetworks().then(L.bind(function(networks) {
this.networks = networks; this.networks = networks;
@@ -472,18 +471,18 @@ var CBINetworkSelect = form.ListValue.extend({
}, this)); }, this));
}, },
filter: function(section_id, value) { filter(section_id, value) {
return true; return true;
}, },
renderIfaceBadge: function(network) { renderIfaceBadge(network) {
var span = E('span', { 'class': 'ifacebadge' }, network.getName() + ': '), const span = E('span', { 'class': 'ifacebadge' }, network.getName() + ': ');
devices = getDevices(network); const devices = getDevices(network);
for (var j = 0; j < devices.length && devices[j]; j++) { for (let d of devices) {
span.appendChild(E('img', { span.appendChild(E('img', {
'title': devices[j].getI18n(), 'title': d.getI18n(),
'src': L.resource('icons/%s%s.svg'.format(devices[j].getType(), devices[j].isUp() ? '' : '_disabled')) 'src': L.resource('icons/%s%s.svg'.format(d.getType(), d.isUp() ? '' : '_disabled'))
})); }));
} }
@@ -495,10 +494,10 @@ var CBINetworkSelect = form.ListValue.extend({
return span; return span;
}, },
renderWidget: function(section_id, option_index, cfgvalue) { renderWidget(section_id, option_index, cfgvalue) {
var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default), let values = L.toArray((cfgvalue != null) ? cfgvalue : this.default);
choices = {}, const choices = {};
checked = {}; const checked = {};
for (var i = 0; i < values.length; i++) for (var i = 0; i < values.length; i++)
checked[values[i]] = true; checked[values[i]] = true;
@@ -508,9 +507,8 @@ var CBINetworkSelect = form.ListValue.extend({
if (!this.multiple && (this.rmempty || this.optional)) if (!this.multiple && (this.rmempty || this.optional))
choices[''] = E('em', _('unspecified')); choices[''] = E('em', _('unspecified'));
for (var i = 0; i < this.networks.length; i++) { for (let network of this.networks) {
var network = this.networks[i], const name = network.getName();
name = network.getName();
if (name == this.exclude || !this.filter(section_id, name)) if (name == this.exclude || !this.filter(section_id, name))
continue; continue;
@@ -527,7 +525,7 @@ var CBINetworkSelect = form.ListValue.extend({
choices[name] = this.renderIfaceBadge(network); choices[name] = this.renderIfaceBadge(network);
} }
var widget = new ui.Dropdown(this.multiple ? values : values[0], choices, { const widget = new ui.Dropdown(this.multiple ? values : values[0], choices, {
id: this.cbid(section_id), id: this.cbid(section_id),
sort: true, sort: true,
multiple: this.multiple, multiple: this.multiple,
@@ -550,14 +548,13 @@ var CBINetworkSelect = form.ListValue.extend({
return widget.render(); return widget.render();
}, },
textvalue: function(section_id) { textvalue(section_id) {
var cfgvalue = this.cfgvalue(section_id), const cfgvalue = this.cfgvalue(section_id);
values = L.toArray((cfgvalue != null) ? cfgvalue : this.default), const values = L.toArray((cfgvalue != null) ? cfgvalue : this.default);
rv = E([]); const rv = E([]);
for (var i = 0; i < (this.networks || []).length; i++) { for (let network of this.networks) {
var network = this.networks[i], const name = network.getName();
name = network.getName();
if (values.indexOf(name) == -1) if (values.indexOf(name) == -1)
continue; continue;
@@ -578,7 +575,7 @@ var CBINetworkSelect = form.ListValue.extend({
var CBIDeviceSelect = form.ListValue.extend({ var CBIDeviceSelect = form.ListValue.extend({
__name__: 'CBI.DeviceSelect', __name__: 'CBI.DeviceSelect',
load: function(section_id) { load(section_id) {
return Promise.all([ return Promise.all([
network.getDevices(), network.getDevices(),
this.noaliases ? null : network.getNetworks() this.noaliases ? null : network.getNetworks()
@@ -590,11 +587,11 @@ var CBIDeviceSelect = form.ListValue.extend({
}, this)); }, this));
}, },
filter: function(section_id, value) { filter(section_id, value) {
return true; return true;
}, },
renderWidget: function(section_id, option_index, cfgvalue) { renderWidget(section_id, option_index, cfgvalue) {
var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default), var values = L.toArray((cfgvalue != null) ? cfgvalue : this.default),
choices = {}, choices = {},
checked = {}, checked = {},
@@ -608,10 +605,9 @@ var CBIDeviceSelect = form.ListValue.extend({
if (!this.multiple && (this.rmempty || this.optional)) if (!this.multiple && (this.rmempty || this.optional))
choices[''] = E('em', _('unspecified')); choices[''] = E('em', _('unspecified'));
for (var i = 0; i < this.devices.length; i++) { for (let device of this.devices) {
var device = this.devices[i], const name = device.getName();
name = device.getName(), const type = device.getType();
type = device.getType();
if (name == 'lo' || name == this.exclude || !this.filter(section_id, name)) if (name == 'lo' || name == this.exclude || !this.filter(section_id, name))
continue; continue;
@@ -625,7 +621,7 @@ var CBIDeviceSelect = form.ListValue.extend({
if (this.noinactive && device.isUp() == false) if (this.noinactive && device.isUp() == false)
continue; continue;
var item = E([ const item = E([
E('img', { E('img', {
'title': device.getI18n(), 'title': device.getI18n(),
'src': L.resource('icons/%s%s.svg'.format(type, device.isUp() ? '' : '_disabled')) 'src': L.resource('icons/%s%s.svg'.format(type, device.isUp() ? '' : '_disabled'))
@@ -634,7 +630,7 @@ var CBIDeviceSelect = form.ListValue.extend({
E('span', { 'class': 'hide-close'}, [ device.getI18n() ]) E('span', { 'class': 'hide-close'}, [ device.getI18n() ])
]); ]);
var networks = device.getNetworks(); const networks = device.getNetworks();
if (networks.length > 0) if (networks.length > 0)
L.dom.append(item.lastChild, [ ' (', networks.map(function(n) { return n.getName() }).join(', '), ')' ]); L.dom.append(item.lastChild, [ ' (', networks.map(function(n) { return n.getName() }).join(', '), ')' ]);
@@ -647,10 +643,9 @@ var CBIDeviceSelect = form.ListValue.extend({
} }
if (this.networks != null) { if (this.networks != null) {
for (var i = 0; i < this.networks.length; i++) { for (let net of this.networks) {
var net = this.networks[i], const device = network.instantiateDevice('@%s'.format(net.getName()), net);
device = network.instantiateDevice('@%s'.format(net.getName()), net), const name = device.getName();
name = device.getName();
if (name == '@loopback' || name == this.exclude || !this.filter(section_id, name)) if (name == '@loopback' || name == this.exclude || !this.filter(section_id, name))
continue; continue;
@@ -658,7 +653,7 @@ var CBIDeviceSelect = form.ListValue.extend({
if (this.noinactive && net.isUp() == false) if (this.noinactive && net.isUp() == false)
continue; continue;
var item = E([ const item = E([
E('img', { E('img', {
'title': device.getI18n(), 'title': device.getI18n(),
'src': L.resource('icons/alias%s.svg'.format(device.isUp() ? '' : '_disabled')) 'src': L.resource('icons/alias%s.svg'.format(device.isUp() ? '' : '_disabled'))
@@ -690,27 +685,27 @@ var CBIDeviceSelect = form.ListValue.extend({
} }
if (!this.nocreate) { if (!this.nocreate) {
var keys = Object.keys(checked).sort(L.naturalCompare); const keys = Object.keys(checked).sort(L.naturalCompare);
for (var i = 0; i < keys.length; i++) { for (let k of keys) {
if (choices.hasOwnProperty(keys[i])) if (choices.hasOwnProperty(k))
continue; continue;
choices[keys[i]] = E([ choices[k] = E([
E('img', { E('img', {
'title': _('Absent Interface'), 'title': _('Absent Interface'),
'src': L.resource('icons/ethernet_disabled.svg') 'src': L.resource('icons/ethernet_disabled.svg')
}), }),
E('span', { 'class': 'hide-open' }, [ keys[i] ]), E('span', { 'class': 'hide-open' }, [ k ]),
E('span', { 'class': 'hide-close'}, [ '%s: "%h"'.format(_('Absent Interface'), keys[i]) ]) E('span', { 'class': 'hide-close'}, [ '%s: "%h"'.format(_('Absent Interface'), k) ])
]); ]);
values.push(keys[i]); values.push(k);
order.push(keys[i]); order.push(k);
} }
} }
var widget = new ui.Dropdown(this.multiple ? values : values[0], choices, { const widget = new ui.Dropdown(this.multiple ? values : values[0], choices, {
id: this.cbid(section_id), id: this.cbid(section_id),
sort: order, sort: order,
multiple: this.multiple, multiple: this.multiple,
@@ -736,7 +731,7 @@ var CBIDeviceSelect = form.ListValue.extend({
var CBIUserSelect = form.ListValue.extend({ var CBIUserSelect = form.ListValue.extend({
__name__: 'CBI.UserSelect', __name__: 'CBI.UserSelect',
load: function(section_id) { load(section_id) {
return getUsers().then(L.bind(function(users) { return getUsers().then(L.bind(function(users) {
delete this.keylist; delete this.keylist;
delete this.vallist; delete this.vallist;
@@ -748,7 +743,7 @@ var CBIUserSelect = form.ListValue.extend({
}, this)); }, this));
}, },
filter: function(section_id, value) { filter(section_id, value) {
return true; return true;
}, },
}); });
@@ -756,7 +751,7 @@ var CBIUserSelect = form.ListValue.extend({
var CBIGroupSelect = form.ListValue.extend({ var CBIGroupSelect = form.ListValue.extend({
__name__: 'CBI.GroupSelect', __name__: 'CBI.GroupSelect',
load: function(section_id) { load(section_id) {
return getGroups().then(L.bind(function(groups) { return getGroups().then(L.bind(function(groups) {
for (var i = 0; i < groups.length; i++) { for (var i = 0; i < groups.length; i++) {
this.value(groups[i]); this.value(groups[i]);
@@ -766,7 +761,7 @@ var CBIGroupSelect = form.ListValue.extend({
}, this)); }, this));
}, },
filter: function(section_id, value) { filter(section_id, value) {
return true; return true;
}, },
}); });

View File

@@ -905,7 +905,6 @@ return baseclass.extend(/** @lends LuCI.uci.prototype */ {
* have been reloaded by the save operation. * have been reloaded by the save operation.
*/ */
save() { save() {
const v = this.state.values;
const n = this.state.creates; const n = this.state.creates;
const c = this.state.changes; const c = this.state.changes;
const d = this.state.deletes; const d = this.state.deletes;

View File

@@ -20,11 +20,11 @@ var callFileList = rpc.declare({
network.registerPatternVirtual(/^3g-.+$/); network.registerPatternVirtual(/^3g-.+$/);
function write_keepalive(section_id, value) { function write_keepalive(section_id) {
var f_opt = this.map.lookupOption('_keepalive_failure', section_id), const f_opt = this.map.lookupOption('_keepalive_failure', section_id);
i_opt = this.map.lookupOption('_keepalive_interval', section_id), const i_opt = this.map.lookupOption('_keepalive_interval', section_id);
f = parseInt(f_opt?.[0]?.formvalue(section_id), 10), let f = parseInt(f_opt?.[0]?.formvalue(section_id), 10);
i = parseInt(i_opt?.[0]?.formvalue(section_id), 10); let i = parseInt(i_opt?.[0]?.formvalue(section_id), 10);
if (isNaN(i)) if (isNaN(i))
i = 1; i = 1;
@@ -68,18 +68,18 @@ return network.registerProtocol('3g', {
}, },
renderFormOptions: function(s) { renderFormOptions: function(s) {
var o; let o;
o = s.taboption('general', form.Value, '_modem_device', _('Modem device')); o = s.taboption('general', form.Value, '_modem_device', _('Modem device'));
o.ucioption = 'device'; o.ucioption = 'device';
o.rmempty = false; o.rmempty = false;
o.load = function(section_id) { o.load = function(section_id) {
return callFileList('/dev/').then(L.bind(function(devices) { return callFileList('/dev/').then(L.bind(function(devices) {
for (var i = 0; i < devices.length; i++) for (let i = 0; i < devices.length; i++)
this.value(devices[i]); this.value(devices[i]);
return callFileList('/dev/tts/'); return callFileList('/dev/tts/');
}, this)).then(L.bind(function(devices) { }, this)).then(L.bind(function(devices) {
for (var i = 0; i < devices.length; i++) for (let i = 0; i < devices.length; i++)
this.value(devices[i]); this.value(devices[i]);
return form.Value.prototype.load.apply(this, [section_id]); return form.Value.prototype.load.apply(this, [section_id]);
}, this)); }, this));
@@ -133,9 +133,9 @@ return network.registerProtocol('3g', {
o.write = write_keepalive; o.write = write_keepalive;
o.remove = write_keepalive; o.remove = write_keepalive;
o.cfgvalue = function(section_id) { o.cfgvalue = function(section_id) {
var v = uci.get('network', section_id, 'keepalive'); let v = uci.get('network', section_id, 'keepalive');
if (typeof(v) == 'string' && v != '') { if (typeof(v) == 'string' && v != '') {
var m = v.match(/^(\d+)[ ,]\d+$/); const m = v.match(/^(\d+)[ ,]\d+$/);
return m ? m[1] : v; return m ? m[1] : v;
} }
}; };
@@ -146,9 +146,9 @@ return network.registerProtocol('3g', {
o.write = write_keepalive; o.write = write_keepalive;
o.remove = write_keepalive; o.remove = write_keepalive;
o.cfgvalue = function(section_id) { o.cfgvalue = function(section_id) {
var v = uci.get('network', section_id, 'keepalive'); let v = uci.get('network', section_id, 'keepalive');
if (typeof(v) == 'string' && v != '') { if (typeof(v) == 'string' && v != '') {
var m = v.match(/^\d+[ ,](\d+)$/); const m = v.match(/^\d+[ ,](\d+)$/);
return m ? m[1] : v; return m ? m[1] : v;
} }
}; };

View File

@@ -59,36 +59,35 @@ function validateCert(priv, section_id, value) {
} }
return network.registerProtocol('openconnect', { return network.registerProtocol('openconnect', {
getI18n: function() { getI18n() {
return _('OpenConnect'); return _('OpenConnect');
}, },
getIfname: function() { getIfname() {
return this._ubus('l3_device') || 'vpn-%s'.format(this.sid); return this._ubus('l3_device') || 'vpn-%s'.format(this.sid);
}, },
getPackageName: function() { getPackageName() {
return 'openconnect'; return 'openconnect';
}, },
isFloating: function() { isFloating() {
return true; return true;
}, },
isVirtual: function() { isVirtual() {
return true; return true;
}, },
getDevices: function() { getDevices() {
return null; return null;
}, },
containsDevice: function(ifname) { containsDevice(ifname) {
return (network.getIfnameOf(ifname) == this.getIfname()); return (network.getIfnameOf(ifname) == this.getIfname());
}, },
renderFormOptions: function(s) { renderFormOptions(s) {
const dev = this.getDevice().getName();
let certLoadPromise = null; let certLoadPromise = null;
let o; let o;
@@ -104,7 +103,7 @@ return network.registerProtocol('openconnect', {
o = s.taboption('general', form.Value, 'uri', _('VPN Server')); o = s.taboption('general', form.Value, 'uri', _('VPN Server'));
o.placeholder = 'https://example.com:443/usergroup'; o.placeholder = 'https://example.com:443/usergroup';
o.validate = function(section_id, value) { o.validate = function(section_id, value) {
const m = String(value).match(/^(?:(\w+):\/\/|)(?:\[([0-9a-f:.]{2,45})\]|([^\/:]+))(?::([0-9]{1,5}))?(?:\/.*)?$/i); const m = String(value).match(/^(?:(\w+):\/\/|)(?:\[([0-9a-f:.]{2,45})\]|([^/:]+))(?::([0-9]{1,5}))?(?:\/.*)?$/i);
if (!m) if (!m)
return _('Invalid server URL'); return _('Invalid server URL');

View File

@@ -4,14 +4,14 @@
'require network'; 'require network';
'require tools.widgets as widgets'; 'require tools.widgets as widgets';
var callGetCertificateFiles = rpc.declare({ const callGetCertificateFiles = rpc.declare({
object: 'luci.openfortivpn', object: 'luci.openfortivpn',
method: 'getCertificates', method: 'getCertificates',
params: [ 'interface' ], params: [ 'interface' ],
expect: { '': {} } expect: { '': {} }
}); });
var callSetCertificateFiles = rpc.declare({ const callSetCertificateFiles = rpc.declare({
object: 'luci.openfortivpn', object: 'luci.openfortivpn',
method: 'setCertificates', method: 'setCertificates',
params: [ 'interface', 'user_cert', 'user_key', 'ca_file' ], params: [ 'interface', 'user_cert', 'user_key', 'ca_file' ],
@@ -38,9 +38,9 @@ function sanitizeCert(s) {
} }
function validateCert(priv, section_id, value) { function validateCert(priv, section_id, value) {
var lines = value.trim().split(/[\r\n]/), const lines = value.trim().split(/[\r\n]/);
start = false, let start = false;
i; let i;
if (value === null || value === '') if (value === null || value === '')
return true; return true;
@@ -48,7 +48,7 @@ function validateCert(priv, section_id, value) {
for (i = 0; i < lines.length; i++) { for (i = 0; i < lines.length; i++) {
if (lines[i].match(/^-{5}BEGIN ((|RSA |DSA )PRIVATE KEY|(|TRUSTED |X509 )CERTIFICATE)-{5}$/)) if (lines[i].match(/^-{5}BEGIN ((|RSA |DSA )PRIVATE KEY|(|TRUSTED |X509 )CERTIFICATE)-{5}$/))
start = true; start = true;
else if (start && !lines[i].match(/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/)) else if (start && !lines[i].match(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/))
break; break;
} }
@@ -59,36 +59,36 @@ function validateCert(priv, section_id, value) {
} }
return network.registerProtocol('openfortivpn', { return network.registerProtocol('openfortivpn', {
getI18n: function() { getI18n() {
return _('OpenFortivpn'); return _('OpenFortivpn');
}, },
getIfname: function() { getIfname() {
return this._ubus('l3_device') || 'vpn-%s'.format(this.sid); return this._ubus('l3_device') || 'vpn-%s'.format(this.sid);
}, },
getPackageName: function() { getPackageName() {
return 'openfortivpn'; return 'openfortivpn';
}, },
isFloating: function() { isFloating() {
return true; return true;
}, },
isVirtual: function() { isVirtual() {
return true; return true;
}, },
getDevices: function() { getDevices() {
return null; return null;
}, },
containsDevice: function(ifname) { containsDevice(ifname) {
return (network.getIfnameOf(ifname) == this.getIfname()); return (network.getIfnameOf(ifname) == this.getIfname());
}, },
renderFormOptions: function(s) { renderFormOptions(s) {
var o; let o, certLoadPromise;
o = s.taboption('general', form.Value, 'peeraddr', _('VPN Server')); o = s.taboption('general', form.Value, 'peeraddr', _('VPN Server'));
o.datatype = 'host(0)'; o.datatype = 'host(0)';
@@ -108,7 +108,7 @@ return network.registerProtocol('openfortivpn', {
o.monospace = true; o.monospace = true;
o.validate = L.bind(validateCert, o, false); o.validate = L.bind(validateCert, o, false);
o.load = function(section_id) { o.load = function(section_id) {
var certLoadPromise = certLoadPromise || callGetCertificateFiles(section_id); certLoadPromise = certLoadPromise || callGetCertificateFiles(section_id);
return certLoadPromise.then(function(certs) { return certs.user_cert }); return certLoadPromise.then(function(certs) { return certs.user_cert });
}; };
o.write = function(section_id, value) { o.write = function(section_id, value) {
@@ -121,7 +121,7 @@ return network.registerProtocol('openfortivpn', {
o.monospace = true; o.monospace = true;
o.validate = L.bind(validateCert, o, true); o.validate = L.bind(validateCert, o, true);
o.load = function(section_id) { o.load = function(section_id) {
var certLoadPromise = certLoadPromise || callGetCertificateFiles(section_id); certLoadPromise = certLoadPromise || callGetCertificateFiles(section_id);
return certLoadPromise.then(function(certs) { return certs.user_key }); return certLoadPromise.then(function(certs) { return certs.user_key });
}; };
o.write = function(section_id, value) { o.write = function(section_id, value) {
@@ -134,7 +134,7 @@ return network.registerProtocol('openfortivpn', {
o.monospace = true; o.monospace = true;
o.validate = L.bind(validateCert, o, false); o.validate = L.bind(validateCert, o, false);
o.load = function(section_id) { o.load = function(section_id) {
var certLoadPromise = certLoadPromise || callGetCertificateFiles(section_id); certLoadPromise = certLoadPromise || callGetCertificateFiles(section_id);
return certLoadPromise.then(function(certs) { return certs.ca_file }); return certLoadPromise.then(function(certs) { return certs.ca_file });
}; };
o.write = function(section_id, value) { o.write = function(section_id, value) {

View File

@@ -7,17 +7,17 @@
network.registerPatternVirtual(/^relay-.+$/); network.registerPatternVirtual(/^relay-.+$/);
var RelayDevicePrototype = { var RelayDevicePrototype = {
__init__: function(ifname, network) { __init__(ifname, network) {
this.ifname = ifname; this.ifname = ifname;
this.network = network; this.network = network;
}, },
_aggregateDevices: function(fn, first) { _aggregateDevices(fn, first) {
var devices = this.network ? this.network.getDevices() : [], const devices = this.network ? this.network.getDevices() : [];
rv = 0; let rv = 0;
for (var i = 0; i < devices.length; i++) { for (let d of devices) {
var v = devices[i][fn].apply(devices[i]); var v = d[fn].apply(d);
if (v != null) { if (v != null) {
if (first) if (first)
@@ -30,88 +30,88 @@ var RelayDevicePrototype = {
return first ? null : [ rv, devices.length ]; return first ? null : [ rv, devices.length ];
}, },
getPorts: function() { return this.network ? this.network.getDevices() : [] }, getPorts() { return this.network ? this.network.getDevices() : [] },
getType: function() { return 'tunnel' }, getType() { return 'tunnel' },
getTypeI18n: function() { return _('Relay Bridge') }, getTypeI18n() { return _('Relay Bridge') },
getShortName: function() { getShortName() {
return '%s "%h"'.format(_('Relay'), this.ifname); return '%s "%h"'.format(_('Relay'), this.ifname);
}, },
isUp: function() { isUp() {
var res = this._aggregateDevices('isUp'); var res = this._aggregateDevices('isUp');
return (res[1] > 0 && res[0] == res[1]); return (res[1] > 0 && res[0] == res[1]);
}, },
getTXBytes: function() { return this._aggregateDevices('getTXBytes')[0] }, getTXBytes() { return this._aggregateDevices('getTXBytes')[0] },
getRXBytes: function() { return this._aggregateDevices('getRXBytes')[0] }, getRXBytes() { return this._aggregateDevices('getRXBytes')[0] },
getTXPackets: function() { return this._aggregateDevices('getTXPackets')[0] }, getTXPackets() { return this._aggregateDevices('getTXPackets')[0] },
getRXPackets: function() { return this._aggregateDevices('getRXPackets')[0] }, getRXPackets() { return this._aggregateDevices('getRXPackets')[0] },
getMAC: function() { return this._aggregateDevices('getMAC', true) }, getMAC() { return this._aggregateDevices('getMAC', true) },
getIPAddrs: function() { getIPAddrs() {
var ipaddr = this.network ? L.toArray(uci.get('network', this.network.getName(), 'ipaddr'))[0] : null; var ipaddr = this.network ? L.toArray(uci.get('network', this.network.getName(), 'ipaddr'))[0] : null;
return (ipaddr != null ? [ ipaddr ] : []); return (ipaddr != null ? [ ipaddr ] : []);
}, },
getIP6Addrs: function() { return [] } getIP6Addrs() { return [] }
}; };
return network.registerProtocol('relay', { return network.registerProtocol('relay', {
getI18n: function() { getI18n() {
return _('Relay bridge'); return _('Relay bridge');
}, },
getIfname: function() { getIfname() {
return 'relay-%s'.format(this.sid); return 'relay-%s'.format(this.sid);
}, },
getPackageName: function() { getPackageName() {
return 'relayd'; return 'relayd';
}, },
isFloating: function() { isFloating() {
return true; return true;
}, },
isVirtual: function() { isVirtual() {
return true; return true;
}, },
containsDevice: function(ifname) { containsDevice(ifname) {
return (network.getIfnameOf(ifname) == this.getIfname()); return (network.getIfnameOf(ifname) == this.getIfname());
}, },
isUp: function() { isUp() {
var dev = this.getDevice(); var dev = this.getDevice();
return (dev ? dev.isUp() : false); return (dev ? dev.isUp() : false);
}, },
getDevice: function() { getDevice() {
return network.instantiateDevice(this.sid, this, RelayDevicePrototype); return network.instantiateDevice(this.sid, this, RelayDevicePrototype);
}, },
getDevices: function() { getDevices() {
if (this.devices) if (this.devices)
return this.devices; return this.devices;
var networkNames = L.toArray(uci.get('network', this.sid, 'network')), const networkNames = L.toArray(uci.get('network', this.sid, 'network'));
deviceNames = L.toArray(uci.get('network', this.sid, 'ifname')), let deviceNames = L.toArray(uci.get('network', this.sid, 'ifname'));
devices = {}, const devices = {};
rv = []; const rv = [];
for (var i = 0; i < networkNames.length; i++) { for (let nn of networkNames) {
var net = network.instantiateNetwork(networkNames[i]), var net = network.instantiateNetwork(nn),
dev = net ? net.getDevice() : null; dev = net ? net.getDevice() : null;
if (dev) if (dev)
devices[dev.getName()] = dev; devices[dev.getName()] = dev;
} }
for (var i = 0; i < deviceNames.length; i++) { for (let dn of deviceNames) {
var dev = network.getDevice(deviceNames[i]); const dev = network.getDevice(dn);
if (dev) if (dev)
devices[dev.getName()] = dev; devices[dev.getName()] = dev;
@@ -120,20 +120,20 @@ return network.registerProtocol('relay', {
deviceNames = Object.keys(devices); deviceNames = Object.keys(devices);
deviceNames.sort(); deviceNames.sort();
for (var i = 0; i < deviceNames.length; i++) for (let dn of deviceNames)
rv.push(devices[deviceNames[i]]); rv.push(devices[dn]);
this.devices = rv; this.devices = rv;
return rv; return rv;
}, },
getUptime: function() { getUptime() {
var networkNames = L.toArray(uci.get('network', this.sid, 'network')), const networkNames = L.toArray(uci.get('network', this.sid, 'network'));
uptime = 0; let uptime = 0;
for (var i = 0; i < networkNames.length; i++) { for (let nn of networkNames) {
var net = network.instantiateNetwork(networkNames[i]); const net = network.instantiateNetwork(nn);
if (net) if (net)
uptime = Math.max(uptime, net.getUptime()); uptime = Math.max(uptime, net.getUptime());
} }
@@ -141,11 +141,11 @@ return network.registerProtocol('relay', {
return uptime; return uptime;
}, },
getErrors: function() { getErrors() {
return null; return null;
}, },
renderFormOptions: function(s) { renderFormOptions(s) {
var o; var o;
o = s.taboption('general', form.Value, 'ipaddr', _('Local IPv4 address'), _('Address to access local relay bridge')); o = s.taboption('general', form.Value, 'ipaddr', _('Local IPv4 address'), _('Address to access local relay bridge'));

View File

@@ -34,7 +34,7 @@ function validateBase64(section_id, value) {
if (value.length == 0) if (value.length == 0)
return true; return true;
if (value.length != 44 || !value.match(/^(?:[A-Za-z0-9+\/]{4})*(?:[A-Za-z0-9+\/]{2}==|[A-Za-z0-9+\/]{3}=)?$/)) if (value.length != 44 || !value.match(/^(?:[A-Za-z0-9+/]{4})*(?:[A-Za-z0-9+/]{2}==|[A-Za-z0-9+/]{3}=)?$/))
return _('Invalid Base64 key string'); return _('Invalid Base64 key string');
if (value[43] != "=" ) if (value[43] != "=" )
@@ -45,26 +45,17 @@ function validateBase64(section_id, value) {
var stubValidator = { var stubValidator = {
factory: validation, factory: validation,
apply: function(type, value, args) { apply(type, value, args) {
if (value != null) if (value != null)
this.value = value; this.value = value;
return validation.types[type].apply(this, args); return validation.types[type].apply(this, args);
}, },
assert: function(condition) { assert(condition) {
return !!condition; return !!condition;
} }
}; };
function generateDescription(name, texts) {
return E('li', { 'style': 'color: inherit;' }, [
E('span', name),
E('ul', texts.map(function (text) {
return E('li', { 'style': 'color: inherit;' }, text);
}))
]);
}
function buildSVGQRCode(data, code) { function buildSVGQRCode(data, code) {
// pixel size larger than 4 clips right and bottom edges of complex configs // pixel size larger than 4 clips right and bottom edges of complex configs
const options = { const options = {
@@ -78,7 +69,7 @@ function buildSVGQRCode(data, code) {
} }
var cbiKeyPairGenerate = form.DummyValue.extend({ var cbiKeyPairGenerate = form.DummyValue.extend({
cfgvalue: function(section_id, value) { cfgvalue(section_id, value) {
return E('button', { return E('button', {
'class': 'btn', 'class': 'btn',
'click': ui.createHandlerFn(this, function(section_id, ev) { 'click': ui.createHandlerFn(this, function(section_id, ev) {
@@ -101,36 +92,36 @@ function handleWindowDragDropIgnore(ev) {
} }
return network.registerProtocol('wireguard', { return network.registerProtocol('wireguard', {
getI18n: function() { getI18n() {
return _('WireGuard VPN'); return _('WireGuard VPN');
}, },
getIfname: function() { getIfname() {
return this._ubus('l3_device') || this.sid; return this._ubus('l3_device') || this.sid;
}, },
getPackageName: function() { getPackageName() {
return 'wireguard-tools'; return 'wireguard-tools';
}, },
isFloating: function() { isFloating() {
return true; return true;
}, },
isVirtual: function() { isVirtual() {
return true; return true;
}, },
getDevices: function() { getDevices() {
return null; return null;
}, },
containsDevice: function(ifname) { containsDevice(ifname) {
return (network.getIfnameOf(ifname) == this.getIfname()); return (network.getIfnameOf(ifname) == this.getIfname());
}, },
renderFormOptions: function(s) { renderFormOptions(s) {
var o, ss, ss2; var o, ss;
// -- general --------------------------------------------------------------------- // -- general ---------------------------------------------------------------------
@@ -139,8 +130,6 @@ return network.registerProtocol('wireguard', {
o.validate = validateBase64; o.validate = validateBase64;
o.rmempty = false; o.rmempty = false;
var serverName = this.getIfname();
o = s.taboption('general', form.Value, 'public_key', _('Public Key'), _('Base64-encoded public key of this interface for sharing.')); o = s.taboption('general', form.Value, 'public_key', _('Public Key'), _('Base64-encoded public key of this interface for sharing.'));
o.rmempty = false; o.rmempty = false;
o.write = function() {/* write nothing */}; o.write = function() {/* write nothing */};
@@ -268,16 +257,16 @@ return network.registerProtocol('wireguard', {
if (config.interface_address) { if (config.interface_address) {
config.interface_address = config.interface_address.split(/[, ]+/); config.interface_address = config.interface_address.split(/[, ]+/);
for (var i = 0; i < config.interface_address.length; i++) for (let cfia of config.interface_address)
if (!stubValidator.apply('ipaddr', config.interface_address[i])) if (!stubValidator.apply('ipaddr', cfia))
return _('Address setting is invalid'); return _('Address setting is invalid');
} }
if (config.interface_dns) { if (config.interface_dns) {
config.interface_dns = config.interface_dns.split(/[, ]+/); config.interface_dns = config.interface_dns.split(/[, ]+/);
for (var i = 0; i < config.interface_dns.length; i++) for (let cfid of config.interface_dns)
if (!stubValidator.apply('ipaddr', config.interface_dns[i], ['nomask'])) if (!stubValidator.apply('ipaddr', cfid, ['nomask']))
return _('DNS setting is invalid'); return _('DNS setting is invalid');
} }
@@ -287,9 +276,7 @@ return network.registerProtocol('wireguard', {
if (!stubValidator.apply('port', config.interface_listenport || '0')) if (!stubValidator.apply('port', config.interface_listenport || '0'))
return _('ListenPort setting is invalid'); return _('ListenPort setting is invalid');
for (var i = 0; i < config.peers.length; i++) { for (let pconf of config.peers) {
var pconf = config.peers[i];
if (pconf.peer_publickey != null && validateBase64(null, pconf.peer_publickey) !== true) if (pconf.peer_publickey != null && validateBase64(null, pconf.peer_publickey) !== true)
return _('PublicKey setting is invalid'); return _('PublicKey setting is invalid');
@@ -327,10 +314,10 @@ return network.registerProtocol('wireguard', {
}; };
ss.handleApplyConfig = function(mode, nodes, comment, ev) { ss.handleApplyConfig = function(mode, nodes, comment, ev) {
var input = nodes.querySelector('textarea').value, const input = nodes.querySelector('textarea').value;
error = nodes.querySelector('.alert-message'), const error = nodes.querySelector('.alert-message');
cancel = nodes.nextElementSibling.querySelector('.btn'), const cancel = nodes.nextElementSibling.querySelector('.btn');
config = this.parseConfig(input); const config = this.parseConfig(input);
if (typeof(config) == 'string') { if (typeof(config) == 'string') {
error.firstChild.data = _('Cannot parse configuration: %s').format(config); error.firstChild.data = _('Cannot parse configuration: %s').format(config);
@@ -339,7 +326,7 @@ return network.registerProtocol('wireguard', {
} }
if (mode == 'full') { if (mode == 'full') {
var prv = s.formvalue(s.section, 'private_key'); const prv = s.formvalue(s.section, 'private_key');
if (prv && prv != config.interface_privatekey && !confirm(_('Overwrite the current settings with the imported configuration?'))) if (prv && prv != config.interface_privatekey && !confirm(_('Overwrite the current settings with the imported configuration?')))
return; return;
@@ -353,9 +340,8 @@ return network.registerProtocol('wireguard', {
if (config.interface_dns) if (config.interface_dns)
s.getOption('dns').getUIElement(s.section).setValue(config.interface_dns); s.getOption('dns').getUIElement(s.section).setValue(config.interface_dns);
for (var i = 0; i < config.peers.length; i++) { for (let pconf of config.peers) {
var pconf = config.peers[i]; const sid = uci.add('network', 'wireguard_' + s.section);
var sid = uci.add('network', 'wireguard_' + s.section);
uci.sections('network', 'wireguard_' + s.section, function(peer) { uci.sections('network', 'wireguard_' + s.section, function(peer) {
if (peer.public_key == pconf.peer_publickey) if (peer.public_key == pconf.peer_publickey)
@@ -381,8 +367,8 @@ return network.registerProtocol('wireguard', {
} }
else { else {
return getPublicAndPrivateKeyFromPrivate(config.interface_privatekey).then(function(keypair) { return getPublicAndPrivateKeyFromPrivate(config.interface_privatekey).then(function(keypair) {
var sid = uci.add('network', 'wireguard_' + s.section); const sid = uci.add('network', 'wireguard_' + s.section);
var pub = s.formvalue(s.section, 'public_key'); const pub = s.formvalue(s.section, 'public_key');
uci.sections('network', 'wireguard_' + s.section, function(peer) { uci.sections('network', 'wireguard_' + s.section, function(peer) {
if (peer.public_key == keypair.pub) if (peer.public_key == keypair.pub)
@@ -393,9 +379,7 @@ return network.registerProtocol('wireguard', {
uci.set('network', sid, 'public_key', keypair.pub); uci.set('network', sid, 'public_key', keypair.pub);
uci.set('network', sid, 'private_key', keypair.priv); uci.set('network', sid, 'private_key', keypair.priv);
for (var i = 0; i < config.peers.length; i++) { for (let pconf of config.peers) {
var pconf = config.peers[i];
if (pconf.peer_publickey == pub) { if (pconf.peer_publickey == pub) {
uci.set('network', sid, 'preshared_key', pconf.peer_presharedkey); uci.set('network', sid, 'preshared_key', pconf.peer_presharedkey);
uci.set('network', sid, 'allowed_ips', pconf.peer_allowedips); uci.set('network', sid, 'allowed_ips', pconf.peer_allowedips);
@@ -412,11 +396,10 @@ return network.registerProtocol('wireguard', {
}; };
ss.handleConfigImport = function(mode) { ss.handleConfigImport = function(mode) {
var mapNode = ss.getActiveModalMap(), const mapNode = ss.getActiveModalMap();
headNode = mapNode.parentNode.querySelector('h4'), const headNode = mapNode.parentNode.querySelector('h4');
parent = this.map;
var nodes = E('div', { const nodes = E('div', {
'dragover': this.handleDragConfig, 'dragover': this.handleDragConfig,
'drop': this.handleDropConfig.bind(this, mode) 'drop': this.handleDropConfig.bind(this, mode)
}, [ }, [
@@ -440,7 +423,7 @@ return network.registerProtocol('wireguard', {
}, ['']) }, [''])
]); ]);
var cancelFn = function() { const cancelFn = function() {
nodes.parentNode.removeChild(nodes.nextSibling); nodes.parentNode.removeChild(nodes.nextSibling);
nodes.parentNode.removeChild(nodes); nodes.parentNode.removeChild(nodes);
mapNode.classList.remove('hidden'); mapNode.classList.remove('hidden');
@@ -450,7 +433,7 @@ return network.registerProtocol('wireguard', {
window.removeEventListener('drop', handleWindowDragDropIgnore); window.removeEventListener('drop', handleWindowDragDropIgnore);
}; };
var a = nodes.querySelector('a.full-import'); const a = nodes.querySelector('a.full-import');
if (a) { if (a) {
a.addEventListener('click', ui.createHandlerFn(this, function(mode) { a.addEventListener('click', ui.createHandlerFn(this, function(mode) {
@@ -485,7 +468,7 @@ return network.registerProtocol('wireguard', {
}; };
ss.renderSectionAdd = function(/* ... */) { ss.renderSectionAdd = function(/* ... */) {
var nodes = this.super('renderSectionAdd', arguments); const nodes = this.super('renderSectionAdd', arguments);
nodes.appendChild(E('button', { nodes.appendChild(E('button', {
'class': 'btn', 'class': 'btn',
@@ -510,14 +493,14 @@ return network.registerProtocol('wireguard', {
o.optional = true; o.optional = true;
o.width = '30%'; o.width = '30%';
o.textvalue = function(section_id) { o.textvalue = function(section_id) {
var dis = ss.getOption('disabled'), const dis = ss.getOption('disabled');
pub = ss.getOption('public_key'), const pub = ss.getOption('public_key');
prv = ss.getOption('private_key'), const prv = ss.getOption('private_key');
psk = ss.getOption('preshared_key'), const psk = ss.getOption('preshared_key');
name = this.cfgvalue(section_id), const name = this.cfgvalue(section_id);
key = pub.cfgvalue(section_id); const key = pub.cfgvalue(section_id);
var desc = [ const desc = [
E('p', [ E('p', [
name ? E('span', [ name ]) : E('em', [ _('Untitled peer') ]) name ? E('span', [ name ]) : E('em', [ _('Untitled peer') ])
]) ])
@@ -566,8 +549,8 @@ return network.registerProtocol('wireguard', {
}; };
function handleKeyChange(ev, section_id, value) { function handleKeyChange(ev, section_id, value) {
var prv = this.section.getUIElement(section_id, 'private_key'), const prv = this.section.getUIElement(section_id, 'private_key');
btn = this.map.findElement('.btn.qr-code'); const btn = this.map.findElement('.btn.qr-code');
btn.disabled = (!prv.isValid() || !prv.getValue()); btn.disabled = (!prv.isValid() || !prv.getValue());
} }
@@ -611,8 +594,8 @@ return network.registerProtocol('wireguard', {
o = ss.option(form.DynamicList, 'allowed_ips', _('Allowed IPs'), _("Optional. IP addresses and prefixes that this peer is allowed to use inside the tunnel. Usually the peer's tunnel IP addresses and the networks the peer routes through the tunnel.")); o = ss.option(form.DynamicList, 'allowed_ips', _('Allowed IPs'), _("Optional. IP addresses and prefixes that this peer is allowed to use inside the tunnel. Usually the peer's tunnel IP addresses and the networks the peer routes through the tunnel."));
o.datatype = 'ipaddr'; o.datatype = 'ipaddr';
o.textvalue = function(section_id) { o.textvalue = function(section_id) {
var ips = L.toArray(this.cfgvalue(section_id)), const ips = L.toArray(this.cfgvalue(section_id));
list = []; const list = [];
for (var i = 0; i < ips.length; i++) { for (var i = 0; i < ips.length; i++) {
if (i > 7) { if (i > 7) {
@@ -650,8 +633,8 @@ return network.registerProtocol('wireguard', {
o.placeholder = 'vpn.example.com'; o.placeholder = 'vpn.example.com';
o.datatype = 'host'; o.datatype = 'host';
o.textvalue = function(section_id) { o.textvalue = function(section_id) {
var host = this.cfgvalue(section_id), const host = this.cfgvalue(section_id);
port = this.section.cfgvalue(section_id, 'endpoint_port'); const port = this.section.cfgvalue(section_id, 'endpoint_port');
return (host && port) return (host && port)
? '%h:%d'.format(host, port) ? '%h:%d'.format(host, port)
@@ -680,12 +663,12 @@ return network.registerProtocol('wireguard', {
o.modalonly = true; o.modalonly = true;
o.createPeerConfig = function(section_id, endpoint, ips, eips, dns) { o.createPeerConfig = function(section_id, endpoint, ips, eips, dns) {
var pub = s.formvalue(s.section, 'public_key'), const pub = s.formvalue(s.section, 'public_key');
port = s.formvalue(s.section, 'listen_port') || '51820', const port = s.formvalue(s.section, 'listen_port') || '51820';
prv = this.section.formvalue(section_id, 'private_key'), const prv = this.section.formvalue(section_id, 'private_key');
psk = this.section.formvalue(section_id, 'preshared_key'), const psk = this.section.formvalue(section_id, 'preshared_key');
eport = this.section.formvalue(section_id, 'endpoint_port'), const eport = this.section.formvalue(section_id, 'endpoint_port');
keep = this.section.formvalue(section_id, 'persistent_keepalive'); const keep = this.section.formvalue(section_id, 'persistent_keepalive');
// If endpoint is IPv6 we must escape it with [] // If endpoint is IPv6 we must escape it with []
if (endpoint.indexOf(':') > 0) { if (endpoint.indexOf(':') > 0) {
@@ -709,11 +692,11 @@ return network.registerProtocol('wireguard', {
}; };
o.handleGenerateQR = function(section_id, ev) { o.handleGenerateQR = function(section_id, ev) {
var mapNode = ss.getActiveModalMap(), const mapNode = ss.getActiveModalMap();
headNode = mapNode.parentNode.querySelector('h4'), const headNode = mapNode.parentNode.querySelector('h4');
configGenerator = this.createPeerConfig.bind(this, section_id), const configGenerator = this.createPeerConfig.bind(this, section_id);
parent = this.map, const parent = this.map;
eips = this.section.formvalue(section_id, 'allowed_ips'); const eips = this.section.formvalue(section_id, 'allowed_ips');
return Promise.all([ return Promise.all([
network.getWANNetworks(), network.getWANNetworks(),
@@ -722,8 +705,8 @@ return network.registerProtocol('wireguard', {
L.resolveDefault(uci.load('ddns')), L.resolveDefault(uci.load('ddns')),
L.resolveDefault(uci.load('system')), L.resolveDefault(uci.load('system')),
parent.save(null, true) parent.save(null, true)
]).then(function(data) { ]).then(function([wNets, w6Nets, lnet]) {
var hostnames = []; const hostnames = [];
uci.sections('ddns', 'service', function(s) { uci.sections('ddns', 'service', function(s) {
if (typeof(s?.lookup_host) == 'string' && s?.enabled == '1') if (typeof(s?.lookup_host) == 'string' && s?.enabled == '1')
@@ -735,25 +718,24 @@ return network.registerProtocol('wireguard', {
hostnames.push(s.hostname); hostnames.push(s.hostname);
}); });
for (var i = 0; i < data[0].length; i++) for (let wNet of wNets)
hostnames.push.apply(hostnames, data[0][i].getIPAddrs().map(function(ip) { return ip.split('/')[0] })); hostnames.push.apply(hostnames, wNet.getIPAddrs().map(function(ip) { return ip.split('/')[0] }));
for (var i = 0; i < data[1].length; i++) for (let w6Net of w6Nets)
hostnames.push.apply(hostnames, data[1][i].getIP6Addrs().map(function(ip) { return ip.split('/')[0] })); hostnames.push.apply(hostnames, w6Net.getIP6Addrs().map(function(ip) { return ip.split('/')[0] }));
var ips = [ '0.0.0.0/0', '::/0' ]; const ips = [ '0.0.0.0/0', '::/0' ];
var dns = []; const dns = [];
var lan = data[2]; if (lnet) {
if (lan) { const lanIp = lnet.getIPAddr();
var lanIp = lan.getIPAddr();
if (lanIp) { if (lanIp) {
dns.unshift(lanIp) dns.unshift(lanIp)
} }
} }
var qrm, qrs, qro; let qrm, qrs, qro;
qrm = new form.JSONMap({ config: { endpoint: hostnames[0], allowed_ips: ips, addresses: eips, dns_servers: dns } }, null, _('The generated configuration can be imported into a WireGuard client application to set up a connection towards this device.')); qrm = new form.JSONMap({ config: { endpoint: hostnames[0], allowed_ips: ips, addresses: eips, dns_servers: dns } }, null, _('The generated configuration can be imported into a WireGuard client application to set up a connection towards this device.'));
qrm.parent = parent; qrm.parent = parent;
@@ -761,12 +743,12 @@ return network.registerProtocol('wireguard', {
qrs = qrm.section(form.NamedSection, 'config'); qrs = qrm.section(form.NamedSection, 'config');
function handleConfigChange(ev, section_id, value) { function handleConfigChange(ev, section_id, value) {
var code = this.map.findElement('.qr-code'), const code = this.map.findElement('.qr-code');
conf = this.map.findElement('.client-config'), const conf = this.map.findElement('.client-config');
endpoint = this.section.getUIElement(section_id, 'endpoint'), const endpoint = this.section.getUIElement(section_id, 'endpoint');
ips = this.section.getUIElement(section_id, 'allowed_ips'); const ips = this.section.getUIElement(section_id, 'allowed_ips');
eips = this.section.getUIElement(section_id, 'addresses'); const eips = this.section.getUIElement(section_id, 'addresses');
dns = this.section.getUIElement(section_id, 'dns_servers'); const dns = this.section.getUIElement(section_id, 'dns_servers');
if (this.isValid(section_id)) { if (this.isValid(section_id)) {
conf.firstChild.data = configGenerator(endpoint.getValue(), ips.getValue(), eips.getValue(), dns.getValue()); conf.firstChild.data = configGenerator(endpoint.getValue(), ips.getValue(), eips.getValue(), dns.getValue());
@@ -800,9 +782,9 @@ return network.registerProtocol('wireguard', {
qro = qrs.option(form.DummyValue, 'output'); qro = qrs.option(form.DummyValue, 'output');
qro.renderWidget = function() { qro.renderWidget = function() {
var peer_config = configGenerator(hostnames[0], ips, eips, dns); const peer_config = configGenerator(hostnames[0], ips, eips, dns);
var node = E('div', { const node = E('div', {
'style': 'display:flex;flex-wrap:wrap;align-items:center;gap:.5em;width:100%' 'style': 'display:flex;flex-wrap:wrap;align-items:center;gap:.5em;width:100%'
}, [ }, [
E('div', { E('div', {
@@ -814,9 +796,9 @@ return network.registerProtocol('wireguard', {
E('pre', { E('pre', {
'class': 'client-config', 'class': 'client-config',
'style': 'flex:1;white-space:pre;overflow:auto', 'style': 'flex:1;white-space:pre;overflow:auto',
'click': function(ev) { 'click'(ev) {
var sel = window.getSelection(), const sel = window.getSelection();
range = document.createRange(); const range = document.createRange();
range.selectNodeContents(ev.currentTarget); range.selectNodeContents(ev.currentTarget);
@@ -849,7 +831,7 @@ return network.registerProtocol('wireguard', {
}, [ }, [
E('button', { E('button', {
'class': 'btn', 'class': 'btn',
'click': function() { 'click'() {
// Remove QR code button (row) // Remove QR code button (row)
nodes.parentNode.removeChild(nodes.nextSibling); nodes.parentNode.removeChild(nodes.nextSibling);
// Remove QR code form // Remove QR code form
@@ -891,7 +873,7 @@ return network.registerProtocol('wireguard', {
}; };
}, },
deleteConfiguration: function() { deleteConfiguration() {
uci.sections('network', 'wireguard_%s'.format(this.sid), function(s) { uci.sections('network', 'wireguard_%s'.format(this.sid), function(s) {
uci.remove('network', s['.name']); uci.remove('network', s['.name']);
}); });