{
var _luci2 = this;
- var alphacmp = function(a, b)
- {
- if (a < b)
- return -1;
- else if (a > b)
- return 1;
- else
- return 0;
- };
-
- var retcb = function(cb, rv)
- {
- if (typeof(cb) == 'function')
- cb(rv);
-
- return rv;
- };
-
- var isa = function(x, t)
- {
- if (typeof(x) != 'string' && typeof(t) == 'string')
- return (Object.prototype.toString.call(x) == '[object ' + t + ']');
-
- return (Object.prototype.toString.call(x) == Object.prototype.toString.call(t));
- };
-
- var rcall = function(obj, func, params, res_attr, res_default, cb, filter)
- {
- if (typeof(params) == 'undefined')
- params = { };
-
- return _luci2.rpc.call(obj, func, params).then(function(res) {
- if (res[0] != 0 || typeof(res[1]) == 'undefined')
- return retcb(cb, res_default);
-
- var rv = (typeof(res_attr) != 'undefined') ? res[1][res_attr] : res[1];
- if (typeof(rv) == 'undefined' || (typeof(res_default) != 'undefined' && !isa(rv, res_default)))
- return retcb(cb, res_default);
-
- if (typeof(filter) == 'function')
- rv = filter(rv);
-
- return retcb(cb, rv);
- });
- };
-
var Class = function() { };
Class.extend = function(properties)
return obj;
};
- this.deferred = function(x)
+ this.isDeferred = function(x)
{
return (typeof(x) == 'object' &&
typeof(x.then) == 'function' &&
this.deferrable = function()
{
- if (this.deferred(arguments[0]))
+ if (this.isDeferred(arguments[0]))
return arguments[0];
var d = $.Deferred();
};
this.globals = {
- resource: '/luci2'
+ timeout: 15000,
+ resource: '/luci2',
+ sid: '00000000000000000000000000000000'
};
this.rpc = {
- _msg_id: 1,
+ _id: 1,
+ _batch: undefined,
+ _requests: { },
+
+ _call: function(req, cb)
+ {
+ return $.ajax('/ubus', {
+ cache: false,
+ contentType: 'application/json',
+ data: JSON.stringify(req),
+ dataType: 'json',
+ type: 'POST',
+ timeout: _luci2.globals.timeout
+ }).then(cb);
+ },
- _wrap_msg: function(method, object, func, args)
+ _list_cb: function(msg)
{
- if (typeof(args) != 'object')
- args = { };
+ /* verify message frame */
+ if (typeof(msg) != 'object' || msg.jsonrpc != '2.0' || !msg.id)
+ throw 'Invalid JSON response';
- return {
- id: _luci2.rpc._msg_id++,
- jsonrpc: "2.0",
- method: method,
- params: (method == 'call') ? [ _luci2.globals.sid, object, func, args ] : object
- };
+ return msg.result;
},
- _parse_response: function(keys, priv)
+ _call_cb: function(msg)
{
- return function(data) {
- var obj;
- try {
- obj = $.parseJSON(data);
- } catch(e) { }
+ var data = [ ];
+ var type = Object.prototype.toString;
- if (typeof(obj) != 'object')
- return undefined;
+ if (!$.isArray(msg))
+ msg = [ msg ];
+
+ for (var i = 0; i < msg.length; i++)
+ {
+ /* verify message frame */
+ if (typeof(msg[i]) != 'object' || msg[i].jsonrpc != '2.0' || !msg[i].id)
+ throw 'Invalid JSON response';
+
+ /* fetch related request info */
+ var req = _luci2.rpc._requests[msg[i].id];
+ if (typeof(req) != 'object')
+ throw 'No related request for JSON response';
- /* is a batched response */
- if (keys)
+ /* fetch response attribute and verify returned type */
+ var ret = undefined;
+
+ if ($.isArray(msg[i].result) && msg[i].result[0] == 0)
+ ret = (msg[i].result.length > 1) ? msg[i].result[1] : msg[i].result[0];
+
+ if (req.expect)
{
- var rv = { };
- for (var i = 0; i < obj.length; i++)
+ for (var key in req.expect)
{
- var p = (typeof(priv) != 'undefined') ? priv[i] : undefined;
+ if (typeof(ret) != 'undefined' && key != '')
+ ret = ret[key];
- if ($.isArray(obj[i].result) && typeof(priv) != 'undefined')
- obj[i].result[2] = p;
+ if (type.call(ret) != type.call(req.expect[key]))
+ ret = req.expect[key];
- if (obj[i].jsonrpc != '2.0' || obj[i].error || !obj[i].result)
- rv[keys[i]] = [ 4 /* UBUS_STATUS_NO_DATA */, undefined, p ];
- else
- rv[keys[i]] = obj[i].result;
+ break;
}
- return rv;
}
- if (obj.jsonrpc != '2.0' || obj.error || !obj.result)
- return [ 4 /* UBUS_STATUS_NO_DATA */, undefined, priv ];
+ /* apply filter */
+ if (typeof(req.filter) == 'function')
+ {
+ req.priv[0] = ret;
+ req.priv[1] = req.params;
+ ret = req.filter.apply(_luci2.rpc, req.priv);
+ }
- if ($.isArray(obj.result) && typeof(priv) != 'undefined')
- obj.result[2] = priv;
+ /* store response data */
+ if (typeof(req.index) == 'number')
+ data[req.index] = ret;
+ else
+ data = ret;
- return obj.result;
- };
+ /* delete request object */
+ delete _luci2.rpc._requests[msg[i].id];
+ }
+
+ return data;
},
- _post_msg: function(message, cb, keys, priv)
+ list: function()
{
- return $.ajax('/ubus', {
- cache: false,
- contentType: 'application/json',
- data: JSON.stringify(message),
- dataFilter: _luci2.rpc._parse_response(keys, priv),
- dataType: 'text',
- success: cb,
- type: 'POST'
- });
+ var params = [ ];
+ for (var i = 0; i < arguments.length; i++)
+ params[i] = arguments[i];
+
+ var msg = {
+ jsonrpc: '2.0',
+ id: this._id++,
+ method: 'list',
+ params: (params.length > 0) ? params : undefined
+ };
+
+ return this._call(msg, this._list_cb);
},
- _post_single: function(object, method, args, cb, priv)
+ batch: function()
{
- var msg = _luci2.rpc._wrap_msg('call', object, method, args, priv);
- return _luci2.rpc._post_msg(msg, cb, undefined, priv);
+ if (!$.isArray(this._batch))
+ this._batch = [ ];
},
- _post_batch: function(methods, cb)
+ flush: function()
{
- if (typeof(methods) != 'object')
- return undefined;
+ if (!$.isArray(this._batch))
+ return _luci2.deferrable([ ]);
- var msgs = [ ];
- var keys = [ ];
- var priv = [ ];
+ var req = this._batch;
+ delete this._batch;
- for (var k in methods)
- {
- if (typeof(methods[k]) != 'object' || methods[k].length < 2)
- continue;
+ /* call rpc */
+ return this._call(req, this._call_cb);
+ },
- keys.push(k);
- priv.push(methods[k][3]);
- msgs.push(_luci2.rpc._wrap_msg('call', methods[k][0], methods[k][1], methods[k][2]));
- }
+ declare: function(options)
+ {
+ var _rpc = this;
- if (msgs.length > 0)
- return _luci2.rpc._post_msg(msgs, cb, keys, priv);
+ return function() {
+ /* build parameter object */
+ var p_off = 0;
+ var params = { };
+ if ($.isArray(options.params))
+ for (p_off = 0; p_off < options.params.length; p_off++)
+ params[options.params[p_off]] = arguments[p_off];
- return _luci2.deferrable([ ]);
- },
+ /* all remaining arguments are private args */
+ var priv = [ undefined, undefined ];
+ for (; p_off < arguments.length; p_off++)
+ priv.push(arguments[p_off]);
- call: function()
- {
- var a = arguments;
- if (typeof a[0] == 'string')
- return _luci2.rpc._post_single(a[0], a[1], a[2], a[3], a[4]);
- else
- return _luci2.rpc._post_batch(a[0], a[1]);
- },
+ /* store request info */
+ var req = _rpc._requests[_rpc._id] = {
+ expect: options.expect,
+ filter: options.filter,
+ params: params,
+ priv: priv
+ };
- list: function(objects)
- {
- var msg = _luci2.rpc._wrap_msg('list', objects);
- return _luci2.rpc._post_msg(msg);
- },
+ /* build message object */
+ var msg = {
+ jsonrpc: '2.0',
+ id: _rpc._id++,
+ method: 'call',
+ params: [
+ _luci2.globals.sid,
+ options.object,
+ options.method,
+ params
+ ]
+ };
- access: function(scope, object, method, cb)
- {
- return _luci2.rpc._post_single('session', 'access', {
- 'sid': _luci2.globals.sid,
- 'scope': scope,
- 'object': object,
- 'function': method
- }, function(rv) {
- return retcb(cb, (rv[0] == 0 && rv[1] && rv[1].access == true));
- });
+ /* when a batch is in progress then store index in request data
+ * and push message object onto the stack */
+ if ($.isArray(_rpc._batch))
+ {
+ req.index = _rpc._batch.push(msg) - 1;
+ return _luci2.deferrable(msg);
+ }
+
+ /* call rpc */
+ return _rpc._call(msg, _rpc._call_cb);
+ };
}
};
this.uci = {
- writable: function(cb)
+ writable: function()
{
- return _luci2.rpc.access('ubus', 'uci', 'commit', cb);
+ return _luci2.session.access('ubus', 'uci', 'commit');
},
- add: function(config, type, cb)
- {
- return rcall('uci', 'add', { config: config, type: type }, 'section', '', cb);
- },
+ add: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'add',
+ params: [ 'config', 'type', 'name', 'values' ],
+ expect: { section: '' }
+ }),
apply: function()
{
},
+ configs: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'configs',
+ expect: { configs: [ ] }
+ }),
+
+ _changes: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'changes',
+ params: [ 'config' ],
+ expect: { changes: [ ] }
+ }),
+
changes: function(config)
{
- return rcall('uci', 'changes', { config: config }, 'changes', [ ], cb);
- },
+ if (typeof(config) == 'string')
+ return this._changes(config);
- commit: function(config)
- {
- return rcall('uci', 'commit', { config: config }, undefined, undefined, cb);
- },
+ var configlist;
+ return this.configs().then(function(configs) {
+ _luci2.rpc.batch();
+ configlist = configs;
- 'delete': function(config, section, option)
- {
- var req = { config: config, section: section };
+ for (var i = 0; i < configs.length; i++)
+ _luci2.uci._changes(configs[i]);
- if (isa(option, 'Array'))
- req.options = option;
- else
- req.option = option;
+ return _luci2.rpc.flush();
+ }).then(function(changes) {
+ var rv = { };
+
+ for (var i = 0; i < configlist.length; i++)
+ if (changes[i].length)
+ rv[configlist[i]] = changes[i];
- return rcall('uci', 'delete', req, undefined, undefined, cb);
+ return rv;
+ });
},
- delete_all: function(config, type, matches)
+ commit: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'commit',
+ params: [ 'config' ]
+ }),
+
+ _delete_one: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'delete',
+ params: [ 'config', 'section', 'option' ]
+ }),
+
+ _delete_multiple: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'delete',
+ params: [ 'config', 'section', 'options' ]
+ }),
+
+ 'delete': function(config, section, option)
{
- return rcall('uci', 'delete', { config: config, type: type, match: matches }, undefined, undefined, cb);
+ if ($.isArray(option))
+ return this._delete_multiple(config, section, option);
+ else
+ return this._delete_one(config, section, option);
},
+ delete_all: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'delete',
+ params: [ 'config', 'type', 'match' ]
+ }),
+
+ _foreach: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'get',
+ params: [ 'config', 'type' ],
+ expect: { values: { } }
+ }),
+
foreach: function(config, type, cb)
{
- return rcall('uci', 'get', { config: config, type: type }, 'values', { }, function(sections) {
+ return this._foreach(config, type).then(function(sections) {
for (var s in sections)
cb(sections[s]);
});
},
- get: function(config, section, option, cb)
- {
- return rcall('uci', 'get', { config: config, section: section, option: option }, undefined, { }, function(res) {
- if (typeof(option) == 'undefined')
- return retcb(cb, (res.values && res.values['.type']) ? res.values['.type'] : undefined);
-
- return retcb(cb, res.value);
- });
- },
-
- get_all: function(config, section, cb)
- {
- return rcall('uci', 'get', { config: config, section: section }, 'values', { }, cb);
- },
+ get: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'get',
+ params: [ 'config', 'section', 'option' ],
+ expect: { '': { } },
+ filter: function(data, params) {
+ if (typeof(params.option) == 'undefined')
+ return data.values ? data.values['.type'] : undefined;
+ else
+ return data.value;
+ }
+ }),
+
+ get_all: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'get',
+ params: [ 'config', 'section' ],
+ expect: { values: { } },
+ filter: function(data, params) {
+ if (typeof(params.section) == 'string')
+ data['.section'] = params.section;
+ else if (typeof(params.config) == 'string')
+ data['.package'] = params.config;
+ return data;
+ }
+ }),
- get_first: function(config, type, option, cb)
+ get_first: function(config, type, option)
{
- return rcall('uci', 'get', { config: config, type: type }, 'values', { }, function(sections) {
+ return this._foreach(config, type).then(function(sections) {
for (var s in sections)
{
var val = (typeof(option) == 'string') ? sections[s][option] : sections[s]['.name'];
if (typeof(val) != 'undefined')
- return retcb(cb, val);
+ return val;
}
- return retcb(cb, undefined);
+ return undefined;
});
},
- section: function(config, type, name, values, cb)
- {
- return rcall('uci', 'add', { config: config, type: type, name: name, values: values }, 'section', undefined, cb);
- },
+ section: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'add',
+ params: [ 'config', 'type', 'name', 'values' ],
+ expect: { section: '' }
+ }),
- set: function(config, section, option, value, cb)
+ _set: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'set',
+ params: [ 'config', 'section', 'values' ]
+ }),
+
+ set: function(config, section, option, value)
{
if (typeof(value) == 'undefined' && typeof(option) == 'string')
- return rcall('uci', 'add', { config: config, section: section, type: option }, undefined, undefined, cb);
- else if (isa(option, 'Object'))
- return rcall('uci', 'set', { config: config, section: section, values: option }, undefined, undefined, cb);
- else
+ return this.section(config, section, option); /* option -> type */
+ else if ($.isPlainObject(option))
+ return this._set(config, section, option); /* option -> values */
var values = { };
values[option] = value;
- return rcall('uci', 'set', { config: config, section: section, values: values }, undefined, undefined, cb);
+ return this._set(config, section, values);
},
- order: function(config, sections, cb)
- {
- return rcall('uci', 'order', { config: config, sections: sections }, undefined, undefined, cb);
- }
+ order: _luci2.rpc.declare({
+ object: 'uci',
+ method: 'order',
+ params: [ 'config', 'sections' ]
+ })
};
this.network = {
- getNetworkStatus: function(cb)
- {
- var ifaces = [ ];
- var assign = function(target, key)
- {
- return function(value) {
- if (typeof(value) != 'undefined' && !$.isEmptyObject(value))
- target[key] = value;
- };
- };
+ listNetworkNames: function() {
+ return _luci2.rpc.list('network.interface.*').then(function(list) {
+ var names = [ ];
+ for (var name in list)
+ if (name != 'network.interface.loopback')
+ names.push(name.substring(18));
+ names.sort();
+ return names;
+ });
+ },
- return _luci2.rpc.list().then(function(data) {
- var requests = [ ];
+ listDeviceNames: _luci2.rpc.declare({
+ object: 'network.device',
+ method: 'status',
+ expect: { '': { } },
+ filter: function(data) {
+ var names = [ ];
+ for (var name in data)
+ if (name != 'lo')
+ names.push(name);
+ names.sort();
+ return names;
+ }
+ }),
- for (var i = 0; i < data.length; i++)
- {
- if (data[i].indexOf('network.interface.') != 0)
- continue;
+ getNetworkStatus: function()
+ {
+ var nets = [ ];
+ var devs = { };
- var ifname = data[i].substring(18);
- if (ifname == 'loopback')
- continue;
+ return this.listNetworkNames().then(function(names) {
+ _luci2.rpc.batch();
- var iface = { 'name': ifname };
+ for (var i = 0; i < names.length; i++)
+ _luci2.network.getInterfaceStatus(names[i]);
- ifaces.push(iface);
- requests.push(['network.interface', 'status', { 'interface': ifname }, iface]);
+ return _luci2.rpc.flush();
+ }).then(function(networks) {
+ for (var i = 0; i < networks.length; i++)
+ {
+ var net = nets[i] = networks[i];
+ var dev = net.l3_device || net.l2_device;
+ if (dev)
+ net.device = devs[dev] || (devs[dev] = { });
}
- return _luci2.rpc.call(requests, function(responses) {
- for (var key in responses)
- if (responses[key][0] == 0 && responses[key][1] && responses[key][2])
- $.extend(responses[key][2], responses[key][1]);
- });
- }).then(function() {
- var requests = [ ];
+ _luci2.rpc.batch();
- for (var i = 0; i < ifaces.length; i++)
- {
- var iface = ifaces[i];
+ for (var dev in devs)
+ _luci2.network.getDeviceStatus(dev);
- var dev = iface.l3_device || iface.l2_device;
- if (!dev)
- continue;
+ return _luci2.rpc.flush();
+ }).then(function(devices) {
+ _luci2.rpc.batch();
- iface.device = { 'name': dev };
- requests[dev] = ['network.device', 'status', { 'name': dev }, iface.device];
- }
+ for (var i = 0; i < devices.length; i++)
+ {
+ var brm = devices[i]['bridge-members'];
+ delete devices[i]['bridge-members'];
- return _luci2.rpc.call(requests, function(responses) {
- for (var key in responses)
- if (responses[key][0] == 0 && responses[key][1] && responses[key][2])
- $.extend(responses[key][2], responses[key][1]);
- });
- }).then(function() {
- var requests = [ ];
+ $.extend(devs[devices[i]['device']], devices[i]);
- for (var i = 0; i < ifaces.length; i++)
- {
- var iface = ifaces[i];
- if (!iface.device)
+ if (!brm)
continue;
- var subdevs = iface.device['bridge-members'];
- if (!subdevs)
- continue;
+ devs[devices[i]['device']].subdevices = [ ];
- iface.subdevices = [ ];
- for (var j = 0; j < subdevs.length; j++)
+ for (var j = 0; j < brm.length; j++)
{
- iface.subdevices[j] = { 'name': subdevs[j] };
- requests.push(['network.device', 'status', { 'name': subdevs[j] }, iface.subdevices[j]]);
+ if (!devs[brm[j]])
+ {
+ devs[brm[j]] = { };
+ _luci2.network.getDeviceStatus(brm[j]);
+ }
+
+ devs[devices[i]['device']].subdevices[j] = devs[brm[j]];
}
}
- return _luci2.rpc.call(requests, function(responses) {
- for (var key in responses)
- if (responses[key][0] == 0 && responses[key][1] && responses[key][2])
- $.extend(responses[key][2], responses[key][1]);
- });
- }).then(function() {
- var requests = [ ];
+ return _luci2.rpc.flush();
+ }).then(function(subdevices) {
+ for (var i = 0; i < subdevices.length; i++)
+ $.extend(devs[subdevices[i]['device']], subdevices[i]);
- for (var i = 0; i < ifaces.length; i++)
- {
- var iface = ifaces[i];
+ _luci2.rpc.batch();
- if (iface.device)
- requests.push(['iwinfo', 'info', { 'device': iface.device.name }, iface.device]);
+ for (var dev in devs)
+ _luci2.wireless.getDeviceStatus(dev);
- if (iface.subdevices)
- for (var j = 0; j < iface.subdevices.length; j++)
- requests.push(['iwinfo', 'info', { 'device': iface.subdevices[j].name }, iface.subdevices[j]]);
- }
+ return _luci2.rpc.flush();
+ }).then(function(wifidevices) {
+ for (var i = 0; i < wifidevices.length; i++)
+ if (wifidevices[i])
+ devs[wifidevices[i]['device']].wireless = wifidevices[i];
- return _luci2.rpc.call(requests, function(responses) {
- for (var key in responses)
- if (responses[key][0] == 0 && responses[key][1] && responses[key][2])
- if (!$.isEmptyObject(responses[key][1]))
- responses[key][2].wireless = responses[key][1];
- });
- }).then(function() {
- ifaces.sort(function(a, b) {
+ nets.sort(function(a, b) {
if (a['interface'] < b['interface'])
return -1;
else if (a['interface'] > b['interface'])
else
return 0;
});
- return retcb(cb, ifaces);
+
+ return nets;
});
},
findWanInterfaces: function(cb)
{
- return _luci2.rpc.list().then(function(data) {
- var requests = { };
- for (var i = 0; i < data.length; i++)
- {
- if (data[i].indexOf('network.interface.') == 0)
- {
- var ifname = data[i].substring(18);
- requests[ifname] = ['network.interface', 'status', { 'interface': ifname }];
- }
- }
- return _luci2.rpc.call(requests);
- }).then(function(responses) {
- var rv = [ ];
- for (var ifname in responses)
- {
- var response = responses[ifname];
+ return this.listNetworkNames().then(function(names) {
+ _luci2.rpc.batch();
- if (response[0] != 0 || !response[1] || !response[1].route)
+ for (var i = 0; i < names.length; i++)
+ _luci2.network.getInterfaceStatus(names[i]);
+
+ return _luci2.rpc.flush();
+ }).then(function(interfaces) {
+ var rv = [ undefined, undefined ];
+
+ for (var i = 0; i < interfaces.length; i++)
+ {
+ if (!interfaces[i].route)
continue;
- for (var rn = 0, rt = response[1].route[rn];
- rn < response[1].route.length;
- rn++, rt = response[1].route[rn])
+ for (var j = 0; j < interfaces[i].route.length; j++)
{
+ var rt = interfaces[i].route[j];
+
if (typeof(rt.table) != 'undefined')
continue;
if (rt.target == '0.0.0.0' && rt.mask == 0)
- rv[0] = response[1];
+ rv[0] = interfaces[i];
else if (rt.target == '::' && rt.mask == 0)
- rv[1] = response[1];
+ rv[1] = interfaces[i];
}
}
- return retcb(cb, rv);
- });
- },
-
- getDHCPLeases: function(cb)
- {
- return rcall('luci2.network', 'dhcp_leases', undefined, 'leases', [ ], cb);
- },
-
- getDHCPv6Leases: function(cb)
- {
- return rcall('luci2.network', 'dhcp6_leases', undefined, 'leases', [ ], cb);
- },
-
- getRoutes: function(cb)
- {
- return rcall('luci2.network', 'routes', undefined, 'routes', [ ], cb);
- },
-
- getIPv6Routes: function(cb)
- {
- return rcall('luci2.network', 'routes6', undefined, 'routes', [ ], cb);
- },
-
- getARPTable: function(cb)
- {
- return rcall('luci2.network', 'arp_table', undefined, 'entries', [ ], cb);
- },
-
- getInterfaceStatus: function(iface, cb)
- {
- return rcall('network.interface', 'status', { 'interface': iface }, undefined, { }, cb, function(rv) {
- rv['interface'] = iface;
- rv['l2_device'] = rv['device'];
return rv;
});
},
- getDeviceStatus: function(dev, cb)
- {
- return rcall('network.device', 'status', { name: dev }, undefined, { }, cb, function(rv) {
- if (typeof(dev) == 'string')
- rv.device = dev;
- return rv;
- });
- },
-
- getConntrackCount: function(cb)
- {
- return rcall('luci2.network', 'conntrack_count', undefined, undefined, {
- count: 0,
- limit: 0
- }, cb);
- }
+ getDHCPLeases: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'dhcp_leases',
+ expect: { leases: [ ] }
+ }),
+
+ getDHCPv6Leases: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'dhcp6_leases',
+ expect: { leases: [ ] }
+ }),
+
+ getRoutes: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'routes',
+ expect: { routes: [ ] }
+ }),
+
+ getIPv6Routes: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'routes',
+ expect: { routes: [ ] }
+ }),
+
+ getARPTable: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'arp_table',
+ expect: { entries: [ ] }
+ }),
+
+ getInterfaceStatus: _luci2.rpc.declare({
+ object: 'network.interface',
+ method: 'status',
+ params: [ 'interface' ],
+ expect: { '': { } },
+ filter: function(data, params) {
+ data['interface'] = params['interface'];
+ data['l2_device'] = data['device'];
+ delete data['device'];
+ return data;
+ }
+ }),
+
+ getDeviceStatus: _luci2.rpc.declare({
+ object: 'network.device',
+ method: 'status',
+ params: [ 'name' ],
+ expect: { '': { } },
+ filter: function(data, params) {
+ data['device'] = params['name'];
+ return data;
+ }
+ }),
+
+ getConntrackCount: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'conntrack_count',
+ expect: { '': { count: 0, limit: 0 } }
+ }),
+
+ listSwitchNames: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'switch_list',
+ expect: { switches: [ ] }
+ }),
+
+ getSwitchInfo: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'switch_info',
+ params: [ 'switch' ],
+ expect: { info: { } },
+ filter: function(data, params) {
+ data['attrs'] = data['switch'];
+ data['vlan_attrs'] = data['vlan'];
+ data['port_attrs'] = data['port'];
+ data['switch'] = params['switch'];
+
+ delete data.vlan;
+ delete data.port;
+
+ return data;
+ }
+ }),
+
+ getSwitchStatus: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'switch_status',
+ params: [ 'switch' ],
+ expect: { ports: [ ] }
+ }),
+
+
+ runPing: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'ping',
+ params: [ 'data' ],
+ expect: { '': { code: -1 } }
+ }),
+
+ runPing6: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'ping6',
+ params: [ 'data' ],
+ expect: { '': { code: -1 } }
+ }),
+
+ runTraceroute: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'traceroute',
+ params: [ 'data' ],
+ expect: { '': { code: -1 } }
+ }),
+
+ runTraceroute6: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'traceroute6',
+ params: [ 'data' ],
+ expect: { '': { code: -1 } }
+ }),
+
+ runNslookup: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'nslookup',
+ params: [ 'data' ],
+ expect: { '': { code: -1 } }
+ }),
+
+
+ setUp: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'ifup',
+ params: [ 'data' ],
+ expect: { '': { code: -1 } }
+ }),
+
+ setDown: _luci2.rpc.declare({
+ object: 'luci2.network',
+ method: 'ifdown',
+ params: [ 'data' ],
+ expect: { '': { code: -1 } }
+ })
};
this.wireless = {
- getDevices: function(cb) {
- return rcall('iwinfo', 'devices', undefined, 'devices', [ ], cb, function(rv) {
- rv.sort();
- return rv;
- });
- },
+ listDeviceNames: _luci2.rpc.declare({
+ object: 'iwinfo',
+ method: 'devices',
+ expect: { 'devices': [ ] },
+ filter: function(data) {
+ data.sort();
+ return data;
+ }
+ }),
+
+ getDeviceStatus: _luci2.rpc.declare({
+ object: 'iwinfo',
+ method: 'info',
+ params: [ 'device' ],
+ expect: { '': { } },
+ filter: function(data, params) {
+ if (!$.isEmptyObject(data))
+ {
+ data['device'] = params['device'];
+ return data;
+ }
+ return undefined;
+ }
+ }),
+
+ getAssocList: _luci2.rpc.declare({
+ object: 'iwinfo',
+ method: 'assoclist',
+ params: [ 'device' ],
+ expect: { results: [ ] },
+ filter: function(data, params) {
+ for (var i = 0; i < data.length; i++)
+ data[i]['device'] = params['device'];
- getInfo: function(dev, cb) {
- var parse_info = function(device, info, rv)
- {
- if (!rv[info.phy])
- rv[info.phy] = {
- networks: [ ]
- };
+ data.sort(function(a, b) {
+ if (a.bssid < b.bssid)
+ return -1;
+ else if (a.bssid > b.bssid)
+ return 1;
+ else
+ return 0;
+ });
+
+ return data;
+ }
+ }),
- var phy = rv[info.phy];
+ getWirelessStatus: function() {
+ return this.listDeviceNames().then(function(names) {
+ _luci2.rpc.batch();
+
+ for (var i = 0; i < names.length; i++)
+ _luci2.wireless.getDeviceStatus(names[i]);
+
+ return _luci2.rpc.flush();
+ }).then(function(networks) {
+ var rv = { };
var phy_attrs = [
'country', 'channel', 'frequency', 'frequency_offset',
'signal', 'noise', 'bitrate', 'encryption'
];
- for (var i = 0; i < phy_attrs.length; i++)
- phy[phy_attrs[i]] = info[phy_attrs[i]];
-
- var net = {
- device: device
- };
-
- for (var i = 0; i < net_attrs.length; i++)
- net[net_attrs[i]] = info[net_attrs[i]];
-
- phy.networks.push(net);
-
- return phy;
- };
-
- if (!dev)
- {
- return _luci2.wireless.getDevices().then(function(devices) {
- var requests = [ ];
-
- for (var i = 0; i < devices.length; i++)
- {
- if (devices[i].indexOf('.sta') >= 0)
- continue;
-
- requests[devices[i]] = [ 'iwinfo', 'info', { device: devices[i] } ];
- }
-
- return _luci2.rpc.call(requests);
- }).then(function(responses) {
- var rv = { };
-
- for (var device in responses)
- {
- var response = responses[device];
+ for (var i = 0; i < networks.length; i++)
+ {
+ var phy = rv[networks[i].phy] || (
+ rv[networks[i].phy] = { networks: [ ] }
+ );
- if (response[0] != 0 || !response[1])
- continue;
+ var net = {
+ device: networks[i].device
+ };
- parse_info(device, response[1], rv);
- }
+ for (var j = 0; j < phy_attrs.length; j++)
+ phy[phy_attrs[j]] = networks[i][phy_attrs[j]];
- return retcb(cb, rv);
- });
- }
+ for (var j = 0; j < net_attrs.length; j++)
+ net[net_attrs[j]] = networks[i][net_attrs[j]];
- return _luci2.rpc.call('iwinfo', 'info', { device: dev }).then(function(response) {
- if (response[0] != 0 || !response[1])
- return retcb(cb, { });
+ phy.networks.push(net);
+ }
- return retcb(cb, parse_info(dev, response[1], { }));
+ return rv;
});
},
- getAssocList: function(dev, cb)
+ getAssocLists: function()
{
- if (!dev)
- {
- return _luci2.wireless.getDevices().then(function(devices) {
- var requests = { };
-
- for (var i = 0; i < devices.length; i++)
- {
- if (devices[i].indexOf('.sta') >= 0)
- continue;
-
- requests[devices[i]] = [ 'iwinfo', 'assoclist', { device: devices[i] } ];
- }
-
- return _luci2.rpc.call(requests);
- }).then(function(responses) {
- var rv = [ ];
+ return this.listDeviceNames().then(function(names) {
+ _luci2.rpc.batch();
- for (var device in responses)
- {
- var response = responses[device];
+ for (var i = 0; i < names.length; i++)
+ _luci2.wireless.getAssocList(names[i]);
- if (response[0] != 0 || !response[1] || !response[1].results)
- continue;
-
- for (var i = 0; i < response[1].results.length; i++)
- {
- var station = response[1].results[i];
-
- station.device = device;
- rv.push(station);
- }
- }
-
- rv.sort(function(a, b) {
- return (a.device == b.device)
- ? (a.bssid < b.bssid)
- : (a.device > b.device)
- ;
- });
-
- return retcb(cb, rv);
- });
- }
-
- return _luci2.rpc.call('iwinfo', 'assoclist', { device: dev }).then(function(response) {
+ return _luci2.rpc.flush();
+ }).then(function(assoclists) {
var rv = [ ];
- if (response[0] != 0 || !response[1] || !response[1].results)
- return retcb(cb, rv);
-
- for (var i = 0; i < response[1].results.length; i++)
- {
- var station = response[1].results[i];
-
- station.device = dev;
- rv.push(station);
- }
+ for (var i = 0; i < assoclists.length; i++)
+ for (var j = 0; j < assoclists[i].length; j++)
+ rv.push(assoclists[i][j]);
- rv.sort(function(a, b) {
- return (a.bssid < b.bssid);
- });
-
- return retcb(cb, rv);
+ return rv;
});
},
}
};
- this.system = {
- getInfo: function(cb)
+ this.firewall = {
+ getZoneColor: function(zone)
{
- return _luci2.rpc.call({
- info: [ 'system', 'info', { } ],
- board: [ 'system', 'board', { } ],
- disk: [ 'luci2.system', 'diskfree', { } ]
- }).then(function(responses) {
- var rv = { };
+ if ($.isPlainObject(zone))
+ zone = zone.name;
- if (responses.info[0] == 0)
- $.extend(rv, responses.info[1]);
+ if (zone == 'lan')
+ return '#90f090';
+ else if (zone == 'wan')
+ return '#f09090';
- if (responses.board[0] == 0)
- $.extend(rv, responses.board[1]);
+ for (var i = 0, hash = 0;
+ i < zone.length;
+ hash = zone.charCodeAt(i++) + ((hash << 5) - hash));
- if (responses.disk[0] == 0)
- $.extend(rv, responses.disk[1]);
+ for (var i = 0, color = '#';
+ i < 3;
+ color += ('00' + ((hash >> i++ * 8) & 0xFF).tozoneing(16)).slice(-2));
- return retcb(cb, rv);
- });
+ return color;
},
- getProcessList: function(cb)
+ findZoneByNetwork: function(network)
{
- return rcall('luci2.system', 'process_list', undefined, 'processes', [ ], cb, function(rv) {
- rv.sort(function(a, b) { return a.pid - b.pid });
- return rv;
+ var self = this;
+ var zone = undefined;
+
+ return _luci2.uci.foreach('firewall', 'zone', function(z) {
+ if (!z.name || !z.network)
+ return;
+
+ if (!$.isArray(z.network))
+ z.network = z.network.split(/\s+/);
+
+ for (var i = 0; i < z.network.length; i++)
+ {
+ if (z.network[i] == network)
+ {
+ zone = z;
+ break;
+ }
+ }
+ }).then(function() {
+ if (zone)
+ zone.color = self.getZoneColor(zone);
+
+ return zone;
});
- },
+ }
+ };
- getSystemLog: function(cb)
- {
- return rcall('luci2.system', 'syslog', undefined, 'log', '', cb);
- },
+ this.system = {
+ getSystemInfo: _luci2.rpc.declare({
+ object: 'system',
+ method: 'info',
+ expect: { '': { } }
+ }),
+
+ getBoardInfo: _luci2.rpc.declare({
+ object: 'system',
+ method: 'board',
+ expect: { '': { } }
+ }),
+
+ getDiskInfo: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'diskfree',
+ expect: { '': { } }
+ }),
- getKernelLog: function(cb)
+ getInfo: function(cb)
{
- return rcall('luci2.system', 'dmesg', undefined, 'log', '', cb);
- },
+ _luci2.rpc.batch();
- getZoneInfo: function(cb)
- {
- return $.getJSON(_luci2.globals.resource + '/zoneinfo.json', cb);
- },
+ this.getSystemInfo();
+ this.getBoardInfo();
+ this.getDiskInfo();
- canSendSignal: function(cb)
- {
- return _luci2.rpc.access('ubus', 'luci2.system', 'process_signal', cb);
- },
+ return _luci2.rpc.flush().then(function(info) {
+ var rv = { };
- sendSignal: function(pid, sig, cb)
- {
- return _luci2.rpc.call('luci2.system', 'process_signal', { pid: pid, signal: sig }).then(function(response) {
- return retcb(cb, response[0] == 0);
+ $.extend(rv, info[0]);
+ $.extend(rv, info[1]);
+ $.extend(rv, info[2]);
+
+ return rv;
});
},
- initList: function(cb)
+ getProcessList: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'process_list',
+ expect: { processes: [ ] },
+ filter: function(data) {
+ data.sort(function(a, b) { return a.pid - b.pid });
+ return data;
+ }
+ }),
+
+ getSystemLog: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'syslog',
+ expect: { log: '' }
+ }),
+
+ getKernelLog: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'dmesg',
+ expect: { log: '' }
+ }),
+
+ getZoneInfo: function(cb)
{
- return rcall('luci2.system', 'init_list', undefined, 'initscripts', [ ], cb, function(rv) {
- rv.sort(function(a, b) { return (a.start || 0) - (b.start || 0) });
- return rv;
- });
+ return $.getJSON(_luci2.globals.resource + '/zoneinfo.json', cb);
},
+ sendSignal: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'process_signal',
+ params: [ 'pid', 'signal' ],
+ filter: function(data) {
+ return (data == 0);
+ }
+ }),
+
+ initList: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'init_list',
+ expect: { initscripts: [ ] },
+ filter: function(data) {
+ data.sort(function(a, b) { return (a.start || 0) - (b.start || 0) });
+ return data;
+ }
+ }),
+
initEnabled: function(init, cb)
{
- return this.initList(function(list) {
+ return this.initList().then(function(list) {
for (var i = 0; i < list.length; i++)
if (list[i].name == init)
- return retcb(cb, !!list[i].enabled);
+ return !!list[i].enabled;
- return retcb(cb, false);
- });
- },
-
- initRun: function(init, action, cb)
- {
- return _luci2.rpc.call('luci2.system', 'init_action', { name: init, action: action }).then(function(response) {
- return retcb(cb, response[0] == 0);
+ return false;
});
},
- canInitRun: function(cb)
- {
- return _luci2.rpc.access('ubus', 'luci2.system', 'init_action', cb);
- },
+ initRun: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'init_action',
+ params: [ 'name', 'action' ],
+ filter: function(data) {
+ return (data == 0);
+ }
+ }),
initStart: function(init, cb) { return _luci2.system.initRun(init, 'start', cb) },
initStop: function(init, cb) { return _luci2.system.initRun(init, 'stop', cb) },
initDisable: function(init, cb) { return _luci2.system.initRun(init, 'disable', cb) },
- getRcLocal: function(cb)
- {
- return rcall('luci2.system', 'rclocal_get', undefined, 'data', '', cb);
- },
-
- setRcLocal: function(data, cb)
- {
- return rcall('luci2.system', 'rclocal_set', { data: data }, undefined, undefined, cb);
- },
-
- canSetRcLocal: function(cb)
- {
- return _luci2.rpc.access('ubus', 'luci2.system', 'rclocal_set', cb);
- },
-
-
- getCrontab: function(cb)
- {
- return rcall('luci2.system', 'crontab_get', undefined, 'data', '', cb);
- },
+ getRcLocal: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'rclocal_get',
+ expect: { data: '' }
+ }),
+
+ setRcLocal: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'rclocal_set',
+ params: [ 'data' ]
+ }),
- setCrontab: function(data, cb)
- {
- return rcall('luci2.system', 'crontab_set', { data: data }, undefined, undefined, cb);
- },
- canSetCrontab: function(cb)
- {
- return _luci2.rpc.access('ubus', 'luci2.system', 'crontab_set', cb);
- },
+ getCrontab: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'crontab_get',
+ expect: { data: '' }
+ }),
+ setCrontab: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'crontab_set',
+ params: [ 'data' ]
+ }),
- getSSHKeys: function(cb)
- {
- return rcall('luci2.system', 'sshkeys_get', undefined, 'keys', [ ], cb);
- },
- setSSHKeys: function(keys, cb)
- {
- return rcall('luci2.system', 'sshkeys_set', { keys: keys }, undefined, undefined, cb);
- },
-
- canSetSSHKeys: function(cb)
- {
- return _luci2.rpc.access('ubus', 'luci2.system', 'sshkeys_set', cb);
- },
-
-
- setPassword: function(user, pass, cb)
- {
- return rcall('luci2.system', 'password_set', { user: user, password: pass }, undefined, undefined, cb);
- },
-
- canSetPassword: function(cb)
- {
- return _luci2.rpc.access('ubus', 'luci2.system', 'password_set', cb);
- },
-
-
- listLEDs: function(cb)
- {
- return rcall('luci2.system', 'led_list', undefined, 'leds', [ ], cb);
- },
-
- listUSBDevices: function(cb)
- {
- return rcall('luci2.system', 'usb_list', undefined, 'devices', [ ], cb);
- },
-
-
- testUpgrade: function(cb)
- {
- return rcall('luci2.system', 'upgrade_test', undefined, undefined, { }, cb);
- },
+ getSSHKeys: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'sshkeys_get',
+ expect: { keys: [ ] }
+ }),
- startUpgrade: function(keep, cb)
- {
- return rcall('luci2.system', 'upgrade_start', { keep: !!keep }, undefined, undefined, cb);
- },
+ setSSHKeys: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'sshkeys_set',
+ params: [ 'keys' ]
+ }),
- cleanUpgrade: function(cb)
- {
- return rcall('luci2.system', 'upgrade_clean', undefined, undefined, undefined, cb);
- },
- canUpgrade: function(cb)
- {
- return _luci2.rpc.access('ubus', 'luci2.system', 'upgrade_start', cb);
- },
+ setPassword: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'password_set',
+ params: [ 'user', 'password' ]
+ }),
- restoreBackup: function(cb)
- {
- return rcall('luci2.system', 'backup_restore', undefined, undefined, undefined, cb);
- },
+ listLEDs: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'led_list',
+ expect: { leds: [ ] }
+ }),
- cleanBackup: function(cb)
- {
- return rcall('luci2.system', 'backup_clean', undefined, undefined, undefined, cb);
- },
+ listUSBDevices: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'usb_list',
+ expect: { devices: [ ] }
+ }),
- canRestoreBackup: function(cb)
- {
- return _luci2.rpc.access('ubus', 'luci2.system', 'backup_restore', cb);
- },
+ testUpgrade: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'upgrade_test',
+ expect: { '': { } }
+ }),
- getBackupConfig: function(cb)
- {
- return rcall('luci2.system', 'backup_config_get', undefined, 'config', '', cb);
- },
+ startUpgrade: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'upgrade_start',
+ params: [ 'keep' ]
+ }),
- setBackupConfig: function(data, cb)
- {
- return rcall('luci2.system', 'backup_config_set', { data: data }, undefined, undefined, cb);
- },
+ cleanUpgrade: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'upgrade_clean'
+ }),
- canSetBackupConfig: function(cb)
- {
- return _luci2.rpc.access('ubus', 'luci2.system', 'backup_config_set', cb);
- },
+ restoreBackup: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'backup_restore'
+ }),
- listBackup: function(cb)
- {
- return rcall('luci2.system', 'backup_list', undefined, 'files', [ ], cb);
- },
+ cleanBackup: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'backup_clean'
+ }),
- performReboot: function(cb)
- {
- return rcall('luci2.system', 'reboot', undefined, undefined, undefined, cb);
- },
+ getBackupConfig: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'backup_config_get',
+ expect: { config: '' }
+ }),
- canPerformReboot: function(cb)
- {
- return _luci2.rpc.access('ubus', 'luci2.system', 'reboot', cb);
- }
+ setBackupConfig: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'backup_config_set',
+ params: [ 'data' ]
+ }),
+
+
+ listBackup: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'backup_list',
+ expect: { files: [ ] }
+ }),
+
+
+ testReset: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'reset_test',
+ expect: { supported: false }
+ }),
+
+ startReset: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'reset_start'
+ }),
+
+
+ performReboot: _luci2.rpc.declare({
+ object: 'luci2.system',
+ method: 'reboot'
+ })
};
this.opkg = {
- updateLists: function(cb)
- {
- return rcall('luci2.opkg', 'update', undefined, undefined, { }, cb);
- },
-
- _fetchPackages: function(action, offset, limit, pattern, cb)
+ updateLists: _luci2.rpc.declare({
+ object: 'luci2.opkg',
+ method: 'update',
+ expect: { '': { } }
+ }),
+
+ _allPackages: _luci2.rpc.declare({
+ object: 'luci2.opkg',
+ method: 'list',
+ params: [ 'offset', 'limit', 'pattern' ],
+ expect: { '': { } }
+ }),
+
+ _installedPackages: _luci2.rpc.declare({
+ object: 'luci2.opkg',
+ method: 'list_installed',
+ params: [ 'offset', 'limit', 'pattern' ],
+ expect: { '': { } }
+ }),
+
+ _findPackages: _luci2.rpc.declare({
+ object: 'luci2.opkg',
+ method: 'find',
+ params: [ 'offset', 'limit', 'pattern' ],
+ expect: { '': { } }
+ }),
+
+ _fetchPackages: function(action, offset, limit, pattern)
{
var packages = [ ];
- var reqlimit = Math.min(limit, 100);
- if (reqlimit <= 0)
- reqlimit = 100;
+ return action(offset, limit, pattern).then(function(list) {
+ if (!list.total || !list.packages)
+ return { length: 0, total: 0 };
- return _luci2.rpc.call('luci2.opkg', action, { offset: offset, limit: reqlimit, pattern: pattern }).then(function(response) {
- if (response[0] != 0 || !response[1] || !response[1].total)
- return retcb(cb, { length: 0, total: 0 });
-
- packages.push.apply(packages, response[1].packages);
- packages.total = response[1].total;
+ packages.push.apply(packages, list.packages);
+ packages.total = list.total;
if (limit <= 0)
- limit = response[1].total;
+ limit = list.total;
if (packages.length >= limit)
- return retcb(cb, packages);
+ return packages;
+
+ _luci2.rpc.batch();
- var requests = [ ];
for (var i = offset + packages.length; i < limit; i += 100)
- requests.push(['luci2.opkg', action, { offset: i, limit: (Math.min(i + 100, limit) % 100) || 100, pattern: pattern }]);
+ action(i, (Math.min(i + 100, limit) % 100) || 100, pattern);
- return _luci2.rpc.call(requests);
- }).then(function(responses) {
- for (var key in responses)
+ return _luci2.rpc.flush();
+ }).then(function(lists) {
+ for (var i = 0; i < lists.length; i++)
{
- var response = responses[key];
-
- if (response[0] != 0 || !response[1] || !response[1].packages)
+ if (!lists[i].total || !lists[i].packages)
continue;
- packages.push.apply(packages, response[1].packages);
- packages.total = response[1].total;
+ packages.push.apply(packages, lists[i].packages);
+ packages.total = lists[i].total;
}
- return retcb(cb, packages);
+ return packages;
});
},
- listPackages: function(offset, limit, pattern, cb)
+ listPackages: function(offset, limit, pattern)
{
- return _luci2.opkg._fetchPackages('list', offset, limit, pattern, cb);
+ return _luci2.opkg._fetchPackages(_luci2.opkg._allPackages, offset, limit, pattern);
},
- installedPackages: function(offset, limit, pattern, cb)
+ installedPackages: function(offset, limit, pattern)
{
- return _luci2.opkg._fetchPackages('list_installed', offset, limit, pattern, cb);
+ return _luci2.opkg._fetchPackages(_luci2.opkg._installedPackages, offset, limit, pattern);
},
- findPackages: function(offset, limit, pattern, cb)
+ findPackages: function(offset, limit, pattern)
{
- return _luci2.opkg._fetchPackages('find', offset, limit, pattern, cb);
+ return _luci2.opkg._fetchPackages(_luci2.opkg._findPackages, offset, limit, pattern);
},
- installPackage: function(name, cb)
- {
- return rcall('luci2.opkg', 'install', { 'package': name }, undefined, { }, cb);
- },
+ installPackage: _luci2.rpc.declare({
+ object: 'luci2.opkg',
+ method: 'install',
+ params: [ 'package' ],
+ expect: { '': { } }
+ }),
- removePackage: function(name, cb)
- {
- return rcall('luci2.opkg', 'remove', { 'package': name }, undefined, { }, cb);
- },
+ removePackage: _luci2.rpc.declare({
+ object: 'luci2.opkg',
+ method: 'remove',
+ params: [ 'package' ],
+ expect: { '': { } }
+ }),
- getConfig: function(cb)
- {
- return rcall('luci2.opkg', 'config_get', undefined, 'config', '', cb);
- },
+ getConfig: _luci2.rpc.declare({
+ object: 'luci2.opkg',
+ method: 'config_get',
+ expect: { config: '' }
+ }),
- setConfig: function(data, cb)
- {
- return rcall('luci2.opkg', 'config_set', { data: data }, undefined, undefined, cb);
- },
+ setConfig: _luci2.rpc.declare({
+ object: 'luci2.opkg',
+ method: 'config_set',
+ params: [ 'data' ]
+ })
+ };
- canInstallPackage: function(cb)
+ this.session = {
+
+ login: _luci2.rpc.declare({
+ object: 'session',
+ method: 'login',
+ params: [ 'username', 'password' ],
+ expect: { '': { } }
+ }),
+
+ access: _luci2.rpc.declare({
+ object: 'session',
+ method: 'access',
+ params: [ 'scope', 'object', 'function' ],
+ expect: { access: false }
+ }),
+
+ isAlive: function()
{
- return _luci2.rpc.access('ubus', 'luci2.opkg', 'install', cb);
+ return _luci2.session.access('ubus', 'session', 'access');
},
- canRemovePackage: function(cb)
+ startHeartbeat: function()
{
- return _luci2.rpc.access('ubus', 'luci2.opkg', 'remove', cb);
+ this._hearbeatInterval = window.setInterval(function() {
+ _luci2.session.isAlive().then(function(alive) {
+ if (!alive)
+ {
+ _luci2.session.stopHeartbeat();
+ _luci2.ui.login(true);
+ }
+
+ });
+ }, _luci2.globals.timeout * 2);
},
- canSetConfig: function(cb)
+ stopHeartbeat: function()
{
- return _luci2.rpc.access('ubus', 'luci2.opkg', 'config_set', cb);
+ if (typeof(this._hearbeatInterval) != 'undefined')
+ {
+ window.clearInterval(this._hearbeatInterval);
+ delete this._hearbeatInterval;
+ }
}
};
this.ui = {
+ saveScrollTop: function()
+ {
+ this._scroll_top = $(document).scrollTop();
+ },
+
+ restoreScrollTop: function()
+ {
+ if (typeof(this._scroll_top) == 'undefined')
+ return;
+
+ $(document).scrollTop(this._scroll_top);
+
+ delete this._scroll_top;
+ },
+
loading: function(enable)
{
var win = $(window);
var body = $('body');
- var div = _luci2._modal || (
- _luci2._modal = $('<div />')
+
+ var state = _luci2.ui._loading || (_luci2.ui._loading = {
+ modal: $('<div />')
.addClass('cbi-modal-loader')
.append($('<div />').text(_luci2.tr('Loading data...')))
.appendTo(body)
- );
+ });
if (enable)
{
body.css('padding', 0);
body.css('width', win.width());
body.css('height', win.height());
- div.css('width', win.width());
- div.css('height', win.height());
- div.show();
+ state.modal.css('width', win.width());
+ state.modal.css('height', win.height());
+ state.modal.show();
}
else
{
- div.hide();
+ state.modal.hide();
body.css('overflow', '');
body.css('padding', '');
body.css('width', '');
{
var win = $(window);
var body = $('body');
- var div = _luci2._dialog || (
- _luci2._dialog = $('<div />')
+
+ var state = _luci2.ui._dialog || (_luci2.ui._dialog = {
+ dialog: $('<div />')
.addClass('cbi-modal-dialog')
.append($('<div />')
.append($('<div />')
$(this).parent().parent().parent().hide();
}))))
.appendTo(body)
- );
+ });
if (typeof(options) != 'object')
options = { };
.css('width', '')
.css('height', '');
- _luci2._dialog.hide();
+ state.dialog.hide();
return;
}
- var cnt = div.children().children('div.cbi-modal-dialog-body');
- var ftr = div.children().children('div.cbi-modal-dialog-footer');
+ var cnt = state.dialog.children().children('div.cbi-modal-dialog-body');
+ var ftr = state.dialog.children().children('div.cbi-modal-dialog-footer');
ftr.empty();
.attr('disabled', true));
}
- div.find('div.cbi-modal-dialog-header').text(title);
- div.show();
+ state.dialog.find('div.cbi-modal-dialog-header').text(title);
+ state.dialog.show();
cnt
.css('max-height', Math.floor(win.height() * 0.70) + 'px')
.empty()
.append(content);
- div.children()
- .css('margin-top', -Math.floor(div.children().height() / 2) + 'px');
+ state.dialog.children()
+ .css('margin-top', -Math.floor(state.dialog.children().height() / 2) + 'px');
body.css('overflow', 'hidden');
body.css('padding', 0);
body.css('width', win.width());
body.css('height', win.height());
- div.css('width', win.width());
- div.css('height', win.height());
+ state.dialog.css('width', win.width());
+ state.dialog.css('height', win.height());
},
upload: function(title, content, options)
{
- var form = _luci2._upload || (
- _luci2._upload = $('<form />')
+ var state = _luci2.ui._upload || (_luci2.ui._upload = {
+ form: $('<form />')
.attr('method', 'post')
.attr('action', '/cgi-bin/luci-upload')
.attr('enctype', 'multipart/form-data')
.append($('<p />'))
.append($('<input />')
.attr('type', 'hidden')
- .attr('name', 'sessionid')
- .attr('value', _luci2.globals.sid))
+ .attr('name', 'sessionid'))
.append($('<input />')
.attr('type', 'hidden')
- .attr('name', 'filename')
- .attr('value', options.filename))
+ .attr('name', 'filename'))
.append($('<input />')
.attr('type', 'file')
.attr('name', 'filedata')
.attr('name', 'cbi-fileupload-frame')
.css('width', '1px')
.css('height', '1px')
- .css('visibility', 'hidden'))
- );
+ .css('visibility', 'hidden')),
- var finish = _luci2._upload_finish_cb || (
- _luci2._upload_finish_cb = function(ev) {
+ finish_cb: function(ev) {
$(this).off('load');
var body = (this.contentDocument || this.contentWindow.document).body;
$('<p />').text(_luci2.tr('In case of network problems try uploading the file again.'))
], { style: 'close' });
}
- else if (typeof(ev.data.cb) == 'function')
+ else if (typeof(state.success_cb) == 'function')
{
- ev.data.cb(json);
+ state.success_cb(json);
}
- }
- );
+ },
- var confirm = _luci2._upload_confirm_cb || (
- _luci2._upload_confirm_cb = function() {
- var d = _luci2._upload;
- var f = d.find('.cbi-input-file');
- var b = d.find('.progressbar');
- var p = d.find('p');
+ confirm_cb: function() {
+ var f = state.form.find('.cbi-input-file');
+ var b = state.form.find('.progressbar');
+ var p = state.form.find('p');
if (!f.val())
return;
- d.find('iframe').on('load', { cb: options.success }, finish);
- d.submit();
+ state.form.find('iframe').on('load', state.finish_cb);
+ state.form.submit();
f.hide();
b.show();
p.text(_luci2.tr('File upload in progress …'));
- _luci2._dialog.find('button').prop('disabled', true);
+ state.form.parent().parent().find('button').prop('disabled', true);
}
- );
+ });
+
+ state.form.find('.progressbar').hide();
+ state.form.find('.cbi-input-file').val('').show();
+ state.form.find('p').text(content || _luci2.tr('Select the file to upload and press "%s" to proceed.').format(_luci2.tr('Ok')));
+
+ state.form.find('[name=sessionid]').val(_luci2.globals.sid);
+ state.form.find('[name=filename]').val(options.filename);
- _luci2._upload.find('.progressbar').hide();
- _luci2._upload.find('.cbi-input-file').val('').show();
- _luci2._upload.find('p').text(content || _luci2.tr('Select the file to upload and press "%s" to proceed.').format(_luci2.tr('Ok')));
+ state.success_cb = options.success;
- _luci2.ui.dialog(title || _luci2.tr('File upload'), _luci2._upload, {
+ _luci2.ui.dialog(title || _luci2.tr('File upload'), state.form, {
style: 'confirm',
- confirm: confirm
+ confirm: state.confirm_cb
});
},
//}).then(function() {
images.on('load', function() {
var url = this.getAttribute('url');
- _luci2.rpc.access('ubus', 'session', 'access').then(function(response) {
- if (response[0] == 0)
+ _luci2.session.isAlive().then(function(access) {
+ if (access)
{
window.clearTimeout(timeout);
window.clearInterval(interval);
login: function(invalid)
{
- if (!_luci2._login_deferred || _luci2._login_deferred.state() != 'pending')
- _luci2._login_deferred = $.Deferred();
-
- /* try to find sid from hash */
- var sid = _luci2.getHash('id');
- if (sid && sid.match(/^[a-f0-9]{32}$/))
- {
- _luci2.globals.sid = sid;
- _luci2.rpc.access('ubus', 'session', 'access').then(function(response) {
- if (response[0] == 0)
- {
- _luci2._login_deferred.resolve();
- }
- else
- {
- _luci2.setHash('id', undefined);
- _luci2.ui.login();
- }
- });
-
- return _luci2._login_deferred;
- }
-
- var form = _luci2._login || (
- _luci2._login = $('<div />')
+ var state = _luci2.ui._login || (_luci2.ui._login = {
+ form: $('<form />')
+ .attr('target', '')
+ .attr('method', 'post')
.append($('<p />')
.addClass('alert-message')
.text(_luci2.tr('Wrong username or password given!')))
.attr('type', 'text')
.attr('name', 'username')
.attr('value', 'root')
- .addClass('cbi-input-text'))))
+ .addClass('cbi-input-text')
+ .keypress(function(ev) {
+ if (ev.which == 10 || ev.which == 13)
+ state.confirm_cb();
+ }))))
.append($('<p />')
.append($('<label />')
.text(_luci2.tr('Password'))
.append($('<input />')
.attr('type', 'password')
.attr('name', 'password')
- .addClass('cbi-input-password'))))
+ .addClass('cbi-input-password')
+ .keypress(function(ev) {
+ if (ev.which == 10 || ev.which == 13)
+ state.confirm_cb();
+ }))))
.append($('<p />')
- .text(_luci2.tr('Enter your username and password above, then click "%s" to proceed.').format(_luci2.tr('Ok'))))
- );
+ .text(_luci2.tr('Enter your username and password above, then click "%s" to proceed.').format(_luci2.tr('Ok')))),
- var response = _luci2._login_response_cb || (
- _luci2._login_response_cb = function(data) {
- if (typeof(data) == 'object' && typeof(data.sessionid) == 'string')
+ response_cb: function(response) {
+ if (!response.ubus_rpc_session)
{
- _luci2.globals.sid = data.sessionid;
- _luci2.setHash('id', _luci2.globals.sid);
-
- _luci2.ui.dialog(false);
- _luci2._login_deferred.resolve();
+ _luci2.ui.login(true);
}
else
{
- _luci2.ui.login(true);
+ _luci2.globals.sid = response.ubus_rpc_session;
+ _luci2.setHash('id', _luci2.globals.sid);
+ _luci2.session.startHeartbeat();
+ _luci2.ui.dialog(false);
+ state.deferred.resolve();
}
- }
- );
+ },
- var confirm = _luci2._login_confirm_cb || (
- _luci2._login_confirm_cb = function() {
- var d = _luci2._login;
- var u = d.find('[name=username]').val();
- var p = d.find('[name=password]').val();
+ confirm_cb: function() {
+ var u = state.form.find('[name=username]').val();
+ var p = state.form.find('[name=password]').val();
if (!u)
return;
], { style: 'wait' }
);
- $.ajax('/cgi-bin/luci-login', {
- type: 'POST',
- cache: false,
- data: { username: u, password: p },
- dataType: 'json',
- success: response,
- error: response
- });
+ _luci2.globals.sid = '00000000000000000000000000000000';
+ _luci2.session.login(u, p).then(state.response_cb);
}
- );
+ });
+
+ if (!state.deferred || state.deferred.state() != 'pending')
+ state.deferred = $.Deferred();
+
+ /* try to find sid from hash */
+ var sid = _luci2.getHash('id');
+ if (sid && sid.match(/^[a-f0-9]{32}$/))
+ {
+ _luci2.globals.sid = sid;
+ _luci2.session.isAlive().then(function(access) {
+ if (access)
+ {
+ _luci2.session.startHeartbeat();
+ state.deferred.resolve();
+ }
+ else
+ {
+ _luci2.setHash('id', undefined);
+ _luci2.ui.login();
+ }
+ });
+
+ return state.deferred;
+ }
if (invalid)
- form.find('.alert-message').show();
+ state.form.find('.alert-message').show();
else
- form.find('.alert-message').hide();
+ state.form.find('.alert-message').hide();
- _luci2.ui.dialog(_luci2.tr('Authorization Required'), form, {
+ _luci2.ui.dialog(_luci2.tr('Authorization Required'), state.form, {
style: 'confirm',
- confirm: confirm
+ confirm: state.confirm_cb
});
- return _luci2._login_deferred;
+ state.form.find('[name=password]').focus();
+
+ return state.deferred;
+ },
+
+ cryptPassword: _luci2.rpc.declare({
+ object: 'luci2.ui',
+ method: 'crypt',
+ params: [ 'data' ],
+ expect: { crypt: '' }
+ }),
+
+
+ _acl_merge_scope: function(acl_scope, scope)
+ {
+ if ($.isArray(scope))
+ {
+ for (var i = 0; i < scope.length; i++)
+ acl_scope[scope[i]] = true;
+ }
+ else if ($.isPlainObject(scope))
+ {
+ for (var object_name in scope)
+ {
+ if (!$.isArray(scope[object_name]))
+ continue;
+
+ var acl_object = acl_scope[object_name] || (acl_scope[object_name] = { });
+
+ for (var i = 0; i < scope[object_name].length; i++)
+ acl_object[scope[object_name][i]] = true;
+ }
+ }
+ },
+
+ _acl_merge_permission: function(acl_perm, perm)
+ {
+ if ($.isPlainObject(perm))
+ {
+ for (var scope_name in perm)
+ {
+ var acl_scope = acl_perm[scope_name] || (acl_perm[scope_name] = { });
+ this._acl_merge_scope(acl_scope, perm[scope_name]);
+ }
+ }
+ },
+
+ _acl_merge_group: function(acl_group, group)
+ {
+ if ($.isPlainObject(group))
+ {
+ if (!acl_group.description)
+ acl_group.description = group.description;
+
+ if (group.read)
+ {
+ var acl_perm = acl_group.read || (acl_group.read = { });
+ this._acl_merge_permission(acl_perm, group.read);
+ }
+
+ if (group.write)
+ {
+ var acl_perm = acl_group.write || (acl_group.write = { });
+ this._acl_merge_permission(acl_perm, group.write);
+ }
+ }
},
- renderMainMenu: function()
+ _acl_merge_tree: function(acl_tree, tree)
{
- return rcall('luci2.ui', 'menu', undefined, 'menu', { }, function(entries) {
+ if ($.isPlainObject(tree))
+ {
+ for (var group_name in tree)
+ {
+ var acl_group = acl_tree[group_name] || (acl_tree[group_name] = { });
+ this._acl_merge_group(acl_group, tree[group_name]);
+ }
+ }
+ },
+
+ listAvailableACLs: _luci2.rpc.declare({
+ object: 'luci2.ui',
+ method: 'acls',
+ expect: { acls: [ ] },
+ filter: function(trees) {
+ var acl_tree = { };
+ for (var i = 0; i < trees.length; i++)
+ _luci2.ui._acl_merge_tree(acl_tree, trees[i]);
+ return acl_tree;
+ }
+ }),
+
+ renderMainMenu: _luci2.rpc.declare({
+ object: 'luci2.ui',
+ method: 'menu',
+ expect: { menu: { } },
+ filter: function(entries) {
_luci2.globals.mainMenu = new _luci2.ui.menu();
_luci2.globals.mainMenu.entries(entries);
$('#mainmenu')
.empty()
.append(_luci2.globals.mainMenu.render(0, 1));
- });
- },
+ }
+ }),
renderViewMenu: function()
{
.append(_luci2.globals.mainMenu.render(2, 900));
},
- renderView: function(node)
+ renderView: function()
{
+ var node = arguments[0];
var name = node.view.split(/\//).join('.');
+ var args = [ ];
+
+ for (var i = 1; i < arguments.length; i++)
+ args.push(arguments[i]);
+
+ if (_luci2.globals.currentView)
+ _luci2.globals.currentView.finish();
_luci2.ui.renderViewMenu();
_luci2.setHash('view', node.view);
if (_luci2._views[name] instanceof _luci2.ui.view)
- return _luci2._views[name].render();
+ {
+ _luci2.globals.currentView = _luci2._views[name];
+ return _luci2._views[name].render.apply(_luci2._views[name], args);
+ }
+
+ var url = _luci2.globals.resource + '/view/' + name + '.js';
- return $.ajax(_luci2.globals.resource + '/view/' + name + '.js', {
+ return $.ajax(url, {
method: 'GET',
cache: true,
dataType: 'text'
}).then(function(data) {
try {
- var viewConstructor = (new Function(['L', '$'], 'return ' + data))(_luci2, $);
+ var viewConstructorSource = (
+ '(function(L, $) { ' +
+ 'return %s' +
+ '})(_luci2, $);\n\n' +
+ '//@ sourceURL=%s'
+ ).format(data, url);
+
+ var viewConstructor = eval(viewConstructorSource);
_luci2._views[name] = new viewConstructor({
name: name,
acls: node.write || { }
});
- return _luci2._views[name].render();
+ _luci2.globals.currentView = _luci2._views[name];
+ return _luci2._views[name].render.apply(_luci2._views[name], args);
}
- catch(e) { };
+ catch(e) {
+ alert('Unable to instantiate view "%s": %s'.format(url, e));
+ };
return $.Deferred().resolve();
});
},
+ updateHostname: function()
+ {
+ return _luci2.system.getBoardInfo().then(function(info) {
+ if (info.hostname)
+ $('#hostname').text(info.hostname);
+ });
+ },
+
+ updateChanges: function()
+ {
+ return _luci2.uci.changes().then(function(changes) {
+ var n = 0;
+ var html = '';
+
+ for (var config in changes)
+ {
+ var log = [ ];
+
+ for (var i = 0; i < changes[config].length; i++)
+ {
+ var c = changes[config][i];
+
+ switch (c[0])
+ {
+ case 'order':
+ break;
+
+ case 'remove':
+ if (c.length < 3)
+ log.push('uci delete %s.<del>%s</del>'.format(config, c[1]));
+ else
+ log.push('uci delete %s.%s.<del>%s</del>'.format(config, c[1], c[2]));
+ break;
+
+ case 'rename':
+ if (c.length < 4)
+ log.push('uci rename %s.<ins>%s=<strong>%s</strong></ins>'.format(config, c[1], c[2], c[3]));
+ else
+ log.push('uci rename %s.%s.<ins>%s=<strong>%s</strong></ins>'.format(config, c[1], c[2], c[3], c[4]));
+ break;
+
+ case 'add':
+ log.push('uci add %s <ins>%s</ins> (= <ins><strong>%s</strong></ins>)'.format(config, c[2], c[1]));
+ break;
+
+ case 'list-add':
+ log.push('uci add_list %s.%s.<ins>%s=<strong>%s</strong></ins>'.format(config, c[1], c[2], c[3], c[4]));
+ break;
+
+ case 'list-del':
+ log.push('uci del_list %s.%s.<del>%s=<strong>%s</strong></del>'.format(config, c[1], c[2], c[3], c[4]));
+ break;
+
+ case 'set':
+ if (c.length < 4)
+ log.push('uci set %s.<ins>%s=<strong>%s</strong></ins>'.format(config, c[1], c[2]));
+ else
+ log.push('uci set %s.%s.<ins>%s=<strong>%s</strong></ins>'.format(config, c[1], c[2], c[3], c[4]));
+ break;
+ }
+ }
+
+ html += '<code>/etc/config/%s</code><pre class="uci-changes">%s</pre>'.format(config, log.join('\n'));
+ n += changes[config].length;
+ }
+
+ if (n > 0)
+ $('#changes')
+ .empty()
+ .show()
+ .append($('<a />')
+ .attr('href', '#')
+ .addClass('label')
+ .addClass('notice')
+ .text(_luci2.trcp('Pending configuration changes', '1 change', '%d changes', n).format(n))
+ .click(function(ev) {
+ _luci2.ui.dialog(_luci2.tr('Staged configuration changes'), html, { style: 'close' });
+ ev.preventDefault();
+ }));
+ else
+ $('#changes')
+ .hide();
+ });
+ },
+
init: function()
{
_luci2.ui.loading(true);
$.when(
+ _luci2.ui.updateHostname(),
+ _luci2.ui.updateChanges(),
_luci2.ui.renderMainMenu()
).then(function() {
_luci2.ui.renderView(_luci2.globals.defaultNode).then(function() {
insertInto: function(id) {
return $(id).empty().append(this.render());
+ },
+
+ appendTo: function(id) {
+ return $(id).append(this.render());
}
});
container.append($('<div />').addClass('cbi-map-descr').append(this.description));
var self = this;
+ var args = [ ];
+
+ for (var i = 0; i < arguments.length; i++)
+ args.push(arguments[i]);
+
return this._fetch_template().then(function() {
- return _luci2.deferrable(self.execute());
+ return _luci2.deferrable(self.execute.apply(self, args));
});
+ },
+
+ repeat: function(func, interval)
+ {
+ var self = this;
+
+ if (!self._timeouts)
+ self._timeouts = [ ];
+
+ var index = self._timeouts.length;
+
+ if (typeof(interval) != 'number')
+ interval = 5000;
+
+ var setTimer, runTimer;
+
+ setTimer = function() {
+ self._timeouts[index] = window.setTimeout(runTimer, interval);
+ };
+
+ runTimer = function() {
+ _luci2.deferrable(func.call(self)).then(setTimer);
+ };
+
+ runTimer();
+ },
+
+ finish: function()
+ {
+ if ($.isArray(this._timeouts))
+ {
+ for (var i = 0; i < this._timeouts.length; i++)
+ window.clearTimeout(this._timeouts[i]);
+
+ delete this._timeouts;
+ }
}
});
var child = this.firstChildView(nodes[i]);
if (child)
{
- $.extend(node, child);
+ for (var key in child)
+ if (!node.hasOwnProperty(key) && child.hasOwnProperty(key))
+ node[key] = child[key];
+
return node;
}
}
row: function(values)
{
- if (isa(values, 'Array'))
+ if ($.isArray(values))
{
this._rows.push(values);
}
- else if (isa(values, 'Object'))
+ else if ($.isPlainObject(values))
{
var v = [ ];
for (var i = 0; i < this.options.columns.length; i++)
this.ui.devicebadge = AbstractWidget.extend({
render: function()
{
- var dev = this.options.l3_device || this.options.device || '?';
+ var l2dev = this.options.l2_device || this.options.device;
+ var l3dev = this.options.l3_device;
+ var dev = l3dev || l2dev || '?';
var span = document.createElement('span');
span.className = 'ifacebadge';
var type = 'ethernet';
var desc = _luci2.tr('Ethernet device');
- if (this.options.l3_device != this.options.device)
+ if (l3dev != l2dev)
{
type = 'tunnel';
desc = _luci2.tr('Tunnel interface');
}
}
+ validation.i18n('Must be a valid IPv6 address');
return false;
},
};
- var AbstractValue = AbstractWidget.extend({
+ this.cbi.AbstractValue = AbstractWidget.extend({
init: function(name, options)
{
this.name = name;
{
if (typeof(d[i]) == 'string')
dep[d[i]] = true;
- else if (d[i] instanceof AbstractValue)
+ else if (d[i] instanceof _luci2.cbi.AbstractValue)
dep[d[i].name] = true;
}
}
- else if (d instanceof AbstractValue)
+ else if (d instanceof _luci2.cbi.AbstractValue)
{
dep = { };
dep[d.name] = (typeof(v) == 'undefined') ? true : v;
}
});
- this.cbi.CheckboxValue = AbstractValue.extend({
+ this.cbi.CheckboxValue = this.cbi.AbstractValue.extend({
widget: function(sid)
{
var o = this.options;
if (chg)
{
- val = val ? this.options.enabled : this.options.disabled;
-
if (this.options.optional && val == this.options.initial)
this.map.set(uci.config, uci.section, uci.option, undefined);
else
- this.map.set(uci.config, uci.section, uci.option, val);
+ this.map.set(uci.config, uci.section, uci.option, val ? this.options.enabled : this.options.disabled);
}
return chg;
}
});
- this.cbi.InputValue = AbstractValue.extend({
+ this.cbi.InputValue = this.cbi.AbstractValue.extend({
widget: function(sid)
{
var i = $('<input />')
}
});
- this.cbi.PasswordValue = AbstractValue.extend({
+ this.cbi.PasswordValue = this.cbi.AbstractValue.extend({
widget: function(sid)
{
var i = $('<input />')
}
});
- this.cbi.ListValue = AbstractValue.extend({
+ this.cbi.ListValue = this.cbi.AbstractValue.extend({
widget: function(sid)
{
var s = $('<select />');
}
});
- this.cbi.ComboBox = AbstractValue.extend({
+ this.cbi.ComboBox = this.cbi.AbstractValue.extend({
_change: function(ev)
{
var s = ev.target;
}
});
- this.cbi.DummyValue = AbstractValue.extend({
+ this.cbi.DummyValue = this.cbi.AbstractValue.extend({
widget: function(sid)
{
return $('<div />')
}
});
- this.cbi.NetworkList = AbstractValue.extend({
+ this.cbi.NetworkList = this.cbi.AbstractValue.extend({
load: function(sid)
{
var self = this;
if (!self.interfaces)
{
self.interfaces = [ ];
- return _luci2.network.getNetworkStatus(function(ifaces) {
+ return _luci2.network.getNetworkStatus().then(function(ifaces) {
self.interfaces = ifaces;
self = null;
});
type = 'wifi';
desc = _luci2.tr('Wireless Network');
}
- else if (dev.name.indexOf('.') > 0)
+ else if (dev.device.indexOf('.') > 0)
{
type = 'vlan';
desc = _luci2.tr('VLAN interface');
return $('<img />')
.attr('src', _luci2.globals.resource + '/icons/' + type + (dev.up ? '' : '_disabled') + '.png')
- .attr('title', '%s (%s)'.format(desc, dev.name));
+ .attr('title', '%s (%s)'.format(desc, dev.device));
},
widget: function(sid)
var iface = this.interfaces[i];
var badge = $('<span />')
.addClass('ifacebadge')
- .text('%s: '.format(iface.name));
+ .text('%s: '.format(iface['interface']));
- if (iface.subdevices)
- for (var j = 0; j < iface.subdevices.length; j++)
- badge.append(this._device_icon(iface.subdevices[j]));
+ if (iface.device && iface.device.subdevices)
+ for (var j = 0; j < iface.device.subdevices.length; j++)
+ badge.append(this._device_icon(iface.device.subdevices[j]));
else if (iface.device)
badge.append(this._device_icon(iface.device));
else
.append($('<input />')
.attr('name', itype + id)
.attr('type', itype)
- .attr('value', iface.name)
- .prop('checked', !!check[iface.name])
+ .attr('value', iface['interface'])
+ .prop('checked', !!check[iface['interface']])
.addClass('cbi-input-' + itype))
.append(badge))
.appendTo(ul);
});
- var AbstractSection = AbstractWidget.extend({
+ this.cbi.AbstractSection = AbstractWidget.extend({
id: function()
{
var s = [ arguments[0], this.map.uci_package, this.uci_type ];
var w = widget ? new widget(name, options) : null;
- if (!(w instanceof AbstractValue))
+ if (!(w instanceof _luci2.cbi.AbstractValue))
throw 'Widget must be an instance of AbstractValue';
w.section = this;
}
});
- this.cbi.TypedSection = AbstractSection.extend({
+ this.cbi.TypedSection = this.cbi.AbstractSection.extend({
init: function(uci_type, options)
{
this.uci_type = uci_type;
if (addb.prop('disabled') || name === '')
return;
+ _luci2.ui.saveScrollTop();
+
self.active_panel = -1;
self.map.save();
self.add(name);
self.map.redraw();
+
+ _luci2.ui.restoreScrollTop();
},
_remove: function(ev)
var self = ev.data.self;
var sid = ev.data.sid;
+ if (ev.data.index == (self.sections().length - 1))
+ self.active_panel = -1;
+
+ _luci2.ui.saveScrollTop();
+
self.map.save();
self.remove(sid);
self.map.redraw();
+ _luci2.ui.restoreScrollTop();
+
ev.stopPropagation();
},
for (var i = 0; i < this.options.teasers.length; i++)
{
var f = this.options.teasers[i];
- if (f instanceof AbstractValue)
+ if (f instanceof _luci2.cbi.AbstractValue)
tf.push(f);
- else if (typeof(f) == 'string' && this.fields[f] instanceof AbstractValue)
+ else if (typeof(f) == 'string' && this.fields[f] instanceof _luci2.cbi.AbstractValue)
tf.push(this.fields[f]);
}
}
return add;
},
- _render_remove: function(sid)
+ _render_remove: function(sid, index)
{
var text = _luci2.tr('Remove');
var ttip = _luci2.tr('Remove this section');
.addClass('cbi-button')
.addClass('cbi-button-remove')
.val(text).attr('title', ttip)
- .click({ self: this, sid: sid }, this._remove);
+ .click({ self: this, sid: sid, index: index }, this._remove);
},
_render_caption: function(sid)
$('<div />')
.addClass('cbi-section-remove')
.addClass('right')
- .append(this._render_remove(sid))
+ .append(this._render_remove(sid, panel_index))
.appendTo(head);
var body = $('<div />')
deletes: { }
};
- this.active_panel = 0;
+ if (typeof(this.active_panel) == 'undefined')
+ this.active_panel = 0;
var packages = { };
packages[this.uci_package] = true;
- for (var p in packages)
- packages[p] = ['uci', 'get', { config: p }];
-
- var load_cb = this._load_cb || (this._load_cb = $.proxy(function(responses) {
- for (var p in responses)
+ var load_cb = this._load_cb || (this._load_cb = $.proxy(function(packages) {
+ for (var i = 0; i < packages.length; i++)
{
- if (responses[p][0] != 0 || !responses[p][1] || !responses[p][1].values)
- continue;
-
- this.uci.values[p] = responses[p][1].values;
+ this.uci.values[packages[i]['.package']] = packages[i];
+ delete packages[i]['.package'];
}
var deferreds = [ _luci2.deferrable(this.options.prepare()) ];
for (var j = 0; j < s.length; j++)
{
var rv = this.sections[i].fields[f].load(s[j]['.name']);
- if (_luci2.deferred(rv))
+ if (_luci2.isDeferred(rv))
deferreds.push(rv);
}
}
return $.when.apply($, deferreds);
}, this));
- return _luci2.rpc.call(packages).then(load_cb);
+ _luci2.rpc.batch();
+
+ for (var pkg in packages)
+ _luci2.uci.get_all(pkg);
+
+ return _luci2.rpc.flush().then(load_cb);
},
render: function()
{
var w = widget ? new widget(uci_type, options) : null;
- if (!(w instanceof AbstractSection))
+ if (!(w instanceof _luci2.cbi.AbstractSection))
throw 'Widget must be an instance of AbstractSection';
w.map = this;
for (var j = 0; j < s.length; j++)
{
var rv = this.sections[i].fields[f].save(s[j]['.name']);
- if (_luci2.deferred(rv))
+ if (_luci2.isDeferred(rv))
deferreds.push(rv);
}
}
return _luci2.deferrable();
var send_cb = this._send_cb || (this._send_cb = $.proxy(function() {
- var requests = [ ];
+ _luci2.rpc.batch();
if (this.uci.creates)
for (var c in this.uci.creates)
for (var k in this.uci.creates[c][s])
{
if (k == '.type')
- r.type = this.uci.creates[i][k];
+ r.type = this.uci.creates[c][s][k];
else if (k == '.create')
- r.name = this.uci.creates[i][k];
+ r.name = this.uci.creates[c][s][k];
else if (k.charAt(0) != '.')
- r.values[k] = this.uci.creates[i][k];
+ r.values[k] = this.uci.creates[c][s][k];
}
- requests.push(['uci', 'add', r]);
+
+ _luci2.uci.add(r.config, r.type, r.name, r.values);
}
if (this.uci.changes)
for (var c in this.uci.changes)
for (var s in this.uci.changes[c])
- requests.push(['uci', 'set', {
- config: c,
- section: s,
- values: this.uci.changes[c][s]
- }]);
+ _luci2.uci.set(c, s, this.uci.changes[c][s]);
if (this.uci.deletes)
for (var c in this.uci.deletes)
for (var s in this.uci.deletes[c])
{
var o = this.uci.deletes[c][s];
- requests.push(['uci', 'delete', {
- config: c,
- section: s,
- options: (o === true) ? undefined : o
- }]);
+ _luci2.uci['delete'](c, s, (o === true) ? undefined : o);
}
- return _luci2.rpc.call(requests);
+ return _luci2.rpc.flush().then(function() {
+ return _luci2.ui.updateChanges();
+ });
}, this));
var self = this;
+ _luci2.ui.saveScrollTop();
_luci2.ui.loading(true);
return this.save().then(send_cb).then(function() {
self = null;
_luci2.ui.loading(false);
- });
- },
-
- dialog: function(id)
- {
- var d = $('<div />');
- var p = $('<p />');
-
- $('<img />')
- .attr('src', _luci2.globals.resource + '/icons/loading.gif')
- .css('vertical-align', 'middle')
- .css('padding-right', '10px')
- .appendTo(p);
-
- p.append(_luci2.tr('Loading data...'));
-
- p.appendTo(d);
- d.appendTo(id);
-
- return d.dialog({
- modal: true,
- draggable: false,
- resizable: false,
- height: 90,
- open: function() {
- $(this).parent().children('.ui-dialog-titlebar').hide();
- }
+ _luci2.ui.restoreScrollTop();
});
},