diff options
Diffstat (limited to 'modules/luci-base/htdocs/luci-static/resources')
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) { |