summaryrefslogtreecommitdiffhomepage
path: root/modules/luci-base/htdocs/luci-static/resources/luci.js
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2019-01-07 15:26:08 +0100
committerJo-Philipp Wich <jo@mein.io>2019-07-07 15:25:49 +0200
commit344c4c511936062ec4521d42d1c1ba51577daee0 (patch)
tree9b7d2504765e4864f0313875e87b679e24c29a1c /modules/luci-base/htdocs/luci-static/resources/luci.js
parentec6d4094b988818faf6d5d06f6b26d3e1bcbcd6f (diff)
luci-base: luci.js: split ui helper functions into external ui.js
Use the new class loader infrastructure to move gui specific functionality out of the luci.js core and dispatch a new event 'luci-loaded' which is fired once all external classes have been fetched. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'modules/luci-base/htdocs/luci-static/resources/luci.js')
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/luci.js353
1 files changed, 43 insertions, 310 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/luci.js b/modules/luci-base/htdocs/luci-static/resources/luci.js
index 610cbcb62a..b86d499c6d 100644
--- a/modules/luci-base/htdocs/luci-static/resources/luci.js
+++ b/modules/luci-base/htdocs/luci-static/resources/luci.js
@@ -423,10 +423,7 @@
});
- var modalDiv = null,
- tooltipDiv = null,
- tooltipTimeout = null,
- dummyElem = null,
+ var dummyElem = null,
domParser = null,
originalCBIInit = null,
classes = {};
@@ -436,17 +433,6 @@
__init__: function(env) {
Object.assign(this.env, env);
- modalDiv = document.body.appendChild(
- this.dom.create('div', { id: 'modal_overlay' },
- this.dom.create('div', { class: 'modal', role: 'dialog', 'aria-modal': true })));
-
- tooltipDiv = document.body.appendChild(this.dom.create('div', { class: 'cbi-tooltip' }));
-
- document.addEventListener('mouseover', this.showTooltip.bind(this), true);
- document.addEventListener('mouseout', this.hideTooltip.bind(this), true);
- document.addEventListener('focus', this.showTooltip.bind(this), true);
- document.addEventListener('blur', this.hideTooltip.bind(this), true);
-
document.addEventListener('DOMContentLoaded', this.setupDOM.bind(this));
document.addEventListener('poll-start', function(ev) {
@@ -554,32 +540,50 @@
/* DOM setup */
setupDOM: function(ev) {
- this.tabs.init();
-
- Request.addInterceptor(function(res) {
- if (res.status != 403 || res.headers.get('X-LuCI-Login-Required') != 'yes')
- return;
-
- Request.poll.stop();
-
- L.showModal(_('Session expired'), [
- E('div', { class: 'alert-message warning' },
- _('A new login is required since the authentication session expired.')),
- E('div', { class: 'right' },
- E('div', {
- class: 'btn primary',
- click: function() {
- var loc = window.location;
- window.location = loc.protocol + '//' + loc.host + loc.pathname + loc.search;
- }
- }, _('To login…')))
- ]);
+ Promise.all([
+ L.require('ui')
+ ]).then(function() {
+ Request.addInterceptor(function(res) {
+ if (res.status != 403 || res.headers.get('X-LuCI-Login-Required') != 'yes')
+ return;
- return Promise.reject(new Error('Session expired'));
- });
+ Request.poll.stop();
+
+ L.ui.showModal(_('Session expired'), [
+ E('div', { class: 'alert-message warning' },
+ _('A new login is required since the authentication session expired.')),
+ E('div', { class: 'right' },
+ E('div', {
+ class: 'btn primary',
+ click: function() {
+ var loc = window.location;
+ window.location = loc.protocol + '//' + loc.host + loc.pathname + loc.search;
+ }
+ }, _('To login…')))
+ ]);
+
+ return Promise.reject(new Error('Session expired'));
+ });
+
+ originalCBIInit();
+ Request.poll.start();
- originalCBIInit();
- Request.poll.start();
+ document.dispatchEvent(new CustomEvent('luci-loaded'));
+ }).catch(function(error) {
+ var msg = (error.stack || '').replace(/(.+?)@(.+):(\d+):(\d+)/g,
+ ' at $1 ($2:$3:$4)');
+
+ if (msg.indexOf(error.message) == -1)
+ msg = error.message + '\n' + msg;
+
+ if (error.name && msg.indexOf(error.name) != 0)
+ msg = error.name + ': ' + msg;
+
+ alert('LuCI class loading error:\n' + msg);
+
+ if (window.console && console.debug)
+ console.debug(error);
+ });
},
env: {},
@@ -649,281 +653,10 @@
halt: function() { return Request.poll.stop() },
run: function() { return Request.poll.start() },
-
- /* Modal dialog */
- showModal: function(title, children) {
- var dlg = modalDiv.firstElementChild;
-
- dlg.setAttribute('class', 'modal');
-
- this.dom.content(dlg, this.dom.create('h4', {}, title));
- this.dom.append(dlg, children);
-
- document.body.classList.add('modal-overlay-active');
-
- return dlg;
- },
-
- hideModal: function() {
- document.body.classList.remove('modal-overlay-active');
- },
-
-
- /* Tooltip */
- showTooltip: function(ev) {
- var target = findParent(ev.target, '[data-tooltip]');
-
- if (!target)
- return;
-
- if (tooltipTimeout !== null) {
- window.clearTimeout(tooltipTimeout);
- tooltipTimeout = null;
- }
-
- var rect = target.getBoundingClientRect(),
- x = rect.left + window.pageXOffset,
- y = rect.top + rect.height + window.pageYOffset;
-
- tooltipDiv.className = 'cbi-tooltip';
- tooltipDiv.innerHTML = '▲ ';
- tooltipDiv.firstChild.data += target.getAttribute('data-tooltip');
-
- if (target.hasAttribute('data-tooltip-style'))
- tooltipDiv.classList.add(target.getAttribute('data-tooltip-style'));
-
- if ((y + tooltipDiv.offsetHeight) > (window.innerHeight + window.pageYOffset)) {
- y -= (tooltipDiv.offsetHeight + target.offsetHeight);
- tooltipDiv.firstChild.data = '▼ ' + tooltipDiv.firstChild.data.substr(2);
- }
-
- tooltipDiv.style.top = y + 'px';
- tooltipDiv.style.left = x + 'px';
- tooltipDiv.style.opacity = 1;
-
- tooltipDiv.dispatchEvent(new CustomEvent('tooltip-open', {
- bubbles: true,
- detail: { target: target }
- }));
- },
-
- hideTooltip: function(ev) {
- if (ev.target === tooltipDiv || ev.relatedTarget === tooltipDiv ||
- tooltipDiv.contains(ev.target) || tooltipDiv.contains(ev.relatedTarget))
- return;
-
- if (tooltipTimeout !== null) {
- window.clearTimeout(tooltipTimeout);
- tooltipTimeout = null;
- }
-
- tooltipDiv.style.opacity = 0;
- tooltipTimeout = window.setTimeout(function() { tooltipDiv.removeAttribute('style'); }, 250);
-
- tooltipDiv.dispatchEvent(new CustomEvent('tooltip-close', { bubbles: true }));
- },
-
-
- /* Widget helper */
- itemlist: function(node, items, separators) {
- var children = [];
-
- if (!Array.isArray(separators))
- separators = [ separators || E('br') ];
-
- for (var i = 0; i < items.length; i += 2) {
- if (items[i+1] !== null && items[i+1] !== undefined) {
- var sep = separators[(i/2) % separators.length],
- cld = [];
-
- children.push(E('span', { class: 'nowrap' }, [
- items[i] ? E('strong', items[i] + ': ') : '',
- items[i+1]
- ]));
-
- if ((i+2) < items.length)
- children.push(this.dom.elem(sep) ? sep.cloneNode(true) : sep);
- }
- }
-
- this.dom.content(node, children);
-
- return node;
- },
-
Class: Class,
Request: Request
});
- /* Tabs */
- LuCI.prototype.tabs = {
- init: function() {
- var groups = [], prevGroup = null, currGroup = null;
-
- document.querySelectorAll('[data-tab]').forEach(function(tab) {
- var parent = tab.parentNode;
-
- if (!parent.hasAttribute('data-tab-group'))
- parent.setAttribute('data-tab-group', groups.length);
-
- currGroup = +parent.getAttribute('data-tab-group');
-
- if (currGroup !== prevGroup) {
- prevGroup = currGroup;
-
- if (!groups[currGroup])
- groups[currGroup] = [];
- }
-
- groups[currGroup].push(tab);
- });
-
- for (var i = 0; i < groups.length; i++)
- this.initTabGroup(groups[i]);
-
- document.addEventListener('dependency-update', this.updateTabs.bind(this));
-
- this.updateTabs();
-
- if (!groups.length)
- this.setActiveTabId(-1, -1);
- },
-
- initTabGroup: function(panes) {
- if (!Array.isArray(panes) || panes.length === 0)
- return;
-
- var menu = E('ul', { 'class': 'cbi-tabmenu' }),
- group = panes[0].parentNode,
- groupId = +group.getAttribute('data-tab-group'),
- selected = null;
-
- for (var i = 0, pane; pane = panes[i]; i++) {
- var name = pane.getAttribute('data-tab'),
- title = pane.getAttribute('data-tab-title'),
- active = pane.getAttribute('data-tab-active') === 'true';
-
- menu.appendChild(E('li', {
- 'class': active ? 'cbi-tab' : 'cbi-tab-disabled',
- 'data-tab': name
- }, E('a', {
- 'href': '#',
- 'click': this.switchTab.bind(this)
- }, title)));
-
- if (active)
- selected = i;
- }
-
- group.parentNode.insertBefore(menu, group);
-
- if (selected === null) {
- selected = this.getActiveTabId(groupId);
-
- if (selected < 0 || selected >= panes.length)
- selected = 0;
-
- menu.childNodes[selected].classList.add('cbi-tab');
- menu.childNodes[selected].classList.remove('cbi-tab-disabled');
- panes[selected].setAttribute('data-tab-active', 'true');
-
- this.setActiveTabId(groupId, selected);
- }
- },
-
- getActiveTabState: function() {
- var page = document.body.getAttribute('data-page');
-
- try {
- var val = JSON.parse(window.sessionStorage.getItem('tab'));
- if (val.page === page && Array.isArray(val.groups))
- return val;
- }
- catch(e) {}
-
- window.sessionStorage.removeItem('tab');
- return { page: page, groups: [] };
- },
-
- getActiveTabId: function(groupId) {
- return +this.getActiveTabState().groups[groupId] || 0;
- },
-
- setActiveTabId: function(groupId, tabIndex) {
- try {
- var state = this.getActiveTabState();
- state.groups[groupId] = tabIndex;
-
- window.sessionStorage.setItem('tab', JSON.stringify(state));
- }
- catch (e) { return false; }
-
- return true;
- },
-
- updateTabs: function(ev) {
- document.querySelectorAll('[data-tab-title]').forEach(function(pane) {
- var menu = pane.parentNode.previousElementSibling,
- tab = menu.querySelector('[data-tab="%s"]'.format(pane.getAttribute('data-tab'))),
- n_errors = pane.querySelectorAll('.cbi-input-invalid').length;
-
- if (!pane.firstElementChild) {
- tab.style.display = 'none';
- tab.classList.remove('flash');
- }
- else if (tab.style.display === 'none') {
- tab.style.display = '';
- requestAnimationFrame(function() { tab.classList.add('flash') });
- }
-
- if (n_errors) {
- tab.setAttribute('data-errors', n_errors);
- tab.setAttribute('data-tooltip', _('%d invalid field(s)').format(n_errors));
- tab.setAttribute('data-tooltip-style', 'error');
- }
- else {
- tab.removeAttribute('data-errors');
- tab.removeAttribute('data-tooltip');
- }
- });
- },
-
- switchTab: function(ev) {
- var tab = ev.target.parentNode,
- name = tab.getAttribute('data-tab'),
- menu = tab.parentNode,
- group = menu.nextElementSibling,
- groupId = +group.getAttribute('data-tab-group'),
- index = 0;
-
- ev.preventDefault();
-
- if (!tab.classList.contains('cbi-tab-disabled'))
- return;
-
- menu.querySelectorAll('[data-tab]').forEach(function(tab) {
- tab.classList.remove('cbi-tab');
- tab.classList.remove('cbi-tab-disabled');
- tab.classList.add(
- tab.getAttribute('data-tab') === name ? 'cbi-tab' : 'cbi-tab-disabled');
- });
-
- group.childNodes.forEach(function(pane) {
- if (L.dom.matches(pane, '[data-tab]')) {
- if (pane.getAttribute('data-tab') === name) {
- pane.setAttribute('data-tab-active', 'true');
- L.tabs.setActiveTabId(groupId, index);
- }
- else {
- pane.setAttribute('data-tab-active', 'false');
- }
-
- index++;
- }
- });
- }
- };
-
/* DOM manipulation */
LuCI.prototype.dom = {
elem: function(e) {