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>
296 lines
8 KiB
JavaScript
296 lines
8 KiB
JavaScript
var modalDiv = null,
|
|
tooltipDiv = null,
|
|
tooltipTimeout = null;
|
|
|
|
return L.Class.extend({
|
|
__init__: function() {
|
|
modalDiv = document.body.appendChild(
|
|
L.dom.create('div', { id: 'modal_overlay' },
|
|
L.dom.create('div', { class: 'modal', role: 'dialog', 'aria-modal': true })));
|
|
|
|
tooltipDiv = document.body.appendChild(
|
|
L.dom.create('div', { class: 'cbi-tooltip' }));
|
|
|
|
/* setup old aliases */
|
|
L.showModal = this.showModal;
|
|
L.hideModal = this.hideModal;
|
|
L.showTooltip = this.showTooltip;
|
|
L.hideTooltip = this.hideTooltip;
|
|
L.itemlist = this.itemlist;
|
|
|
|
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('luci-loaded', this.tabs.init.bind(this.tabs));
|
|
},
|
|
|
|
/* Modal dialog */
|
|
showModal: function(title, children) {
|
|
var dlg = modalDiv.firstElementChild;
|
|
|
|
dlg.setAttribute('class', 'modal');
|
|
|
|
L.dom.content(dlg, L.dom.create('h4', {}, title));
|
|
L.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(L.dom.elem(sep) ? sep.cloneNode(true) : sep);
|
|
}
|
|
}
|
|
|
|
L.dom.content(node, children);
|
|
|
|
return node;
|
|
},
|
|
|
|
/* Tabs */
|
|
tabs: L.Class.singleton({
|
|
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 (typeof(panes) != 'object' || !('length' in 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.ui.tabs.setActiveTabId(groupId, index);
|
|
}
|
|
else {
|
|
pane.setAttribute('data-tab-active', 'false');
|
|
}
|
|
|
|
index++;
|
|
}
|
|
});
|
|
}
|
|
})
|
|
});
|