2 LuCI2 - OpenWrt Web Interface
4 Copyright 2013-2014 Jo-Philipp Wich <jow@openwrt.org>
6 Licensed under the Apache License, Version 2.0 (the "License");
7 you may not use this file except in compliance with the License.
8 You may obtain a copy of the License at
10 http://www.apache.org/licenses/LICENSE-2.0
13 String.prototype.format = function()
15 var html_esc = [/&/g, '&', /"/g, '"', /'/g, ''', /</g, '<', />/g, '>'];
16 var quot_esc = [/"/g, '"', /'/g, '''];
19 for( var i = 0; i < r.length; i += 2 )
20 s = s.replace(r[i], r[i+1]);
26 var re = /^(([^%]*)%('.|0|\x20)?(-)?(\d+)?(\.\d+)?(%|b|c|d|u|f|o|s|x|X|q|h|j|t|m))/;
27 var a = b = [], numSubstitutions = 0, numMatches = 0;
29 while ((a = re.exec(str)) != null)
32 var leftpart = a[2], pPad = a[3], pJustify = a[4], pMinLength = a[5];
33 var pPrecision = a[6], pType = a[7];
43 if (numSubstitutions < arguments.length)
45 var param = arguments[numSubstitutions++];
48 if (pPad && pPad.substr(0,1) == "'")
49 pad = leftpart.substr(1,1);
53 var justifyRight = true;
54 if (pJustify && pJustify === "-")
59 minLength = parseInt(pMinLength);
62 if (pPrecision && pType == 'f')
63 precision = parseInt(pPrecision.substring(1));
70 subst = (parseInt(param) || 0).toString(2);
74 subst = String.fromCharCode(parseInt(param) || 0);
78 subst = (parseInt(param) || 0);
82 subst = Math.abs(parseInt(param) || 0);
86 subst = (precision > -1)
87 ? ((parseFloat(param) || 0.0)).toFixed(precision)
88 : (parseFloat(param) || 0.0);
92 subst = (parseInt(param) || 0).toString(8);
100 subst = ('' + (parseInt(param) || 0).toString(16)).toLowerCase();
104 subst = ('' + (parseInt(param) || 0).toString(16)).toUpperCase();
108 subst = esc(param, html_esc);
112 subst = esc(param, quot_esc);
116 subst = String.serialize(param);
123 var ts = (param || 0);
126 tm = Math.floor(ts / 60);
131 th = Math.floor(tm / 60);
136 td = Math.floor(th / 24);
141 ? '%dd %dh %dm %ds'.format(td, th, tm, ts)
142 : '%dh %dm %ds'.format(th, tm, ts);
147 var mf = pMinLength ? parseInt(pMinLength) : 1000;
148 var pr = pPrecision ? Math.floor(10*parseFloat('0'+pPrecision)) : 2;
151 var val = parseFloat(param || 0);
152 var units = [ '', 'K', 'M', 'G', 'T', 'P', 'E' ];
154 for (i = 0; (i < units.length) && (val > mf); i++)
157 subst = val.toFixed(pr) + ' ' + units[i];
161 subst = (typeof(subst) == 'undefined') ? '' : subst.toString();
163 if (minLength > 0 && pad.length > 0)
164 for (var i = 0; i < (minLength - subst.length); i++)
165 subst = justifyRight ? (pad + subst) : (subst + pad);
169 out += leftpart + subst;
170 str = str.substr(m.length);
176 if (!window.location.origin)
177 window.location.origin = '%s//%s%s'.format(
178 window.location.protocol,
179 window.location.hostname,
180 (window.location.port ? ':' + window.location.port : '')
187 var Class = function() { };
189 Class.extend = function(properties)
191 Class.initializing = true;
193 var prototype = new this();
194 var superprot = this.prototype;
196 Class.initializing = false;
198 $.extend(prototype, properties, {
199 callSuper: function() {
201 var meth = arguments[0];
203 if (typeof(superprot[meth]) != 'function')
206 for (var i = 1; i < arguments.length; i++)
207 args.push(arguments[i]);
209 return superprot[meth].apply(this, args);
215 this.options = arguments[0] || { };
217 if (!Class.initializing && typeof(this.init) == 'function')
218 this.init.apply(this, arguments);
221 _class.prototype = prototype;
222 _class.prototype.constructor = _class;
224 _class.extend = Class.extend;
229 Class.require = function(name)
231 var path = '/' + name.replace(/\./g, '/') + '.js';
233 return $.ajax(path, {
238 }).then(function(text) {
239 var code = '%s\n\n//@ sourceURL=%s/%s'.format(text, window.location.origin, path);
240 var construct = eval(code);
242 var parts = name.split(/\./);
243 var cparent = L.Class || (L.Class = { });
245 for (var i = 1; i < parts.length - 1; i++)
247 cparent = cparent[parts[i]];
250 throw "Missing parent class";
253 cparent[parts[i]] = construct;
257 Class.instantiate = function(name)
259 Class.require(name).then(function() {
260 var parts = name.split(/\./);
262 var construct = L.Class;
264 for (var i = 1; i < parts.length - 1; i++)
266 iparent = iparent[parts[i]];
267 construct = construct[parts[i]];
270 throw "Missing parent class";
273 if (construct[parts[i]])
274 iparent[parts[i]] = new construct[parts[i]]();
278 this.defaults = function(obj, def)
281 if (typeof(obj[key]) == 'undefined')
287 this.isDeferred = function(x)
289 return (typeof(x) == 'object' &&
290 typeof(x.then) == 'function' &&
291 typeof(x.promise) == 'function');
294 this.deferrable = function()
296 if (this.isDeferred(arguments[0]))
299 var d = $.Deferred();
300 d.resolve.apply(d, arguments);
309 plural: function(n) { return 0 + (n != 1) },
315 var lang = (navigator.userLanguage || navigator.language || 'en').toLowerCase();
316 var langs = (lang.indexOf('-') > -1) ? [ lang, lang.split(/-/)[0] ] : [ lang ];
318 for (var i = 0; i < langs.length; i++)
319 $.ajax('%s/i18n/base.%s.json'.format(L.globals.resource, langs[i]), {
323 success: function(data) {
324 $.extend(L.i18n.catalog, data);
326 var pe = L.i18n.catalog[''];
329 delete L.i18n.catalog[''];
331 var pf = new Function('n', 'return 0 + (' + pe + ')');
338 L.i18n.loaded = true;
343 this.tr = function(msgid)
347 var msgstr = L.i18n.catalog[msgid];
349 if (typeof(msgstr) == 'undefined')
351 else if (typeof(msgstr) == 'string')
357 this.trp = function(msgid, msgid_plural, count)
361 var msgstr = L.i18n.catalog[msgid];
363 if (typeof(msgstr) == 'undefined')
364 return (count == 1) ? msgid : msgid_plural;
365 else if (typeof(msgstr) == 'string')
368 return msgstr[L.i18n.plural(count)];
371 this.trc = function(msgctx, msgid)
375 var msgstr = L.i18n.catalog[msgid + '\u0004' + msgctx];
377 if (typeof(msgstr) == 'undefined')
379 else if (typeof(msgstr) == 'string')
385 this.trcp = function(msgctx, msgid, msgid_plural, count)
389 var msgstr = L.i18n.catalog[msgid + '\u0004' + msgctx];
391 if (typeof(msgstr) == 'undefined')
392 return (count == 1) ? msgid : msgid_plural;
393 else if (typeof(msgstr) == 'string')
396 return msgstr[L.i18n.plural(count)];
399 this.setHash = function(key, value)
402 var data = this.getHash(undefined);
404 if (typeof(value) == 'undefined')
415 for (var i = 0; i < keys.length; i++)
420 h += keys[i] + ':' + data[keys[i]];
424 location.hash = '#' + h;
429 this.getHash = function(key)
432 var tuples = (location.hash || '#').substring(1).split(/,/);
434 for (var i = 0; i < tuples.length; i++)
436 var tuple = tuples[i].split(/:/);
437 if (tuple.length == 2)
438 data[tuple[0]] = tuple[1];
441 if (typeof(key) != 'undefined')
447 this.toArray = function(x)
457 var l = x.split(/\s+/);
458 for (var i = 0; i < l.length; i++)
467 for (var i = 0; i < x.length; i++)
471 else if ($.isPlainObject(x))
475 if (x.hasOwnProperty(k))
484 this.toObject = function(x)
494 var l = x.split(/\x+/);
495 for (var i = 0; i < l.length; i++)
504 for (var i = 0; i < x.length; i++)
508 else if ($.isPlainObject(x))
517 this.filterArray = function(array, item)
519 if (!$.isArray(array))
522 for (var i = 0; i < array.length; i++)
523 if (array[i] === item)
524 array.splice(i--, 1);
529 this.toClassName = function(str, suffix)
532 var l = str.split(/[\/.]/);
534 for (var i = 0; i < l.length; i++)
536 n += l[i].charAt(0).toUpperCase() + l[i].substr(1).toLowerCase();
538 if (typeof(suffix) == 'string')
544 this.toColor = function(str)
546 if (typeof(str) != 'string' || str.length == 0)
551 else if (str == 'lan')
556 while (i < str.length)
557 hash = str.charCodeAt(i++) + ((hash << 5) - hash);
559 var r = (hash & 0xFF) % 128;
560 var g = ((hash >> 8) & 0xFF) % 128;
570 var b = min + (((hash >> 16) & 0xFF) % (max - min));
572 return '#%02X%02X%02X'.format(0xFF - r, 0xFF - g, 0xFF - b);
575 this.parseIPv4 = function(str)
577 if ((typeof(str) != 'string' && !(str instanceof String)) ||
578 !str.match(/^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}$/))
582 var parts = str.split(/\./);
584 for (var i = 0; i < parts.length; i++)
586 var n = parseInt(parts[i], 10);
587 if (isNaN(n) || n > 255)
596 this.parseIPv6 = function(str)
598 if ((typeof(str) != 'string' && !(str instanceof String)) ||
599 !str.match(/^[a-fA-F0-9:]+(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})?$/))
602 var parts = str.split(/::/);
603 if (parts.length == 0 || parts.length > 2)
607 if (parts[0].length > 0)
609 var left = parts[0].split(/:/);
610 for (var i = 0; i < left.length; i++)
612 var n = parseInt(left[i], 16);
616 lnum.push((n / 256) >> 0);
622 if (parts.length > 1 && parts[1].length > 0)
624 var right = parts[1].split(/:/);
626 for (var i = 0; i < right.length; i++)
628 if (right[i].indexOf('.') > 0)
630 var addr = L.parseIPv4(right[i]);
634 rnum.push.apply(rnum, addr);
638 var n = parseInt(right[i], 16);
642 rnum.push((n / 256) >> 0);
647 if (rnum.length > 0 && (lnum.length + rnum.length) > 15)
652 num.push.apply(num, lnum);
654 for (var i = 0; i < (16 - lnum.length - rnum.length); i++)
657 num.push.apply(num, rnum);
665 this.isNetmask = function(addr)
667 if (!$.isArray(addr))
672 for (c = 0; (c < addr.length) && (addr[c] == 255); c++);
674 if (c == addr.length)
677 if ((addr[c] == 254) || (addr[c] == 252) || (addr[c] == 248) ||
678 (addr[c] == 240) || (addr[c] == 224) || (addr[c] == 192) ||
679 (addr[c] == 128) || (addr[c] == 0))
681 for (c++; (c < addr.length) && (addr[c] == 0); c++);
683 if (c == addr.length)
693 sid: '00000000000000000000000000000000'
702 _call: function(req, cb)
704 return $.ajax('/ubus', {
706 contentType: 'application/json',
707 data: JSON.stringify(req),
710 timeout: L.globals.timeout,
715 _list_cb: function(msg)
717 var list = msg.result;
719 /* verify message frame */
720 if (typeof(msg) != 'object' || msg.jsonrpc != '2.0' || !msg.id || !$.isArray(list))
723 return $.Deferred().resolveWith(this, [ list ]);
726 _call_cb: function(msg)
729 var type = Object.prototype.toString;
730 var reqs = this._rpc_req;
732 if (!$.isArray(reqs))
738 for (var i = 0; i < msg.length; i++)
740 /* fetch related request info */
741 var req = L.rpc._requests[reqs[i].id];
742 if (typeof(req) != 'object')
743 throw 'No related request for JSON response';
745 /* fetch response attribute and verify returned type */
748 /* verify message frame */
749 if (typeof(msg[i]) == 'object' && msg[i].jsonrpc == '2.0')
750 if ($.isArray(msg[i].result) && msg[i].result[0] == 0)
751 ret = (msg[i].result.length > 1) ? msg[i].result[1] : msg[i].result[0];
755 for (var key in req.expect)
757 if (typeof(ret) != 'undefined' && key != '')
760 if (typeof(ret) == 'undefined' || type.call(ret) != type.call(req.expect[key]))
761 ret = req.expect[key];
768 if (typeof(req.filter) == 'function')
771 req.priv[1] = req.params;
772 ret = req.filter.apply(L.rpc, req.priv);
775 /* store response data */
776 if (typeof(req.index) == 'number')
777 data[req.index] = ret;
781 /* delete request object */
782 delete L.rpc._requests[reqs[i].id];
785 return $.Deferred().resolveWith(this, [ data ]);
791 for (var i = 0; i < arguments.length; i++)
792 params[i] = arguments[i];
798 params: (params.length > 0) ? params : undefined
801 return this._call(msg, this._list_cb);
806 if (!$.isArray(this._batch))
812 if (!$.isArray(this._batch))
813 return L.deferrable([ ]);
815 var req = this._batch;
819 return this._call(req, this._call_cb);
822 declare: function(options)
827 /* build parameter object */
830 if ($.isArray(options.params))
831 for (p_off = 0; p_off < options.params.length; p_off++)
832 params[options.params[p_off]] = arguments[p_off];
834 /* all remaining arguments are private args */
835 var priv = [ undefined, undefined ];
836 for (; p_off < arguments.length; p_off++)
837 priv.push(arguments[p_off]);
839 /* store request info */
840 var req = _rpc._requests[_rpc._id] = {
841 expect: options.expect,
842 filter: options.filter,
847 /* build message object */
860 /* when a batch is in progress then store index in request data
861 * and push message object onto the stack */
862 if ($.isArray(_rpc._batch))
864 req.index = _rpc._batch.push(msg) - 1;
865 return L.deferrable(msg);
869 return _rpc._call(msg, _rpc._call_cb);
874 this.UCIContext = Class.extend({
888 callLoad: L.rpc.declare({
891 params: [ 'config' ],
892 expect: { values: { } }
895 callOrder: L.rpc.declare({
898 params: [ 'config', 'sections' ]
901 callAdd: L.rpc.declare({
904 params: [ 'config', 'type', 'name', 'values' ],
905 expect: { section: '' }
908 callSet: L.rpc.declare({
911 params: [ 'config', 'section', 'values' ]
914 callDelete: L.rpc.declare({
917 params: [ 'config', 'section', 'options' ]
920 callApply: L.rpc.declare({
923 params: [ 'timeout', 'rollback' ]
926 callConfirm: L.rpc.declare({
931 createSID: function(conf)
933 var v = this.state.values;
934 var n = this.state.creates;
938 sid = "new%06x".format(Math.random() * 0xFFFFFF);
939 } while ((n[conf] && n[conf][sid]) || (v[conf] && v[conf][sid]));
944 reorderSections: function()
946 var v = this.state.values;
947 var n = this.state.creates;
948 var r = this.state.reorder;
950 if ($.isEmptyObject(r))
951 return L.deferrable();
956 gather all created and existing sections, sort them according
957 to their index value and issue an uci order call
972 o.sort(function(a, b) {
973 return (a['.index'] - b['.index']);
978 for (var i = 0; i < o.length; i++)
979 sids.push(o[i]['.name']);
981 this.callOrder(c, sids);
985 this.state.reorder = { };
986 return L.rpc.flush();
989 load: function(packages)
995 if (!$.isArray(packages))
996 packages = [ packages ];
1000 for (var i = 0; i < packages.length; i++)
1001 if (!seen[packages[i]] && !self.state.values[packages[i]])
1003 pkgs.push(packages[i]);
1004 seen[packages[i]] = true;
1005 self.callLoad(packages[i]);
1008 return L.rpc.flush().then(function(responses) {
1009 for (var i = 0; i < responses.length; i++)
1010 self.state.values[pkgs[i]] = responses[i];
1016 unload: function(packages)
1018 if (!$.isArray(packages))
1019 packages = [ packages ];
1021 for (var i = 0; i < packages.length; i++)
1023 delete this.state.values[packages[i]];
1024 delete this.state.creates[packages[i]];
1025 delete this.state.changes[packages[i]];
1026 delete this.state.deletes[packages[i]];
1030 add: function(conf, type, name)
1032 var n = this.state.creates;
1033 var sid = name || this.createSID(conf);
1042 '.anonymous': !name,
1043 '.index': 1000 + this.state.newidx++
1049 remove: function(conf, sid)
1051 var n = this.state.creates;
1052 var c = this.state.changes;
1053 var d = this.state.deletes;
1055 /* requested deletion of a just created section */
1056 if (n[conf] && n[conf][sid])
1058 delete n[conf][sid];
1063 delete c[conf][sid];
1068 d[conf][sid] = true;
1072 sections: function(conf, type, cb)
1075 var v = this.state.values[conf];
1076 var n = this.state.creates[conf];
1077 var c = this.state.changes[conf];
1078 var d = this.state.deletes[conf];
1084 if (!d || d[s] !== true)
1085 if (!type || v[s]['.type'] == type)
1086 sa.push($.extend({ }, v[s], c ? c[s] : undefined));
1090 if (!type || n[s]['.type'] == type)
1093 sa.sort(function(a, b) {
1094 return a['.index'] - b['.index'];
1097 for (var i = 0; i < sa.length; i++)
1098 sa[i]['.index'] = i;
1100 if (typeof(cb) == 'function')
1101 for (var i = 0; i < sa.length; i++)
1102 cb.call(this, sa[i], sa[i]['.name']);
1107 get: function(conf, sid, opt)
1109 var v = this.state.values;
1110 var n = this.state.creates;
1111 var c = this.state.changes;
1112 var d = this.state.deletes;
1114 if (typeof(sid) == 'undefined')
1117 /* requested option in a just created section */
1118 if (n[conf] && n[conf][sid])
1123 if (typeof(opt) == 'undefined')
1124 return n[conf][sid];
1126 return n[conf][sid][opt];
1129 /* requested an option value */
1130 if (typeof(opt) != 'undefined')
1132 /* check whether option was deleted */
1133 if (d[conf] && d[conf][sid])
1135 if (d[conf][sid] === true)
1138 for (var i = 0; i < d[conf][sid].length; i++)
1139 if (d[conf][sid][i] == opt)
1143 /* check whether option was changed */
1144 if (c[conf] && c[conf][sid] && typeof(c[conf][sid][opt]) != 'undefined')
1145 return c[conf][sid][opt];
1147 /* return base value */
1148 if (v[conf] && v[conf][sid])
1149 return v[conf][sid][opt];
1154 /* requested an entire section */
1156 return v[conf][sid];
1161 set: function(conf, sid, opt, val)
1163 var v = this.state.values;
1164 var n = this.state.creates;
1165 var c = this.state.changes;
1166 var d = this.state.deletes;
1168 if (typeof(sid) == 'undefined' ||
1169 typeof(opt) == 'undefined' ||
1170 opt.charAt(0) == '.')
1173 if (n[conf] && n[conf][sid])
1175 if (typeof(val) != 'undefined')
1176 n[conf][sid][opt] = val;
1178 delete n[conf][sid][opt];
1180 else if (typeof(val) != 'undefined')
1182 /* do not set within deleted section */
1183 if (d[conf] && d[conf][sid] === true)
1186 /* only set in existing sections */
1187 if (!v[conf] || !v[conf][sid])
1196 /* undelete option */
1197 if (d[conf] && d[conf][sid])
1198 d[conf][sid] = L.filterArray(d[conf][sid], opt);
1200 c[conf][sid][opt] = val;
1204 /* only delete in existing sections */
1205 if (!v[conf] || !v[conf][sid])
1214 if (d[conf][sid] !== true)
1215 d[conf][sid].push(opt);
1219 unset: function(conf, sid, opt)
1221 return this.set(conf, sid, opt, undefined);
1224 get_first: function(conf, type, opt)
1226 var sid = undefined;
1228 L.uci.sections(conf, type, function(s) {
1229 if (typeof(sid) != 'string')
1233 return this.get(conf, sid, opt);
1236 set_first: function(conf, type, opt, val)
1238 var sid = undefined;
1240 L.uci.sections(conf, type, function(s) {
1241 if (typeof(sid) != 'string')
1245 return this.set(conf, sid, opt, val);
1248 unset_first: function(conf, type, opt)
1250 return this.set_first(conf, type, opt, undefined);
1253 swap: function(conf, sid1, sid2)
1255 var s1 = this.get(conf, sid1);
1256 var s2 = this.get(conf, sid2);
1257 var n1 = s1 ? s1['.index'] : NaN;
1258 var n2 = s2 ? s2['.index'] : NaN;
1260 if (isNaN(n1) || isNaN(n2))
1266 this.state.reorder[conf] = true;
1275 var v = this.state.values;
1276 var n = this.state.creates;
1277 var c = this.state.changes;
1278 var d = this.state.deletes;
1287 for (var sid in n[conf])
1294 for (var k in n[conf][sid])
1297 r.type = n[conf][sid][k];
1298 else if (k == '.create')
1299 r.name = n[conf][sid][k];
1300 else if (k.charAt(0) != '.')
1301 r.values[k] = n[conf][sid][k];
1304 snew.push(n[conf][sid]);
1306 self.callAdd(r.config, r.type, r.name, r.values);
1315 for (var sid in c[conf])
1316 self.callSet(conf, sid, c[conf][sid]);
1324 for (var sid in d[conf])
1326 var o = d[conf][sid];
1327 self.callDelete(conf, sid, (o === true) ? undefined : o);
1333 return L.rpc.flush().then(function(responses) {
1335 array "snew" holds references to the created uci sections,
1336 use it to assign the returned names of the new sections
1338 for (var i = 0; i < snew.length; i++)
1339 snew[i]['.name'] = responses[i];
1341 return self.reorderSections();
1342 }).then(function() {
1343 pkgs = L.toArray(pkgs);
1347 return self.load(pkgs);
1351 apply: function(timeout)
1354 var date = new Date();
1355 var deferred = $.Deferred();
1357 if (typeof(timeout) != 'number' || timeout < 1)
1360 self.callApply(timeout, true).then(function(rv) {
1363 deferred.rejectWith(self, [ rv ]);
1367 var try_deadline = date.getTime() + 1000 * timeout;
1368 var try_confirm = function()
1370 return self.callConfirm().then(function(rv) {
1373 if (date.getTime() < try_deadline)
1374 window.setTimeout(try_confirm, 250);
1376 deferred.rejectWith(self, [ rv ]);
1381 deferred.resolveWith(self, [ rv ]);
1385 window.setTimeout(try_confirm, 1000);
1391 changes: L.rpc.declare({
1394 expect: { changes: { } }
1397 readable: function(conf)
1399 return L.session.hasACL('uci', conf, 'read');
1402 writable: function(conf)
1404 return L.session.hasACL('uci', conf, 'write');
1408 this.uci = new this.UCIContext();
1411 listDeviceNames: L.rpc.declare({
1414 expect: { 'devices': [ ] },
1415 filter: function(data) {
1421 getDeviceStatus: L.rpc.declare({
1424 params: [ 'device' ],
1425 expect: { '': { } },
1426 filter: function(data, params) {
1427 if (!$.isEmptyObject(data))
1429 data['device'] = params['device'];
1436 getAssocList: L.rpc.declare({
1438 method: 'assoclist',
1439 params: [ 'device' ],
1440 expect: { results: [ ] },
1441 filter: function(data, params) {
1442 for (var i = 0; i < data.length; i++)
1443 data[i]['device'] = params['device'];
1445 data.sort(function(a, b) {
1446 if (a.bssid < b.bssid)
1448 else if (a.bssid > b.bssid)
1458 getWirelessStatus: function() {
1459 return this.listDeviceNames().then(function(names) {
1462 for (var i = 0; i < names.length; i++)
1463 L.wireless.getDeviceStatus(names[i]);
1465 return L.rpc.flush();
1466 }).then(function(networks) {
1470 'country', 'channel', 'frequency', 'frequency_offset',
1471 'txpower', 'txpower_offset', 'hwmodes', 'hardware', 'phy'
1475 'ssid', 'bssid', 'mode', 'quality', 'quality_max',
1476 'signal', 'noise', 'bitrate', 'encryption'
1479 for (var i = 0; i < networks.length; i++)
1481 var phy = rv[networks[i].phy] || (
1482 rv[networks[i].phy] = { networks: [ ] }
1486 device: networks[i].device
1489 for (var j = 0; j < phy_attrs.length; j++)
1490 phy[phy_attrs[j]] = networks[i][phy_attrs[j]];
1492 for (var j = 0; j < net_attrs.length; j++)
1493 net[net_attrs[j]] = networks[i][net_attrs[j]];
1495 phy.networks.push(net);
1502 getAssocLists: function()
1504 return this.listDeviceNames().then(function(names) {
1507 for (var i = 0; i < names.length; i++)
1508 L.wireless.getAssocList(names[i]);
1510 return L.rpc.flush();
1511 }).then(function(assoclists) {
1514 for (var i = 0; i < assoclists.length; i++)
1515 for (var j = 0; j < assoclists[i].length; j++)
1516 rv.push(assoclists[i][j]);
1522 formatEncryption: function(enc)
1524 var format_list = function(l, s)
1527 for (var i = 0; i < l.length; i++)
1528 rv.push(l[i].toUpperCase());
1529 return rv.join(s ? s : ', ');
1532 if (!enc || !enc.enabled)
1533 return L.tr('None');
1537 if (enc.wep.length == 2)
1538 return L.tr('WEP Open/Shared') + ' (%s)'.format(format_list(enc.ciphers, ', '));
1539 else if (enc.wep[0] == 'shared')
1540 return L.tr('WEP Shared Auth') + ' (%s)'.format(format_list(enc.ciphers, ', '));
1542 return L.tr('WEP Open System') + ' (%s)'.format(format_list(enc.ciphers, ', '));
1546 if (enc.wpa.length == 2)
1547 return L.tr('mixed WPA/WPA2') + ' %s (%s)'.format(
1548 format_list(enc.authentication, '/'),
1549 format_list(enc.ciphers, ', ')
1551 else if (enc.wpa[0] == 2)
1552 return 'WPA2 %s (%s)'.format(
1553 format_list(enc.authentication, '/'),
1554 format_list(enc.ciphers, ', ')
1557 return 'WPA %s (%s)'.format(
1558 format_list(enc.authentication, '/'),
1559 format_list(enc.ciphers, ', ')
1563 return L.tr('Unknown');
1568 getZoneColor: function(zone)
1570 if ($.isPlainObject(zone))
1575 else if (zone == 'wan')
1578 for (var i = 0, hash = 0;
1580 hash = zone.charCodeAt(i++) + ((hash << 5) - hash));
1582 for (var i = 0, color = '#';
1584 color += ('00' + ((hash >> i++ * 8) & 0xFF).tostring(16)).slice(-2));
1589 findZoneByNetwork: function(network)
1592 var zone = undefined;
1594 return L.uci.sections('firewall', 'zone', function(z) {
1595 if (!z.name || !z.network)
1598 if (!$.isArray(z.network))
1599 z.network = z.network.split(/\s+/);
1601 for (var i = 0; i < z.network.length; i++)
1603 if (z.network[i] == network)
1609 }).then(function() {
1611 zone.color = self.getZoneColor(zone);
1618 this.NetworkModel = {
1625 /^wlan[0-9]+\.sta[0-9]+$/
1628 rpcCacheFunctions: [
1629 'protolist', 0, L.rpc.declare({
1631 method: 'get_proto_handlers',
1634 'ifstate', 1, L.rpc.declare({
1635 object: 'network.interface',
1637 expect: { 'interface': [ ] }
1639 'devstate', 2, L.rpc.declare({
1640 object: 'network.device',
1644 'wifistate', 0, L.rpc.declare({
1645 object: 'network.wireless',
1649 'bwstate', 2, L.rpc.declare({
1650 object: 'luci2.network.bwmon',
1651 method: 'statistics',
1652 expect: { 'statistics': { } }
1654 'devlist', 2, L.rpc.declare({
1655 object: 'luci2.network',
1656 method: 'device_list',
1657 expect: { 'devices': [ ] }
1659 'swlist', 0, L.rpc.declare({
1660 object: 'luci2.network',
1661 method: 'switch_list',
1662 expect: { 'switches': [ ] }
1666 loadProtocolHandler: function(proto)
1668 var url = L.globals.resource + '/proto/' + proto + '.js';
1669 var self = L.NetworkModel;
1671 var def = $.Deferred();
1677 }).then(function(data) {
1679 var protoConstructorSource = (
1680 '(function(L, $) { ' +
1684 ).format(data, url);
1686 var protoClass = eval(protoConstructorSource);
1688 self.protocolHandlers[proto] = new protoClass();
1691 alert('Unable to instantiate proto "%s": %s'.format(url, e));
1695 }).fail(function() {
1702 loadProtocolHandlers: function()
1704 var self = L.NetworkModel;
1706 self.loadProtocolHandler('none')
1709 for (var proto in self.rpcCache.protolist)
1710 deferreds.push(self.loadProtocolHandler(proto));
1712 return $.when.apply($, deferreds);
1715 callSwitchInfo: L.rpc.declare({
1716 object: 'luci2.network',
1717 method: 'switch_info',
1718 params: [ 'switch' ],
1719 expect: { 'info': { } }
1722 callSwitchInfoCallback: function(responses) {
1723 var self = L.NetworkModel;
1724 var swlist = self.rpcCache.swlist;
1725 var swstate = self.rpcCache.swstate = { };
1727 for (var i = 0; i < responses.length; i++)
1728 swstate[swlist[i]] = responses[i];
1731 loadCacheCallback: function(level)
1733 var self = L.NetworkModel;
1734 var name = '_fetch_cache_cb_' + level;
1736 return self[name] || (
1737 self[name] = function(responses)
1739 for (var i = 0; i < self.rpcCacheFunctions.length; i += 3)
1740 if (!level || self.rpcCacheFunctions[i + 1] == level)
1741 self.rpcCache[self.rpcCacheFunctions[i]] = responses.shift();
1747 for (var i = 0; i < self.rpcCache.swlist.length; i++)
1748 self.callSwitchInfo(self.rpcCache.swlist[i]);
1750 return L.rpc.flush().then(self.callSwitchInfoCallback);
1753 return L.deferrable();
1758 loadCache: function(level)
1760 var self = L.NetworkModel;
1762 return L.uci.load(['network', 'wireless']).then(function() {
1765 for (var i = 0; i < self.rpcCacheFunctions.length; i += 3)
1766 if (!level || self.rpcCacheFunctions[i + 1] == level)
1767 self.rpcCacheFunctions[i + 2]();
1769 return L.rpc.flush().then(self.loadCacheCallback(level || 0));
1773 isBlacklistedDevice: function(dev)
1775 for (var i = 0; i < this.deviceBlacklist.length; i++)
1776 if (dev.match(this.deviceBlacklist[i]))
1782 sortDevicesCallback: function(a, b)
1784 if (a.options.kind < b.options.kind)
1786 else if (a.options.kind > b.options.kind)
1789 if (a.options.name < b.options.name)
1791 else if (a.options.name > b.options.name)
1797 getDeviceObject: function(ifname)
1799 var alias = (ifname.charAt(0) == '@');
1800 return this.deviceObjects[ifname] || (
1801 this.deviceObjects[ifname] = {
1803 kind: alias ? 'alias' : 'ethernet',
1804 type: alias ? 0 : 1,
1811 getInterfaceObject: function(name)
1813 return this.interfaceObjects[name] || (
1814 this.interfaceObjects[name] = {
1816 proto: this.protocolHandlers.none,
1822 loadDevicesCallback: function()
1824 var self = L.NetworkModel;
1825 var wificount = { };
1827 for (var ifname in self.rpcCache.devstate)
1829 if (self.isBlacklistedDevice(ifname))
1832 var dev = self.rpcCache.devstate[ifname];
1833 var entry = self.getDeviceObject(ifname);
1840 entry.kind = 'tunnel';
1844 entry.kind = 'bridge';
1845 //entry.ports = dev['bridge-members'].sort();
1850 for (var i = 0; i < self.rpcCache.devlist.length; i++)
1852 var dev = self.rpcCache.devlist[i];
1854 if (self.isBlacklistedDevice(dev.device))
1857 var entry = self.getDeviceObject(dev.device);
1859 entry.up = dev.is_up;
1860 entry.type = dev.type;
1864 case 1: /* Ethernet */
1866 entry.kind = 'bridge';
1867 else if (dev.is_tuntap)
1868 entry.kind = 'tunnel';
1869 else if (dev.is_wireless)
1870 entry.kind = 'wifi';
1874 case 768: /* IP-IP Tunnel */
1875 case 769: /* IP6-IP6 Tunnel */
1876 case 776: /* IPv6-in-IPv4 */
1877 case 778: /* GRE over IP */
1878 entry.kind = 'tunnel';
1883 var net = L.uci.sections('network');
1884 for (var i = 0; i < net.length; i++)
1887 var sid = s['.name'];
1889 if (s['.type'] == 'device' && s.name)
1891 var entry = self.getDeviceObject(s.name);
1897 entry.kind = 'tunnel';
1903 else if (s['.type'] == 'interface' && !s['.anonymous'] && s.ifname)
1905 var ifnames = L.toArray(s.ifname);
1907 for (var j = 0; j < ifnames.length; j++)
1908 self.getDeviceObject(ifnames[j]);
1910 if (s['.name'] != 'loopback')
1912 var entry = self.getDeviceObject('@%s'.format(s['.name']));
1915 entry.kind = 'alias';
1919 else if (s['.type'] == 'switch_vlan' && s.device)
1921 var sw = self.rpcCache.swstate[s.device];
1922 var vid = parseInt(s.vid || s.vlan);
1923 var ports = L.toArray(s.ports);
1925 if (!sw || !ports.length || isNaN(vid))
1928 var ifname = undefined;
1930 for (var j = 0; j < ports.length; j++)
1932 var port = parseInt(ports[j]);
1933 var tag = (ports[j].replace(/[^tu]/g, '') == 't');
1935 if (port == sw.cpu_port)
1937 // XXX: need a way to map switch to netdev
1939 ifname = 'eth0.%d'.format(vid);
1950 var entry = self.getDeviceObject(ifname);
1952 entry.kind = 'vlan';
1959 var wifi = L.uci.sections('wireless');
1960 for (var i = 0; i < wifi.length; i++)
1963 var sid = s['.name'];
1965 if (s['.type'] == 'wifi-iface' && s.device)
1967 var r = parseInt(s.device.replace(/^[^0-9]+/, ''));
1968 var n = wificount[s.device] = (wificount[s.device] || 0) + 1;
1969 var id = 'radio%d.network%d'.format(r, n);
1972 if (self.rpcCache.wifistate[s.device])
1974 var ifcs = self.rpcCache.wifistate[s.device].interfaces;
1975 for (var ifc in ifcs)
1977 if (ifcs[ifc].section == sid && ifcs[ifc].ifname)
1979 ifname = ifcs[ifc].ifname;
1985 var entry = self.getDeviceObject(ifname);
1987 entry.kind = 'wifi';
1990 entry.wdev = s.device;
1991 entry.wmode = s.mode;
1992 entry.wssid = s.ssid;
1993 entry.wbssid = s.bssid;
1997 for (var i = 0; i < net.length; i++)
2000 var sid = s['.name'];
2002 if (s['.type'] == 'interface' && !s['.anonymous'] && s.type == 'bridge')
2004 var ifnames = L.toArray(s.ifname);
2006 for (var ifname in self.deviceObjects)
2008 var dev = self.deviceObjects[ifname];
2010 if (dev.kind != 'wifi')
2013 var wnets = L.toArray(L.uci.get('wireless', dev.sid, 'network'));
2014 if ($.inArray(sid, wnets) > -1)
2015 ifnames.push(ifname);
2018 entry = self.getDeviceObject('br-%s'.format(s['.name']));
2020 entry.kind = 'bridge';
2022 entry.ports = ifnames.sort();
2027 loadInterfacesCallback: function()
2029 var self = L.NetworkModel;
2030 var net = L.uci.sections('network');
2032 for (var i = 0; i < net.length; i++)
2035 var sid = s['.name'];
2037 if (s['.type'] == 'interface' && !s['.anonymous'] && s.proto)
2039 var entry = self.getInterfaceObject(s['.name']);
2040 var proto = self.protocolHandlers[s.proto] || self.protocolHandlers.none;
2042 var l3dev = undefined;
2043 var l2dev = undefined;
2045 var ifnames = L.toArray(s.ifname);
2047 for (var ifname in self.deviceObjects)
2049 var dev = self.deviceObjects[ifname];
2051 if (dev.kind != 'wifi')
2054 var wnets = L.toArray(L.uci.get('wireless', dev.sid, 'network'));
2055 if ($.inArray(entry.name, wnets) > -1)
2056 ifnames.push(ifname);
2060 l3dev = '%s-%s'.format(s.proto, entry.name);
2061 else if (s.type == 'bridge')
2062 l3dev = 'br-%s'.format(entry.name);
2066 if (!proto.virtual && s.type == 'bridge')
2067 l2dev = 'br-%s'.format(entry.name);
2068 else if (!proto.virtual)
2071 entry.proto = proto;
2073 entry.l3dev = l3dev;
2074 entry.l2dev = l2dev;
2078 for (var i = 0; i < self.rpcCache.ifstate.length; i++)
2080 var iface = self.rpcCache.ifstate[i];
2081 var entry = self.getInterfaceObject(iface['interface']);
2082 var proto = self.protocolHandlers[iface.proto] || self.protocolHandlers.none;
2084 /* this is a virtual interface, either deleted from config but
2085 not applied yet or set up from external tools (6rd) */
2088 entry.proto = proto;
2089 entry.l2dev = iface.device;
2090 entry.l3dev = iface.l3_device;
2100 return L.deferrable();
2102 self.rpcCache = { };
2103 self.deviceObjects = { };
2104 self.interfaceObjects = { };
2105 self.protocolHandlers = { };
2107 return self.loadCache()
2108 .then(self.loadProtocolHandlers)
2109 .then(self.loadDevicesCallback)
2110 .then(self.loadInterfacesCallback);
2115 delete this.rpcCache;
2119 refreshInterfaceStatus: function()
2121 return this.loadCache(1).then(this.loadInterfacesCallback);
2124 refreshDeviceStatus: function()
2126 return this.loadCache(2).then(this.loadDevicesCallback);
2129 refreshStatus: function()
2131 return this.loadCache(1)
2132 .then(this.loadCache(2))
2133 .then(this.loadDevicesCallback)
2134 .then(this.loadInterfacesCallback);
2137 getDevices: function()
2141 for (var ifname in this.deviceObjects)
2143 devs.push(new L.NetworkModel.Device(this.deviceObjects[ifname]));
2145 return devs.sort(this.sortDevicesCallback);
2148 getDeviceByInterface: function(iface)
2150 if (iface instanceof L.NetworkModel.Interface)
2151 iface = iface.name();
2153 if (this.interfaceObjects[iface])
2154 return this.getDevice(this.interfaceObjects[iface].l3dev) ||
2155 this.getDevice(this.interfaceObjects[iface].l2dev);
2160 getDevice: function(ifname)
2162 if (this.deviceObjects[ifname])
2163 return new L.NetworkModel.Device(this.deviceObjects[ifname]);
2168 createDevice: function(name)
2170 return new L.NetworkModel.Device(this.getDeviceObject(name));
2173 getInterfaces: function()
2177 for (var name in this.interfaceObjects)
2178 if (name != 'loopback')
2179 ifaces.push(this.getInterface(name));
2181 ifaces.sort(function(a, b) {
2182 if (a.name() < b.name())
2184 else if (a.name() > b.name())
2193 getInterfacesByDevice: function(dev)
2197 if (dev instanceof L.NetworkModel.Device)
2200 for (var name in this.interfaceObjects)
2202 var iface = this.interfaceObjects[name];
2203 if (iface.l2dev == dev || iface.l3dev == dev)
2204 ifaces.push(this.getInterface(name));
2207 ifaces.sort(function(a, b) {
2208 if (a.name() < b.name())
2210 else if (a.name() > b.name())
2219 getInterface: function(iface)
2221 if (this.interfaceObjects[iface])
2222 return new L.NetworkModel.Interface(this.interfaceObjects[iface]);
2227 getProtocols: function()
2231 for (var proto in this.protocolHandlers)
2233 var pr = this.protocolHandlers[proto];
2237 description: pr.description,
2238 virtual: pr.virtual,
2243 return rv.sort(function(a, b) {
2244 if (a.name < b.name)
2246 else if (a.name > b.name)
2253 findWANByAddr: function(ipaddr)
2255 for (var i = 0; i < this.rpcCache.ifstate.length; i++)
2257 var ifstate = this.rpcCache.ifstate[i];
2262 for (var j = 0; j < ifstate.route.length; j++)
2263 if (ifstate.route[j].mask == 0 &&
2264 ifstate.route[j].target == ipaddr &&
2265 typeof(ifstate.route[j].table) == 'undefined')
2267 return this.getInterface(ifstate['interface']);
2276 return this.findWANByAddr('0.0.0.0');
2279 findWAN6: function()
2281 return this.findWANByAddr('::');
2284 resolveAlias: function(ifname)
2286 if (ifname instanceof L.NetworkModel.Device)
2287 ifname = ifname.name();
2289 var dev = this.deviceObjects[ifname];
2292 while (dev && dev.kind == 'alias')
2295 if (seen[dev.ifname])
2298 var ifc = this.interfaceObjects[dev.sid];
2300 seen[dev.ifname] = true;
2301 dev = ifc ? this.deviceObjects[ifc.l3dev] : undefined;
2304 return dev ? this.getDevice(dev.ifname) : undefined;
2308 this.NetworkModel.Device = Class.extend({
2311 sta: L.tr('Client'),
2312 adhoc: L.tr('Ad-Hoc'),
2313 monitor: L.tr('Monitor'),
2314 wds: L.tr('Static WDS')
2317 getStatus: function(key)
2319 var s = L.NetworkModel.rpcCache.devstate[this.options.ifname];
2322 return key ? s[key] : s;
2329 var sid = this.options.sid;
2330 var pkg = (this.options.kind == 'wifi') ? 'wireless' : 'network';
2331 return L.uci.get(pkg, sid, key);
2334 set: function(key, val)
2336 var sid = this.options.sid;
2337 var pkg = (this.options.kind == 'wifi') ? 'wireless' : 'network';
2338 return L.uci.set(pkg, sid, key, val);
2343 if (typeof(this.options.type) == 'undefined')
2344 this.options.type = 1;
2346 if (typeof(this.options.kind) == 'undefined')
2347 this.options.kind = 'ethernet';
2349 if (typeof(this.options.networks) == 'undefined')
2350 this.options.networks = [ ];
2355 return this.options.ifname;
2358 description: function()
2360 switch (this.options.kind)
2363 return L.tr('Alias for network "%s"').format(this.options.ifname.substring(1));
2366 return L.tr('Network bridge');
2369 return L.tr('Network device');
2372 switch (this.options.type)
2374 case 1: /* tuntap */
2375 return L.tr('TAP device');
2378 return L.tr('PPP tunnel');
2380 case 768: /* IP-IP Tunnel */
2381 return L.tr('IP-in-IP tunnel');
2383 case 769: /* IP6-IP6 Tunnel */
2384 return L.tr('IPv6-in-IPv6 tunnel');
2386 case 776: /* IPv6-in-IPv4 */
2387 return L.tr('IPv6-over-IPv4 tunnel');
2390 case 778: /* GRE over IP */
2391 return L.tr('GRE-over-IP tunnel');
2394 return L.tr('Tunnel device');
2398 return L.tr('VLAN %d on %s').format(this.options.vid, this.options.vsw.model);
2401 var o = this.options;
2402 return L.trc('(Wifi-Mode) "(SSID)" on (radioX)', '%s "%h" on %s').format(
2403 o.wmode ? this.wifiModeStrings[o.wmode] : L.tr('Unknown mode'),
2404 o.wssid || '?', o.wdev
2408 return L.tr('Unknown device');
2413 var kind = this.options.kind;
2415 if (kind == 'alias')
2418 if (typeof(up) == 'undefined')
2421 return L.globals.resource + '/icons/%s%s.png'.format(kind, up ? '' : '_disabled');
2426 var l = L.NetworkModel.rpcCache.devlist;
2428 for (var i = 0; i < l.length; i++)
2429 if (l[i].device == this.options.ifname)
2430 return (l[i].is_up === true);
2437 return (this.options.kind == 'alias');
2440 isBridge: function()
2442 return (this.options.kind == 'bridge');
2445 isBridgeable: function()
2447 return (this.options.type == 1 && this.options.kind != 'bridge');
2450 isWireless: function()
2452 return (this.options.kind == 'wifi');
2455 isInNetwork: function(net)
2457 if (!(net instanceof L.NetworkModel.Interface))
2458 net = L.NetworkModel.getInterface(net);
2462 if (net.options.l3dev == this.options.ifname ||
2463 net.options.l2dev == this.options.ifname)
2466 var dev = L.NetworkModel.deviceObjects[net.options.l2dev];
2467 if (dev && dev.kind == 'bridge' && dev.ports)
2468 return ($.inArray(this.options.ifname, dev.ports) > -1);
2476 var dev = L.NetworkModel.rpcCache.devstate[this.options.ifname];
2477 if (dev && !isNaN(dev.mtu))
2483 getMACAddress: function()
2485 if (this.options.type != 1)
2488 var dev = L.NetworkModel.rpcCache.devstate[this.options.ifname];
2489 if (dev && dev.macaddr)
2490 return dev.macaddr.toUpperCase();
2495 getInterfaces: function()
2497 return L.NetworkModel.getInterfacesByDevice(this.options.name);
2500 getStatistics: function()
2502 var s = this.getStatus('statistics') || { };
2504 rx_bytes: (s.rx_bytes || 0),
2505 tx_bytes: (s.tx_bytes || 0),
2506 rx_packets: (s.rx_packets || 0),
2507 tx_packets: (s.tx_packets || 0)
2511 getTrafficHistory: function()
2513 var def = new Array(120);
2515 for (var i = 0; i < 120; i++)
2518 var h = L.NetworkModel.rpcCache.bwstate[this.options.ifname] || { };
2520 rx_bytes: (h.rx_bytes || def),
2521 tx_bytes: (h.tx_bytes || def),
2522 rx_packets: (h.rx_packets || def),
2523 tx_packets: (h.tx_packets || def)
2527 removeFromInterface: function(iface)
2529 if (!(iface instanceof L.NetworkModel.Interface))
2530 iface = L.NetworkModel.getInterface(iface);
2535 var ifnames = L.toArray(iface.get('ifname'));
2536 if ($.inArray(this.options.ifname, ifnames) > -1)
2537 iface.set('ifname', L.filterArray(ifnames, this.options.ifname));
2539 if (this.options.kind != 'wifi')
2542 var networks = L.toArray(this.get('network'));
2543 if ($.inArray(iface.name(), networks) > -1)
2544 this.set('network', L.filterArray(networks, iface.name()));
2547 attachToInterface: function(iface)
2549 if (!(iface instanceof L.NetworkModel.Interface))
2550 iface = L.NetworkModel.getInterface(iface);
2555 if (this.options.kind != 'wifi')
2557 var ifnames = L.toArray(iface.get('ifname'));
2558 if ($.inArray(this.options.ifname, ifnames) < 0)
2560 ifnames.push(this.options.ifname);
2561 iface.set('ifname', (ifnames.length > 1) ? ifnames : ifnames[0]);
2566 var networks = L.toArray(this.get('network'));
2567 if ($.inArray(iface.name(), networks) < 0)
2569 networks.push(iface.name());
2570 this.set('network', (networks.length > 1) ? networks : networks[0]);
2576 this.NetworkModel.Interface = Class.extend({
2577 getStatus: function(key)
2579 var s = L.NetworkModel.rpcCache.ifstate;
2581 for (var i = 0; i < s.length; i++)
2582 if (s[i]['interface'] == this.options.name)
2583 return key ? s[i][key] : s[i];
2590 return L.uci.get('network', this.options.name, key);
2593 set: function(key, val)
2595 return L.uci.set('network', this.options.name, key, val);
2600 return this.options.name;
2603 protocol: function()
2605 return (this.get('proto') || 'none');
2610 return (this.getStatus('up') === true);
2613 isVirtual: function()
2615 return (typeof(this.options.sid) != 'string');
2618 getProtocol: function()
2620 var prname = this.get('proto') || 'none';
2621 return L.NetworkModel.protocolHandlers[prname] || L.NetworkModel.protocolHandlers.none;
2624 getUptime: function()
2626 var uptime = this.getStatus('uptime');
2627 return isNaN(uptime) ? 0 : uptime;
2630 getDevice: function(resolveAlias)
2632 if (this.options.l3dev)
2633 return L.NetworkModel.getDevice(this.options.l3dev);
2638 getPhysdev: function()
2640 if (this.options.l2dev)
2641 return L.NetworkModel.getDevice(this.options.l2dev);
2646 getSubdevices: function()
2649 var dev = this.options.l2dev ?
2650 L.NetworkModel.deviceObjects[this.options.l2dev] : undefined;
2652 if (dev && dev.kind == 'bridge' && dev.ports && dev.ports.length)
2653 for (var i = 0; i < dev.ports.length; i++)
2654 rv.push(L.NetworkModel.getDevice(dev.ports[i]));
2659 getIPv4Addrs: function(mask)
2662 var addrs = this.getStatus('ipv4-address');
2665 for (var i = 0; i < addrs.length; i++)
2667 rv.push(addrs[i].address);
2669 rv.push('%s/%d'.format(addrs[i].address, addrs[i].mask));
2674 getIPv6Addrs: function(mask)
2679 addrs = this.getStatus('ipv6-address');
2682 for (var i = 0; i < addrs.length; i++)
2684 rv.push(addrs[i].address);
2686 rv.push('%s/%d'.format(addrs[i].address, addrs[i].mask));
2688 addrs = this.getStatus('ipv6-prefix-assignment');
2691 for (var i = 0; i < addrs.length; i++)
2693 rv.push('%s1'.format(addrs[i].address));
2695 rv.push('%s1/%d'.format(addrs[i].address, addrs[i].mask));
2700 getDNSAddrs: function()
2703 var addrs = this.getStatus('dns-server');
2706 for (var i = 0; i < addrs.length; i++)
2712 getIPv4DNS: function()
2715 var dns = this.getStatus('dns-server');
2718 for (var i = 0; i < dns.length; i++)
2719 if (dns[i].indexOf(':') == -1)
2725 getIPv6DNS: function()
2728 var dns = this.getStatus('dns-server');