summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-base/htdocs/luci-static/resources
diff options
context:
space:
mode:
Diffstat (limited to 'modules/luci-base/htdocs/luci-static/resources')
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/firewall.js9
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/form.js33
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/luci.js7
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/network.js196
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js25
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/protocol/static.js40
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/uci.js24
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/ui.js15
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/validation.js35
9 files changed, 234 insertions, 150 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/firewall.js b/modules/luci-base/htdocs/luci-static/resources/firewall.js
index 2e032ca2d5..4fa4954ba6 100644
--- a/modules/luci-base/htdocs/luci-static/resources/firewall.js
+++ b/modules/luci-base/htdocs/luci-static/resources/firewall.js
@@ -92,7 +92,6 @@ Firewall = L.Class.extend({
z = uci.add('firewall', 'zone');
uci.set('firewall', z, 'name', name);
- uci.set('firewall', z, 'network', ' ');
uci.set('firewall', z, 'input', d.getInput() || 'DROP');
uci.set('firewall', z, 'output', d.getOutput() || 'DROP');
uci.set('firewall', z, 'forward', d.getForward() || 'DROP');
@@ -333,17 +332,17 @@ Zone = AbstractFirewallItem.extend({
return false;
newNetworks.push(network);
- this.set('network', newNetworks.join(' '));
+ this.set('network', newNetworks);
return true;
},
deleteNetwork: function(network) {
var oldNetworks = this.getNetworks(),
- newNetworks = oldNetworks.filter(function(net) { return net != network });
+ newNetworks = oldNetworks.filter(function(net) { return net != network });
if (newNetworks.length > 0)
- this.set('network', newNetworks.join(' '));
+ this.set('network', newNetworks);
else
this.set('network', null);
@@ -355,7 +354,7 @@ Zone = AbstractFirewallItem.extend({
},
clearNetworks: function() {
- this.set('network', ' ');
+ this.set('network', null);
},
getDevices: function() {
diff --git a/modules/luci-base/htdocs/luci-static/resources/form.js b/modules/luci-base/htdocs/luci-static/resources/form.js
index b318168f37..52506d30e8 100644
--- a/modules/luci-base/htdocs/luci-static/resources/form.js
+++ b/modules/luci-base/htdocs/luci-static/resources/form.js
@@ -1862,6 +1862,9 @@ var CBIAbstractValue = CBIAbstractElement.extend(/** @lends LuCI.form.AbstractVa
if (cval == null)
cval = this.default;
+ if (Array.isArray(cval))
+ cval = cval.join(' ');
+
return (cval != null) ? '%h'.format(cval) : null;
},
@@ -2032,10 +2035,32 @@ var CBIAbstractValue = CBIAbstractElement.extend(/** @lends LuCI.form.AbstractVa
* The configuration section ID
*/
remove: function(section_id) {
- return this.map.data.unset(
- this.uciconfig || this.section.uciconfig || this.map.config,
- this.ucisection || section_id,
- this.ucioption || this.option);
+ var this_cfg = this.uciconfig || this.section.uciconfig || this.map.config,
+ this_sid = this.ucisection || section_id,
+ this_opt = this.ucioption || this.option;
+
+ for (var i = 0; i < this.section.children.length; i++) {
+ var sibling = this.section.children[i];
+
+ if (sibling === this || sibling.ucioption == null)
+ continue;
+
+ var sibling_cfg = sibling.uciconfig || sibling.section.uciconfig || sibling.map.config,
+ sibling_sid = sibling.ucisection || section_id,
+ sibling_opt = sibling.ucioption || sibling.option;
+
+ if (this_cfg != sibling_cfg || this_sid != sibling_sid || this_opt != sibling_opt)
+ continue;
+
+ if (!sibling.isActive(section_id))
+ continue;
+
+ /* found another active option aliasing the same uci option name,
+ * so we can't remove the value */
+ return;
+ }
+
+ this.map.data.unset(this_cfg, this_sid, this_opt);
}
});
diff --git a/modules/luci-base/htdocs/luci-static/resources/luci.js b/modules/luci-base/htdocs/luci-static/resources/luci.js
index 936630a79d..23853e2cc8 100644
--- a/modules/luci-base/htdocs/luci-static/resources/luci.js
+++ b/modules/luci-base/htdocs/luci-static/resources/luci.js
@@ -2972,7 +2972,12 @@
}).filter(function(e) {
return (e[1] != null);
}).sort(function(a, b) {
- return (a[1] > b[1]);
+ if (a[1] < b[1])
+ return -1;
+ else if (a[1] > b[1])
+ return 1;
+ else
+ return 0;
}).map(function(e) {
return e[0];
});
diff --git a/modules/luci-base/htdocs/luci-static/resources/network.js b/modules/luci-base/htdocs/luci-static/resources/network.js
index 1f749c593c..fccb72b3d2 100644
--- a/modules/luci-base/htdocs/luci-static/resources/network.js
+++ b/modules/luci-base/htdocs/luci-static/resources/network.js
@@ -387,9 +387,11 @@ function initNetworkState(refresh) {
stats: dev.stats,
macaddr: dev.mac,
type: dev.type,
+ devtype: dev.devtype,
mtu: dev.mtu,
qlen: dev.qlen,
wireless: dev.wireless,
+ parent: dev.parent,
ipaddrs: [],
ip6addrs: []
};
@@ -430,6 +432,16 @@ function initNetworkState(refresh) {
s.isBridge[name] = true;
}
+ for (var name in luci_devs) {
+ var dev = luci_devs[name];
+
+ if (!dev.parent || dev.devtype != 'dsa')
+ continue;
+
+ s.isSwitch[dev.parent] = true;
+ s.isSwitch[name] = true;
+ }
+
if (L.isObject(board_json.switch)) {
for (var switchname in board_json.switch) {
var layout = board_json.switch[switchname],
@@ -1209,6 +1221,59 @@ Network = baseclass.extend(/** @lends LuCI.network.prototype */ {
}
}
+ /* find bridge VLAN devices */
+ var uciBridgeVLANs = uci.sections('network', 'bridge-vlan');
+ for (var i = 0; i < uciBridgeVLANs.length; i++) {
+ var basedev = uciBridgeVLANs[i].device,
+ local = uciBridgeVLANs[i].local,
+ alias = uciBridgeVLANs[i].alias,
+ vid = +uciBridgeVLANs[i].vlan,
+ ports = L.toArray(uciBridgeVLANs[i].ports);
+
+ if (local == '0')
+ continue;
+
+ if (isNaN(vid) || vid < 0 || vid > 4095)
+ continue;
+
+ var vlandev = '%s.%s'.format(basedev, alias || vid);
+
+ _state.isBridge[basedev] = true;
+
+ if (!_state.bridges.hasOwnProperty(basedev))
+ _state.bridges[basedev] = {
+ name: basedev,
+ ifnames: []
+ };
+
+ if (!devices.hasOwnProperty(vlandev))
+ devices[vlandev] = this.instantiateDevice(vlandev);
+
+ ports.forEach(function(port_name) {
+ var m = port_name.match(/^([^:]+)(?::[ut*]+)?$/),
+ p = m ? m[1] : null;
+
+ if (!p)
+ return;
+
+ if (_state.bridges[basedev].ifnames.filter(function(sd) { return sd.name == p }).length)
+ return;
+
+ _state.netdevs[p] = _state.netdevs[p] || {
+ name: p,
+ ipaddrs: [],
+ ip6addrs: [],
+ type: 1,
+ devtype: 'ethernet',
+ stats: {},
+ flags: {}
+ };
+
+ _state.bridges[basedev].ifnames.push(_state.netdevs[p]);
+ _state.netdevs[p].bridge = _state.bridges[basedev];
+ });
+ }
+
/* find wireless interfaces */
var uciWifiIfaces = uci.sections('wireless', 'wifi-iface'),
networkCount = {};
@@ -1453,6 +1518,18 @@ Network = baseclass.extend(/** @lends LuCI.network.prototype */ {
}
}
+ rv.sort(function(a, b) {
+ if (a.metric != b.metric)
+ return (a.metric - b.metric);
+
+ if (a.interface < b.interface)
+ return -1;
+ else if (a.interface > b.interface)
+ return 1;
+
+ return 0;
+ });
+
return rv;
}, this));
},
@@ -2419,14 +2496,14 @@ Protocol = baseclass.extend(/** @lends LuCI.network.Protocol.prototype */ {
*
* Alias interfaces are interfaces layering on top of another interface
* and are denoted by a special `@interfacename` notation in the
- * underlying `ifname` option.
+ * underlying `device` option.
*
* @returns {null|string}
* Returns the name of the parent interface if this logical interface
* is an alias or `null` if it is not an alias interface.
*/
isAlias: function() {
- var ifnames = L.toArray(uci.get('network', this.sid, 'ifname')),
+ var ifnames = L.toArray(uci.get('network', this.sid, 'device')),
parent = null;
for (var i = 0; i < ifnames.length; i++)
@@ -2450,9 +2527,9 @@ Protocol = baseclass.extend(/** @lends LuCI.network.Protocol.prototype */ {
return false;
var empty = true,
- ifname = this._get('ifname');
+ device = this._get('device');
- if (ifname != null && ifname.match(/\S+/))
+ if (device != null && device.match(/\S+/))
empty = false;
if (empty == true && getWifiNetidBySid(this.sid) != null)
@@ -2484,18 +2561,18 @@ Protocol = baseclass.extend(/** @lends LuCI.network.Protocol.prototype */ {
* argument was invalid, if the device was already part of the logical
* interface or if the logical interface is virtual.
*/
- addDevice: function(ifname) {
- ifname = ifnameOf(ifname);
+ addDevice: function(device) {
+ device = ifnameOf(device);
- if (ifname == null || this.isFloating())
+ if (device == null || this.isFloating())
return false;
- var wif = getWifiSidByIfname(ifname);
+ var wif = getWifiSidByIfname(device);
if (wif != null)
return appendValue('wireless', wif, 'network', this.sid);
- return appendValue('network', this.sid, 'ifname', ifname);
+ return appendValue('network', this.sid, 'device', device);
},
/**
@@ -2511,20 +2588,20 @@ Protocol = baseclass.extend(/** @lends LuCI.network.Protocol.prototype */ {
* argument was invalid, if the device was already part of the logical
* interface or if the logical interface is virtual.
*/
- deleteDevice: function(ifname) {
+ deleteDevice: function(device) {
var rv = false;
- ifname = ifnameOf(ifname);
+ device = ifnameOf(device);
- if (ifname == null || this.isFloating())
+ if (device == null || this.isFloating())
return false;
- var wif = getWifiSidByIfname(ifname);
+ var wif = getWifiSidByIfname(device);
if (wif != null)
rv = removeValue('wireless', wif, 'network', this.sid);
- if (removeValue('network', this.sid, 'ifname', ifname))
+ if (removeValue('network', this.sid, 'device', device))
rv = true;
return rv;
@@ -2550,7 +2627,7 @@ Protocol = baseclass.extend(/** @lends LuCI.network.Protocol.prototype */ {
return new Device(ifname, this);
}
else {
- var ifnames = L.toArray(uci.get('network', this.sid, 'ifname'));
+ var ifnames = L.toArray(uci.get('network', this.sid, 'device'));
for (var i = 0; i < ifnames.length; i++) {
var m = ifnames[i].match(/^([^:/]+)/);
@@ -2605,13 +2682,10 @@ Protocol = baseclass.extend(/** @lends LuCI.network.Protocol.prototype */ {
if (!this.isBridge() && !(this.isVirtual() && !this.isFloating()))
return null;
- var ifnames = L.toArray(uci.get('network', this.sid, 'ifname'));
+ var device = uci.get('network', this.sid, 'device');
- for (var i = 0; i < ifnames.length; i++) {
- if (ifnames[i].charAt(0) == '@')
- continue;
-
- var m = ifnames[i].match(/^([^:/]+)/);
+ if (device && device.charAt(0) != '@') {
+ var m = device.match(/^([^:/]+)/);
if (m != null)
rv.push(Network.prototype.instantiateDevice(m[1], this));
}
@@ -2653,25 +2727,24 @@ Protocol = baseclass.extend(/** @lends LuCI.network.Protocol.prototype */ {
* Returns `true` when this logical interface contains the given network
* device or `false` if not.
*/
- containsDevice: function(ifname) {
- ifname = ifnameOf(ifname);
+ containsDevice: function(device) {
+ device = ifnameOf(device);
- if (ifname == null)
+ if (device == null)
return false;
- else if (this.isVirtual() && '%s-%s'.format(this.getProtocol(), this.sid) == ifname)
+ else if (this.isVirtual() && '%s-%s'.format(this.getProtocol(), this.sid) == device)
return true;
- else if (this.isBridge() && 'br-%s'.format(this.sid) == ifname)
+ else if (this.isBridge() && 'br-%s'.format(this.sid) == device)
return true;
- var ifnames = L.toArray(uci.get('network', this.sid, 'ifname'));
-
- for (var i = 0; i < ifnames.length; i++) {
- var m = ifnames[i].match(/^([^:/]+)/);
- if (m != null && m[1] == ifname)
+ var name = uci.get('network', this.sid, 'device');
+ if (name) {
+ var m = name.match(/^([^:/]+)/);
+ if (m != null && m[1] == device)
return true;
}
- var wif = getWifiSidByIfname(ifname);
+ var wif = getWifiSidByIfname(device);
if (wif != null) {
var networks = L.toArray(uci.get('wireless', wif, 'network'));
@@ -2714,19 +2787,19 @@ Protocol = baseclass.extend(/** @lends LuCI.network.Protocol.prototype */ {
* device and allows querying device details such as packet statistics or MTU.
*/
Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
- __init__: function(ifname, network) {
- var wif = getWifiSidByIfname(ifname);
+ __init__: function(device, network) {
+ var wif = getWifiSidByIfname(device);
if (wif != null) {
var res = getWifiStateBySid(wif) || [],
netid = getWifiNetidBySid(wif) || [];
- this.wif = new WifiNetwork(wif, res[0], res[1], netid[0], res[2], { ifname: ifname });
- this.ifname = this.wif.getIfname();
+ this.wif = new WifiNetwork(wif, res[0], res[1], netid[0], res[2], { ifname: device });
+ this.device = this.wif.getIfname();
}
- this.ifname = this.ifname || ifname;
- this.dev = _state.netdevs[this.ifname];
+ this.device = this.device || device;
+ this.dev = Object.assign({}, _state.netdevs[this.device]);
this.network = network;
},
@@ -2749,7 +2822,7 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
* Returns the name of the device, e.g. `eth0` or `wlan0`.
*/
getName: function() {
- return (this.wif != null ? this.wif.getIfname() : this.ifname);
+ return (this.wif != null ? this.wif.getIfname() : this.device);
},
/**
@@ -2797,7 +2870,7 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
},
/**
- * Get the type of the device..
+ * Get the type of the device.
*
* @returns {string}
* Returns a string describing the type of the network device:
@@ -2810,17 +2883,17 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
* - `ethernet` for all other device types
*/
getType: function() {
- if (this.ifname != null && this.ifname.charAt(0) == '@')
+ if (this.device != null && this.device.charAt(0) == '@')
return 'alias';
- else if (this.wif != null || isWifiIfname(this.ifname))
+ else if (this.dev.devtype == 'wlan' || this.wif != null || isWifiIfname(this.device))
return 'wifi';
- else if (_state.isBridge[this.ifname])
+ else if (this.dev.devtype == 'bridge' || _state.isBridge[this.device])
return 'bridge';
- else if (_state.isTunnel[this.ifname])
+ else if (_state.isTunnel[this.device])
return 'tunnel';
- else if (this.ifname.indexOf('.') > -1)
+ else if (this.dev.devtype == 'vlan' || this.device.indexOf('.') > -1)
return 'vlan';
- else if (_state.isSwitch[this.ifname])
+ else if (this.dev.devtype == 'dsa' || _state.isSwitch[this.device])
return 'switch';
else
return 'ethernet';
@@ -2837,7 +2910,7 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
if (this.wif != null)
return this.wif.getShortName();
- return this.ifname;
+ return this.device;
},
/**
@@ -2877,10 +2950,11 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
return _('Bridge');
case 'switch':
- return _('Ethernet Switch');
+ return (_state.netdevs[this.device] && _state.netdevs[this.device].devtype == 'dsa')
+ ? _('Switch port') : _('Ethernet Switch');
case 'vlan':
- return (_state.isSwitch[this.ifname] ? _('Switch VLAN') : _('Software VLAN'));
+ return (_state.isSwitch[this.device] ? _('Switch VLAN') : _('Software VLAN'));
case 'tunnel':
return _('Tunnel Interface');
@@ -2899,7 +2973,7 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
* a Linux bridge.
*/
getPorts: function() {
- var br = _state.bridges[this.ifname],
+ var br = _state.bridges[this.device],
rv = [];
if (br == null || !Array.isArray(br.ifnames))
@@ -2921,7 +2995,7 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
* device is not a Linux bridge.
*/
getBridgeID: function() {
- var br = _state.bridges[this.ifname];
+ var br = _state.bridges[this.device];
return (br != null ? br.id : null);
},
@@ -2933,7 +3007,7 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
* enabled, else `false`.
*/
getBridgeSTP: function() {
- var br = _state.bridges[this.ifname];
+ var br = _state.bridges[this.device];
return (br != null ? !!br.stp : false);
},
@@ -3045,7 +3119,7 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
var networks = enumerateNetworks.apply(L.network);
for (var i = 0; i < networks.length; i++)
- if (networks[i].containsDevice(this.ifname) || networks[i].getIfname() == this.ifname)
+ if (networks[i].containsDevice(this.device) || networks[i].getIfname() == this.device)
this.networks.push(networks[i]);
this.networks.sort(networkSort);
@@ -3064,6 +3138,22 @@ Device = baseclass.extend(/** @lends LuCI.network.Device.prototype */ {
*/
getWifiNetwork: function() {
return (this.wif != null ? this.wif : null);
+ },
+
+ /**
+ * Get the logical parent device of this device.
+ *
+ * In case of DSA switch ports, the parent device will be the DSA switch
+ * device itself, for VLAN devices, the parent refers to the base device
+ * etc.
+ *
+ * @returns {null|LuCI.network.Device}
+ * Returns a `Network.Device` instance representing the parent device or
+ * `null` when this device has no parent, as it is the case for e.g.
+ * ordinary ethernet interfaces.
+ */
+ getParent: function() {
+ return this.dev.parent ? Network.prototype.instantiateDevice(this.dev.parent) : null;
}
});
diff --git a/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js b/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js
index bacbf559f9..71adc235ca 100644
--- a/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js
+++ b/modules/luci-base/htdocs/luci-static/resources/protocol/dhcp.js
@@ -17,7 +17,7 @@ return network.registerProtocol('dhcp', {
},
renderFormOptions: function(s) {
- var dev = this.getL2Device() || this.getDevice(), o;
+ var o;
o = s.taboption('general', form.Value, 'hostname', _('Hostname to send when requesting DHCP'));
o.default = '';
@@ -34,32 +34,9 @@ return network.registerProtocol('dhcp', {
o = s.taboption('advanced', form.Flag, 'broadcast', _('Use broadcast flag'), _('Required for certain ISPs, e.g. Charter with DOCSIS 3'));
o.default = o.disabled;
- o = s.taboption('advanced', form.Flag, 'defaultroute', _('Use default gateway'), _('If unchecked, no default route is configured'));
- o.default = o.enabled;
-
- o = s.taboption('advanced', form.Flag, 'peerdns', _('Use DNS servers advertised by peer'), _('If unchecked, the advertised DNS server addresses are ignored'));
- o.default = o.enabled;
-
- o = s.taboption('advanced', form.DynamicList, 'dns', _('Use custom DNS servers'));
- o.depends('peerdns', '0');
- o.datatype = 'ipaddr';
- o.cast = 'string';
-
- o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric'));
- o.placeholder = '0';
- o.datatype = 'uinteger';
-
o = s.taboption('advanced', form.Value, 'clientid', _('Client ID to send when requesting DHCP'));
o.datatype = 'hexstring';
s.taboption('advanced', form.Value, 'vendorid', _('Vendor Class to send when requesting DHCP'));
-
- o = s.taboption('advanced', form.Value, 'macaddr', _('Override MAC address'));
- o.datatype = 'macaddr';
- o.placeholder = dev ? (dev.getMAC() || '') : '';
-
- o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU'));
- o.placeholder = dev ? (dev.getMTU() || '1500') : '1500';
- o.datatype = 'max(9200)';
}
});
diff --git a/modules/luci-base/htdocs/luci-static/resources/protocol/static.js b/modules/luci-base/htdocs/luci-static/resources/protocol/static.js
index 2d70ae681f..42ebcefba4 100644
--- a/modules/luci-base/htdocs/luci-static/resources/protocol/static.js
+++ b/modules/luci-base/htdocs/luci-static/resources/protocol/static.js
@@ -173,34 +173,12 @@ return network.registerProtocol('static', {
},
renderFormOptions: function(s) {
- var dev = this.getL2Device() || this.getDevice(), o;
+ var o;
s.taboption('general', this.CBIIPValue, 'ipaddr', _('IPv4 address'));
s.taboption('general', this.CBINetmaskValue, 'netmask', _('IPv4 netmask'));
s.taboption('general', this.CBIGatewayValue, 'gateway', _('IPv4 gateway'));
s.taboption('general', this.CBIBroadcastValue, 'broadcast', _('IPv4 broadcast'));
- s.taboption('general', form.DynamicList, 'dns', _('Use custom DNS servers'));
-
- o = s.taboption('general', form.Value, 'ip6assign', _('IPv6 assignment length'), _('Assign a part of given length of every public IPv6-prefix to this interface'));
- o.value('', _('disabled'));
- o.value('64');
- o.datatype = 'max(64)';
-
- o = s.taboption('general', form.Value, 'ip6hint', _('IPv6 assignment hint'), _('Assign prefix parts using this hexadecimal subprefix ID for this interface.'));
- o.placeholder = '0';
- o.validate = function(section_id, value) {
- if (value == null || value == '')
- return true;
-
- var n = parseInt(value, 16);
-
- if (!/^(0x)?[0-9a-fA-F]+$/.test(value) || isNaN(n) || n >= 0xffffffff)
- return _('Expecting a hexadecimal assignment hint');
-
- return true;
- };
- for (var i = 33; i <= 64; i++)
- o.depends('ip6assign', String(i));
o = s.taboption('general', form.DynamicList, 'ip6addr', _('IPv6 address'));
o.datatype = 'ip6addr';
@@ -214,21 +192,5 @@ return network.registerProtocol('static', {
o = s.taboption('general', form.Value, 'ip6prefix', _('IPv6 routed prefix'), _('Public prefix routed to this device for distribution to clients.'));
o.datatype = 'ip6addr';
o.depends('ip6assign', '');
-
- o = s.taboption('general', form.Value, 'ip6ifaceid', _('IPv6 suffix'), _("Optional. Allowed values: 'eui64', 'random', fixed value like '::1' or '::1:2'. When IPv6 prefix (like 'a:b:c:d::') is received from a delegating server, use the suffix (like '::1') to form the IPv6 address ('a:b:c:d::1') for the interface."));
- o.datatype = 'ip6hostid';
- o.placeholder = '::1';
-
- o = s.taboption('advanced', form.Value, 'macaddr', _('Override MAC address'));
- o.datatype = 'macaddr';
- o.placeholder = dev ? (dev.getMAC() || '') : '';
-
- o = s.taboption('advanced', form.Value, 'mtu', _('Override MTU'));
- o.datatype = 'max(9200)';
- o.placeholder = dev ? (dev.getMTU() || '1500') : '1500';
-
- o = s.taboption('advanced', form.Value, 'metric', _('Use gateway metric'));
- o.placeholder = this.getMetric() || '0';
- o.datatype = 'uinteger';
}
});
diff --git a/modules/luci-base/htdocs/luci-static/resources/uci.js b/modules/luci-base/htdocs/luci-static/resources/uci.js
index cbaeb8c080..41e902c5fe 100644
--- a/modules/luci-base/htdocs/luci-static/resources/uci.js
+++ b/modules/luci-base/htdocs/luci-static/resources/uci.js
@@ -489,8 +489,28 @@ return baseclass.extend(/** @lends LuCI.uci.prototype */ {
}
/* requested an entire section */
- if (v[conf])
- return v[conf][sid];
+ if (v[conf]) {
+ /* check whether entire section was deleted */
+ if (d[conf] && d[conf][sid] === true)
+ return null;
+
+ var s = v[conf][sid] || null;
+
+ if (s) {
+ /* merge changes */
+ if (c[conf] && c[conf][sid])
+ for (var opt in c[conf][sid])
+ if (c[conf][sid][opt] != null)
+ s[opt] = c[conf][sid][opt];
+
+ /* merge deletions */
+ if (d[conf] && d[conf][sid])
+ for (var opt in d[conf][sid])
+ delete s[opt];
+ }
+
+ return s;
+ }
return null;
},
diff --git a/modules/luci-base/htdocs/luci-static/resources/ui.js b/modules/luci-base/htdocs/luci-static/resources/ui.js
index 3ada9e375a..5a0e6d297b 100644
--- a/modules/luci-base/htdocs/luci-static/resources/ui.js
+++ b/modules/luci-base/htdocs/luci-static/resources/ui.js
@@ -633,8 +633,9 @@ var UICheckbox = UIElement.extend(/** @lends LuCI.ui.Checkbox.prototype */ {
bind: function(frameEl) {
this.node = frameEl;
- this.setUpdateEvents(frameEl.lastElementChild.previousElementSibling, 'click', 'blur');
- this.setChangeEvents(frameEl.lastElementChild.previousElementSibling, 'change');
+ var input = frameEl.querySelector('input[type="checkbox"]');
+ this.setUpdateEvents(input, 'click', 'blur');
+ this.setChangeEvents(input, 'change');
dom.bindClassInstance(frameEl, this);
@@ -650,7 +651,7 @@ var UICheckbox = UIElement.extend(/** @lends LuCI.ui.Checkbox.prototype */ {
* Returns `true` when the checkbox is currently checked, otherwise `false`.
*/
isChecked: function() {
- return this.node.lastElementChild.previousElementSibling.checked;
+ return this.node.querySelector('input[type="checkbox"]').checked;
},
/** @override */
@@ -662,7 +663,7 @@ var UICheckbox = UIElement.extend(/** @lends LuCI.ui.Checkbox.prototype */ {
/** @override */
setValue: function(value) {
- this.node.lastElementChild.previousElementSibling.checked = (value == this.options.value_enabled);
+ this.node.querySelector('input[type="checkbox"]').checked = (value == this.options.value_enabled);
}
});
@@ -1360,6 +1361,8 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
/** @private */
toggleItem: function(sb, li, force_state) {
+ var ul = li.parentNode;
+
if (li.hasAttribute('unselectable'))
return;
@@ -1436,7 +1439,7 @@ var UIDropdown = UIElement.extend(/** @lends LuCI.ui.Dropdown.prototype */ {
this.closeDropdown(sb, true);
}
- this.saveValues(sb, li.parentNode);
+ this.saveValues(sb, ul);
},
/** @private */
@@ -4060,7 +4063,7 @@ var UI = baseclass.extend(/** @lends LuCI.ui.prototype */ {
E('button', {
'class': 'btn',
'click': UI.prototype.hideModal
- }, [ _('Dismiss') ]), ' ',
+ }, [ _('Close') ]), ' ',
E('button', {
'class': 'cbi-button cbi-button-positive important',
'click': L.bind(this.apply, this, true)
diff --git a/modules/luci-base/htdocs/luci-static/resources/validation.js b/modules/luci-base/htdocs/luci-static/resources/validation.js
index d47392c239..28042ba8cd 100644
--- a/modules/luci-base/htdocs/luci-static/resources/validation.js
+++ b/modules/luci-base/htdocs/luci-static/resources/validation.js
@@ -272,18 +272,21 @@ var ValidatorFactory = baseclass.extend({
_('valid IPv6 prefix value (0-128)'));
},
- cidr: function() {
- return this.assert(this.apply('cidr4') || this.apply('cidr6'), _('valid IPv4 or IPv6 CIDR'));
+ cidr: function(negative) {
+ return this.assert(this.apply('cidr4', null, [negative]) || this.apply('cidr6', null, [negative]),
+ _('valid IPv4 or IPv6 CIDR'));
},
- cidr4: function() {
- var m = this.value.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(\d{1,2})$/);
- return this.assert(m && this.factory.parseIPv4(m[1]) && this.apply('ip4prefix', m[2]), _('valid IPv4 CIDR'));
+ cidr4: function(negative) {
+ var m = this.value.match(/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\/(-)?(\d{1,2})$/);
+ return this.assert(m && this.factory.parseIPv4(m[1]) && (negative || !m[2]) && this.apply('ip4prefix', m[3]),
+ _('valid IPv4 CIDR'));
},
- cidr6: function() {
- var m = this.value.match(/^([0-9a-fA-F:.]+)\/(\d{1,3})$/);
- return this.assert(m && this.factory.parseIPv6(m[1]) && this.apply('ip6prefix', m[2]), _('valid IPv6 CIDR'));
+ cidr6: function(negative) {
+ var m = this.value.match(/^([0-9a-fA-F:.]+)\/(-)?(\d{1,3})$/);
+ return this.assert(m && this.factory.parseIPv6(m[1]) && (negative || !m[2]) && this.apply('ip6prefix', m[3]),
+ _('valid IPv6 CIDR'));
},
ipnet4: function() {
@@ -304,18 +307,18 @@ var ValidatorFactory = baseclass.extend({
return this.assert(!(!v6 || v6[0] || v6[1] || v6[2] || v6[3]), _('valid IPv6 host id'));
},
- ipmask: function() {
- return this.assert(this.apply('ipmask4') || this.apply('ipmask6'),
+ ipmask: function(negative) {
+ return this.assert(this.apply('ipmask4', null, [negative]) || this.apply('ipmask6', null, [negative]),
_('valid network in address/netmask notation'));
},
- ipmask4: function() {
- return this.assert(this.apply('cidr4') || this.apply('ipnet4') || this.apply('ip4addr'),
+ ipmask4: function(negative) {
+ return this.assert(this.apply('cidr4', null, [negative]) || this.apply('ipnet4') || this.apply('ip4addr'),
_('valid IPv4 network'));
},
- ipmask6: function() {
- return this.assert(this.apply('cidr6') || this.apply('ipnet6') || this.apply('ip6addr'),
+ ipmask6: function(negative) {
+ return this.assert(this.apply('cidr6', null, [negative]) || this.apply('ipnet6') || this.apply('ip6addr'),
_('valid IPv6 network'));
},
@@ -358,8 +361,8 @@ var ValidatorFactory = baseclass.extend({
},
network: function() {
- return this.assert(this.apply('uciname') || this.apply('host'),
- _('valid UCI identifier, hostname or IP address'));
+ return this.assert(this.apply('uciname') || this.apply('hostname') || this.apply('ip4addr') || this.apply('ip6addr'),
+ _('valid UCI identifier, hostname or IP address range'));
},
hostport: function(ipv4only) {