X-Git-Url: http://git.archive.openwrt.org/?p=project%2Fluci2%2Fui.git;a=blobdiff_plain;f=luci2%2Fhtdocs%2Fluci2%2Fluci2.js;h=84ebf847040c5efc09ae6320f1820b3ccf9e42e6;hp=4501181a0831e38be3883d3575f2b7a5e1f52171;hb=a5b578d075d148ba572d7fbfb012792d58e5ff7f;hpb=0ce8022fb7701cc270c3011ae9534dc05633aed8 diff --git a/luci2/htdocs/luci2/luci2.js b/luci2/htdocs/luci2/luci2.js index 4501181..84ebf84 100644 --- a/luci2/htdocs/luci2/luci2.js +++ b/luci2/htdocs/luci2/luci2.js @@ -387,7 +387,7 @@ function LuCI2() }; this.globals = { - timeout: 3000, + timeout: 15000, resource: '/luci2', sid: '00000000000000000000000000000000' }; @@ -584,13 +584,44 @@ function LuCI2() }, - changes: _luci2.rpc.declare({ + 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) + { + if (typeof(config) == 'string') + return this._changes(config); + + var configlist; + return this.configs().then(function(configs) { + _luci2.rpc.batch(); + configlist = configs; + + for (var i = 0; i < configs.length; i++) + _luci2.uci._changes(configs[i]); + + 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 rv; + }); + }, + commit: _luci2.rpc.declare({ object: 'uci', method: 'commit', @@ -757,13 +788,13 @@ function LuCI2() var net = nets[i] = networks[i]; var dev = net.l3_device || net.l2_device; if (dev) - net.device = devs[dev] = { }; + net.device = devs[dev] || (devs[dev] = { }); } _luci2.rpc.batch(); for (var dev in devs) - _luci2.network.listDeviceNamestatus(dev); + _luci2.network.getDeviceStatus(dev); return _luci2.rpc.flush(); }).then(function(devices) { @@ -786,7 +817,7 @@ function LuCI2() if (!devs[brm[j]]) { devs[brm[j]] = { }; - _luci2.network.listDeviceNamestatus(brm[j]); + _luci2.network.getDeviceStatus(brm[j]); } devs[devices[i]['device']].subdevices[j] = devs[brm[j]]; @@ -836,6 +867,9 @@ function LuCI2() for (var i = 0; i < interfaces.length; i++) { + if (!interfaces[i].route) + continue; + for (var j = 0; j < interfaces[i].route.length; j++) { var rt = interfaces[i].route[j]; @@ -897,7 +931,7 @@ function LuCI2() } }), - listDeviceNamestatus: _luci2.rpc.declare({ + getDeviceStatus: _luci2.rpc.declare({ object: 'network.device', method: 'status', params: [ 'name' ], @@ -912,6 +946,73 @@ function LuCI2() 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 } } }) }; @@ -1292,6 +1393,18 @@ function LuCI2() }), + 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' @@ -1452,16 +1565,32 @@ function LuCI2() 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 = $('
') + + var state = _luci2.ui._loading || (_luci2.ui._loading = { + modal: $('
') .addClass('cbi-modal-loader') .append($('
').text(_luci2.tr('Loading data...'))) .appendTo(body) - ); + }); if (enable) { @@ -1469,13 +1598,13 @@ function LuCI2() 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', ''); @@ -1487,8 +1616,9 @@ function LuCI2() { var win = $(window); var body = $('body'); - var div = _luci2._dialog || ( - _luci2._dialog = $('
') + + var state = _luci2.ui._dialog || (_luci2.ui._dialog = { + dialog: $('
') .addClass('cbi-modal-dialog') .append($('
') .append($('
') @@ -1510,7 +1640,7 @@ function LuCI2() $(this).parent().parent().parent().hide(); })))) .appendTo(body) - ); + }); if (typeof(options) != 'object') options = { }; @@ -1523,13 +1653,13 @@ function LuCI2() .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(); @@ -1560,29 +1690,29 @@ function LuCI2() .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 = $('
') + var state = _luci2.ui._upload || (_luci2.ui._upload = { + form: $('') .attr('method', 'post') .attr('action', '/cgi-bin/luci-upload') .attr('enctype', 'multipart/form-data') @@ -1590,12 +1720,10 @@ function LuCI2() .append($('

')) .append($('') .attr('type', 'hidden') - .attr('name', 'sessionid') - .attr('value', _luci2.globals.sid)) + .attr('name', 'sessionid')) .append($('') .attr('type', 'hidden') - .attr('name', 'filename') - .attr('value', options.filename)) + .attr('name', 'filename')) .append($('') .attr('type', 'file') .attr('name', 'filedata') @@ -1610,11 +1738,9 @@ function LuCI2() .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; @@ -1639,41 +1765,43 @@ function LuCI2() $('

').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'))); - _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.form.find('[name=sessionid]').val(_luci2.globals.sid); + state.form.find('[name=filename]').val(options.filename); - _luci2.ui.dialog(title || _luci2.tr('File upload'), _luci2._upload, { + state.success_cb = options.success; + + _luci2.ui.dialog(title || _luci2.tr('File upload'), state.form, { style: 'confirm', - confirm: confirm + confirm: state.confirm_cb }); }, @@ -1751,32 +1879,10 @@ function LuCI2() 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.session.isAlive().then(function(access) { - if (access) - { - _luci2.session.startHeartbeat(); - _luci2._login_deferred.resolve(); - } - else - { - _luci2.setHash('id', undefined); - _luci2.ui.login(); - } - }); - - return _luci2._login_deferred; - } - - var form = _luci2._login || ( - _luci2._login = $('

') + var state = _luci2.ui._login || (_luci2.ui._login = { + form: $('') + .attr('target', '') + .attr('method', 'post') .append($('

') .addClass('alert-message') .text(_luci2.tr('Wrong username or password given!'))) @@ -1788,7 +1894,11 @@ function LuCI2() .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($('

') .append($('

') - .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_cb = _luci2._login_response_cb || ( - _luci2._login_response_cb = function(response) { + response_cb: function(response) { if (!response.ubus_rpc_session) { _luci2.ui.login(true); @@ -1813,16 +1925,13 @@ function LuCI2() _luci2.setHash('id', _luci2.globals.sid); _luci2.session.startHeartbeat(); _luci2.ui.dialog(false); - _luci2._login_deferred.resolve(); + state.deferred.resolve(); } - } - ); + }, - var confirm_cb = _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; @@ -1840,23 +1949,56 @@ function LuCI2() ); _luci2.globals.sid = '00000000000000000000000000000000'; - _luci2.session.login(u, p).then(response_cb); + _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_cb + 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) { @@ -1962,6 +2104,9 @@ function LuCI2() { var name = node.view.split(/\//).join('.'); + if (_luci2.globals.currentView) + _luci2.globals.currentView.finish(); + _luci2.ui.renderViewMenu(); if (!_luci2._views) @@ -1970,34 +2115,136 @@ function LuCI2() _luci2.setHash('view', node.view); if (_luci2._views[name] instanceof _luci2.ui.view) + { + _luci2.globals.currentView = _luci2._views[name]; return _luci2._views[name].render(); + } + + 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 || { } }); + _luci2.globals.currentView = _luci2._views[name]; return _luci2._views[name].render(); } - 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.%s'.format(config, c[1])); + else + log.push('uci delete %s.%s.%s'.format(config, c[1], c[2])); + break; + + case 'rename': + if (c.length < 4) + log.push('uci rename %s.%s=%s'.format(config, c[1], c[2], c[3])); + else + log.push('uci rename %s.%s.%s=%s'.format(config, c[1], c[2], c[3], c[4])); + break; + + case 'add': + log.push('uci add %s %s (= %s)'.format(config, c[2], c[1])); + break; + + case 'list-add': + log.push('uci add_list %s.%s.%s=%s'.format(config, c[1], c[2], c[3], c[4])); + break; + + case 'list-del': + log.push('uci del_list %s.%s.%s=%s'.format(config, c[1], c[2], c[3], c[4])); + break; + + case 'set': + if (c.length < 4) + log.push('uci set %s.%s=%s'.format(config, c[1], c[2])); + else + log.push('uci set %s.%s.%s=%s'.format(config, c[1], c[2], c[3], c[4])); + break; + } + } + + html += '/etc/config/%s

%s
'.format(config, log.join('\n')); + n += changes[config].length; + } + + if (n > 0) + $('#changes') + .empty() + .show() + .append($('') + .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() { @@ -2076,6 +2323,42 @@ function LuCI2() return this._fetch_template().then(function() { return _luci2.deferrable(self.execute()); }); + }, + + 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; + } } }); @@ -2129,7 +2412,10 @@ function LuCI2() 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; } } @@ -2414,7 +2700,9 @@ function LuCI2() 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'; @@ -2455,7 +2743,7 @@ function LuCI2() 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'); @@ -2705,6 +2993,7 @@ function LuCI2() } } + validation.i18n('Must be a valid IPv6 address'); return false; }, @@ -2977,7 +3266,7 @@ function LuCI2() }; - var AbstractValue = AbstractWidget.extend({ + this.cbi.AbstractValue = AbstractWidget.extend({ init: function(name, options) { this.name = name; @@ -3243,11 +3532,11 @@ function LuCI2() { 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; @@ -3355,7 +3644,7 @@ function LuCI2() } }); - this.cbi.CheckboxValue = AbstractValue.extend({ + this.cbi.CheckboxValue = this.cbi.AbstractValue.extend({ widget: function(sid) { var o = this.options; @@ -3408,19 +3697,17 @@ function LuCI2() 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 = $('') @@ -3433,7 +3720,7 @@ function LuCI2() } }); - this.cbi.PasswordValue = AbstractValue.extend({ + this.cbi.PasswordValue = this.cbi.AbstractValue.extend({ widget: function(sid) { var i = $('') @@ -3462,7 +3749,7 @@ function LuCI2() } }); - this.cbi.ListValue = AbstractValue.extend({ + this.cbi.ListValue = this.cbi.AbstractValue.extend({ widget: function(sid) { var s = $('