return n;
};
+ this.toColor = function(str)
+ {
+ if (typeof(str) != 'string' || str.length == 0)
+ return '#CCCCCC';
+
+ if (str == 'wan')
+ return '#F09090';
+ else if (str == 'lan')
+ return '#90F090';
+
+ var i = 0, hash = 0;
+
+ while (i < str.length)
+ hash = str.charCodeAt(i++) + ((hash << 5) - hash);
+
+ var r = (hash & 0xFF) % 128;
+ var g = ((hash >> 8) & 0xFF) % 128;
+
+ var min = 0;
+ var max = 128;
+
+ if ((r + g) < 128)
+ min = 128 - r - g;
+ else
+ max = 255 - r - g;
+
+ var b = min + (((hash >> 16) & 0xFF) % (max - min));
+
+ return '#%02X%02X%02X'.format(0xFF - r, 0xFF - g, 0xFF - b);
+ };
+
+ this.parseIPv4 = function(str)
+ {
+ if ((typeof(str) != 'string' && !(str instanceof String)) ||
+ !str.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/))
+ return undefined;
+
+ var num = [ ];
+ var parts = str.split(/\./);
+
+ for (var i = 0; i < parts.length; i++)
+ {
+ var n = parseInt(parts[i], 10);
+ if (isNaN(n) || n > 255)
+ return undefined;
+
+ num.push(n);
+ }
+
+ return num;
+ };
+
+ this.parseIPv6 = function(str)
+ {
+ if ((typeof(str) != 'string' && !(str instanceof String)) ||
+ !str.match(/^[a-fA-F0-9:]+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/))
+ return undefined;
+
+ var parts = str.split(/::/);
+ if (parts.length == 0 || parts.length > 2)
+ return undefined;
+
+ var lnum = [ ];
+ if (parts[0].length > 0)
+ {
+ var left = parts[0].split(/:/);
+ for (var i = 0; i < left.length; i++)
+ {
+ var n = parseInt(left[i], 16);
+ if (isNaN(n))
+ return undefined;
+
+ lnum.push((n / 256) >> 0);
+ lnum.push(n % 256);
+ }
+ }
+
+ var rnum = [ ];
+ if (parts.length > 1 && parts[1].length > 0)
+ {
+ var right = parts[1].split(/:/);
+
+ for (var i = 0; i < right.length; i++)
+ {
+ if (right[i].indexOf('.') > 0)
+ {
+ var addr = L.parseIPv4(right[i]);
+ if (!addr)
+ return undefined;
+
+ rnum.push.apply(rnum, addr);
+ continue;
+ }
+
+ var n = parseInt(right[i], 16);
+ if (isNaN(n))
+ return undefined;
+
+ rnum.push((n / 256) >> 0);
+ rnum.push(n % 256);
+ }
+ }
+
+ if (rnum.length > 0 && (lnum.length + rnum.length) > 15)
+ return undefined;
+
+ var num = [ ];
+
+ num.push.apply(num, lnum);
+
+ for (var i = 0; i < (16 - lnum.length - rnum.length); i++)
+ num.push(0);
+
+ num.push.apply(num, rnum);
+
+ if (num.length > 16)
+ return undefined;
+
+ return num;
+ };
+
+ this.isNetmask = function(addr)
+ {
+ if (!$.isArray(addr))
+ return false;
+
+ var c;
+
+ for (c = 0; (c < addr.length) && (addr[c] == 255); c++);
+
+ if (c == addr.length)
+ return true;
+
+ if ((addr[c] == 254) || (addr[c] == 252) || (addr[c] == 248) ||
+ (addr[c] == 240) || (addr[c] == 224) || (addr[c] == 192) ||
+ (addr[c] == 128) || (addr[c] == 0))
+ {
+ for (c++; (c < addr.length) && (addr[c] == 0); c++);
+
+ if (c == addr.length)
+ return true;
+ }
+
+ return false;
+ };
+
this.globals = {
timeout: 15000,
resource: '/luci2',
return this.set(conf, sid, opt, undefined);
},
+ get_first: function(conf, type, opt)
+ {
+ var sid = undefined;
+
+ L.uci.sections(conf, type, function(s) {
+ if (typeof(sid) != 'string')
+ sid = s['.name'];
+ });
+
+ return this.get(conf, sid, opt);
+ },
+
+ set_first: function(conf, type, opt, val)
+ {
+ var sid = undefined;
+
+ L.uci.sections(conf, type, function(s) {
+ if (typeof(sid) != 'string')
+ sid = s['.name'];
+ });
+
+ return this.set(conf, sid, opt, val);
+ },
+
+ unset_first: function(conf, type, opt)
+ {
+ return this.set_first(conf, type, opt, undefined);
+ },
+
_reload: function()
{
var pkgs = [ ];
_fetch_protocols: function()
{
var self = L.NetworkModel;
- var deferreds = [ ];
+ var deferreds = [
+ self._fetch_protocol('none')
+ ];
for (var proto in self._cache.protolist)
deferreds.push(self._fetch_protocol(proto));
return dev.getTrafficHistory();
},
+ renderBadge: function()
+ {
+ var badge = $('<span />')
+ .addClass('badge')
+ .text('%s: '.format(this.name()));
+
+ var dev = this.getDevice();
+ var subdevs = this.getSubdevices();
+
+ if (subdevs.length)
+ for (var j = 0; j < subdevs.length; j++)
+ badge.append($('<img />')
+ .attr('src', subdevs[j].icon())
+ .attr('title', '%s (%s)'.format(subdevs[j].description(), subdevs[j].name() || '?')));
+ else if (dev)
+ badge.append($('<img />')
+ .attr('src', dev.icon())
+ .attr('title', '%s (%s)'.format(dev.description(), dev.name() || '?')));
+ else
+ badge.append($('<em />').text(L.tr('(No devices attached)')));
+
+ return badge;
+ },
+
setDevices: function(devs)
{
var dev = this.getPhysdev();
'ipaddr': function()
{
- if (validation.types['ip4addr'].apply(this) ||
- validation.types['ip6addr'].apply(this))
+ if (L.parseIPv4(this) || L.parseIPv6(this))
return true;
validation.i18n('Must be a valid IP address');
'ip4addr': function()
{
- if (this.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})(\/(\S+))?$/))
- {
- if ((RegExp.$1 >= 0) && (RegExp.$1 <= 255) &&
- (RegExp.$2 >= 0) && (RegExp.$2 <= 255) &&
- (RegExp.$3 >= 0) && (RegExp.$3 <= 255) &&
- (RegExp.$4 >= 0) && (RegExp.$4 <= 255) &&
- ((RegExp.$6.indexOf('.') < 0)
- ? ((RegExp.$6 >= 0) && (RegExp.$6 <= 32))
- : (validation.types['ip4addr'].apply(RegExp.$6))))
- return true;
- }
+ if (L.parseIPv4(this))
+ return true;
validation.i18n('Must be a valid IPv4 address');
return false;
'ip6addr': function()
{
- if (this.match(/^([a-fA-F0-9:.]+)(\/(\d+))?$/))
- {
- if (!RegExp.$2 || ((RegExp.$3 >= 0) && (RegExp.$3 <= 128)))
- {
- var addr = RegExp.$1;
+ if (L.parseIPv6(this))
+ return true;
- if (addr == '::')
- {
- return true;
- }
+ validation.i18n('Must be a valid IPv6 address');
+ return false;
+ },
- if (addr.indexOf('.') > 0)
- {
- var off = addr.lastIndexOf(':');
+ 'netmask4': function()
+ {
+ if (L.isNetmask(L.parseIPv4(this)))
+ return true;
- if (!(off && validation.types['ip4addr'].apply(addr.substr(off+1))))
- {
- validation.i18n('Must be a valid IPv6 address');
- return false;
- }
+ validation.i18n('Must be a valid IPv4 netmask');
+ return false;
+ },
- addr = addr.substr(0, off) + ':0:0';
- }
+ 'netmask6': function()
+ {
+ if (L.isNetmask(L.parseIPv6(this)))
+ return true;
- if (addr.indexOf('::') >= 0)
- {
- var colons = 0;
- var fill = '0';
+ validation.i18n('Must be a valid IPv6 netmask6');
+ return false;
+ },
- for (var i = 1; i < (addr.length-1); i++)
- if (addr.charAt(i) == ':')
- colons++;
+ 'cidr4': function()
+ {
+ if (this.match(/^([0-9.]+)\/(\d{1,2})$/))
+ if (RegExp.$2 <= 32 && L.parseIPv4(RegExp.$1))
+ return true;
- if (colons > 7)
- {
- validation.i18n('Must be a valid IPv6 address');
- return false;
- }
+ validation.i18n('Must be a valid IPv4 prefix');
+ return false;
+ },
- for (var i = 0; i < (7 - colons); i++)
- fill += ':0';
+ 'cidr6': function()
+ {
+ if (this.match(/^([a-fA-F0-9:.]+)\/(\d{1,3})$/))
+ if (RegExp.$2 <= 128 && L.parseIPv6(RegExp.$1))
+ return true;
- if (addr.match(/^(.*?)::(.*?)$/))
- addr = (RegExp.$1 ? RegExp.$1 + ':' : '') + fill +
- (RegExp.$2 ? ':' + RegExp.$2 : '');
- }
+ validation.i18n('Must be a valid IPv6 prefix');
+ return false;
+ },
- if (addr.match(/^(?:[a-fA-F0-9]{1,4}:){7}[a-fA-F0-9]{1,4}$/) != null)
- return true;
+ 'ipmask4': function()
+ {
+ if (this.match(/^([0-9.]+)\/([0-9.]+)$/))
+ {
+ var addr = RegExp.$1, mask = RegExp.$2;
+ if (L.parseIPv4(addr) && L.isNetmask(L.parseIPv4(mask)))
+ return true;
+ }
- validation.i18n('Must be a valid IPv6 address');
- return false;
- }
+ validation.i18n('Must be a valid IPv4 address/netmask pair');
+ return false;
+ },
+
+ 'ipmask6': function()
+ {
+ if (this.match(/^([a-fA-F0-9:.]+)\/([a-fA-F0-9:.]+)$/))
+ {
+ var addr = RegExp.$1, mask = RegExp.$2;
+ if (L.parseIPv6(addr) && L.isNetmask(L.parseIPv6(mask)))
+ return true;
}
- validation.i18n('Must be a valid IPv6 address');
+ validation.i18n('Must be a valid IPv6 address/netmask pair');
return false;
},
if (typeof(a) != typeof(b))
return true;
- if (typeof(a) == 'object')
+ if ($.isArray(a))
{
if (a.length != b.length)
return true;
return false;
}
+ else if ($.isPlainObject(a))
+ {
+ for (var k in a)
+ if (!(k in b))
+ return true;
+
+ for (var k in b)
+ if (!(k in a) || a[k] !== b[k])
+ return true;
+
+ return false;
+ }
return (a != b);
},
for (var i = 0; i < interfaces.length; i++)
{
var iface = interfaces[i];
- var badge = $('<span />')
- .addClass('badge')
- .text('%s: '.format(iface.name()));
-
- var dev = iface.getDevice();
- var subdevs = iface.getSubdevices();
-
- if (subdevs.length)
- for (var j = 0; j < subdevs.length; j++)
- badge.append(this._device_icon(subdevs[j]));
- else if (dev)
- badge.append(this._device_icon(dev));
- else
- badge.append($('<em />').text(L.tr('(No devices attached)')));
$('<li />')
.append($('<label />')
.addClass(itype + ' inline')
- .append($('<input />')
+ .append(this.validator(sid, $('<input />')
.attr('name', itype + id)
.attr('type', itype)
.attr('value', iface.name())
- .prop('checked', !!check[iface.name()]))
- .append(badge))
+ .prop('checked', !!check[iface.name()]), true))
+ .append(iface.renderBadge()))
.appendTo(ul);
}
return true;
},
+ sort: function(section1, section2)
+ {
+ return 0;
+ },
+
sections: function(cb)
{
var s1 = L.uci.sections(this.map.uci_package);
if (this.filter(s1[i]))
s2.push(s1[i]);
+ s2.sort(this.sort);
+
if (typeof(cb) == 'function')
for (var i = 0; i < s2.length; i++)
cb.call(this, s2[i]);
{
var self = ev.data.self;
- self.trigger('save', ev);
+ self.send().then(function() {
+ self.trigger('save', ev);
+ });
},
_ev_reset: function(ev)
});
},
- reset: function()
+ revert: function()
{
- var self = this;
var packages = { };
for (var i = 0; i < this.sections.length; i++)
packages[this.uci_package] = true;
L.uci.unload(L.toArray(packages));
+ },
+
+ reset: function()
+ {
+ var self = this;
+
+ self.revert();
return self.insertInto(self.target);
},
var self = ev.data.self;
self.trigger('close', ev);
+ self.revert();
self.close();
},