luci-base: introduce common JavaScript api
Introduce a new script file luci.js which is included by default and intended to be the common location of functions currently scattered in cbi.js and xhr.js. The luci.js file provides a LuCI() class which - among other things - implements helpers to construct URL paths and making HTTP requests. A singleton instance of the class is instantiated as window.L upon load and preset with the necessary environment information. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
This commit is contained in:
parent
17690f2d73
commit
706c6836e4
2 changed files with 176 additions and 0 deletions
165
modules/luci-base/htdocs/luci-static/resources/luci.js
Normal file
165
modules/luci-base/htdocs/luci-static/resources/luci.js
Normal file
|
@ -0,0 +1,165 @@
|
|||
(function(window, document) {
|
||||
var modalDiv = null,
|
||||
tooltipDiv = null,
|
||||
tooltipTimeout = null;
|
||||
|
||||
LuCI.prototype = {
|
||||
/* URL construction helpers */
|
||||
path: function(prefix, parts) {
|
||||
var url = [ prefix || '' ];
|
||||
|
||||
for (var i = 0; i < parts.length; i++)
|
||||
if (/^(?:[a-zA-Z0-9_.%,;-]+\/)*[a-zA-Z0-9_.%,;-]+$/.test(parts[i]))
|
||||
url.push('/', parts[i]);
|
||||
|
||||
if (url.length === 1)
|
||||
url.push('/');
|
||||
|
||||
return url.join('');
|
||||
},
|
||||
|
||||
url: function() {
|
||||
return this.path(this.env.scriptname, arguments);
|
||||
},
|
||||
|
||||
resource: function() {
|
||||
return this.path(this.env.resource, arguments);
|
||||
},
|
||||
|
||||
location: function() {
|
||||
return this.path(this.env.scriptname, this.env.requestpath);
|
||||
},
|
||||
|
||||
|
||||
/* HTTP resource fetching */
|
||||
get: function(url, args, cb) {
|
||||
return this.poll(0, url, args, cb, false);
|
||||
},
|
||||
|
||||
post: function(url, args, cb) {
|
||||
return this.poll(0, url, args, cb, true);
|
||||
},
|
||||
|
||||
poll: function(interval, url, args, cb, post) {
|
||||
var data = post ? { token: this.env.token } : null;
|
||||
|
||||
if (!/^(?:\/|\S+:\/\/)/.test(url))
|
||||
url = this.url(url);
|
||||
|
||||
if (typeof(args) === 'object' && args !== null) {
|
||||
data = data || {};
|
||||
|
||||
for (var key in args)
|
||||
if (args.hasOwnProperty(key))
|
||||
switch (typeof(args[key])) {
|
||||
case 'string':
|
||||
case 'number':
|
||||
case 'boolean':
|
||||
data[key] = args[key];
|
||||
break;
|
||||
|
||||
case 'object':
|
||||
data[key] = JSON.stringify(args[key]);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (interval > 0)
|
||||
return XHR.poll(interval, url, data, cb, post);
|
||||
else if (post)
|
||||
return XHR.post(url, data, cb);
|
||||
else
|
||||
return XHR.get(url, data, cb);
|
||||
},
|
||||
|
||||
|
||||
/* Modal dialog */
|
||||
showModal: function(title, children) {
|
||||
var dlg = modalDiv.firstElementChild;
|
||||
|
||||
while (dlg.firstChild)
|
||||
dlg.removeChild(dlg.firstChild);
|
||||
|
||||
dlg.setAttribute('class', 'modal');
|
||||
dlg.appendChild(E('h4', {}, title));
|
||||
|
||||
if (!Array.isArray(children))
|
||||
children = [ children ];
|
||||
|
||||
for (var i = 0; i < children.length; i++)
|
||||
if (isElem(children[i]))
|
||||
dlg.appendChild(children[i]);
|
||||
else
|
||||
dlg.appendChild(document.createTextNode('' + children[i]));
|
||||
|
||||
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;
|
||||
},
|
||||
|
||||
hideTooltip: function(ev) {
|
||||
if (ev.target === tooltipDiv || ev.relatedTarget === tooltipDiv)
|
||||
return;
|
||||
|
||||
if (tooltipTimeout !== null) {
|
||||
window.clearTimeout(tooltipTimeout);
|
||||
tooltipTimeout = null;
|
||||
}
|
||||
|
||||
tooltipDiv.style.opacity = 0;
|
||||
tooltipTimeout = window.setTimeout(function() { tooltipDiv.removeAttribute('style'); }, 250);
|
||||
}
|
||||
};
|
||||
|
||||
function LuCI(env) {
|
||||
this.env = env;
|
||||
|
||||
modalDiv = document.body.appendChild(E('div', { id: 'modal_overlay' }, E('div', { class: 'modal' })));
|
||||
tooltipDiv = document.body.appendChild(E('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);
|
||||
}
|
||||
|
||||
window.LuCI = LuCI;
|
||||
})(window, document);
|
|
@ -10,3 +10,14 @@
|
|||
luci.dispatcher.context.template_header_sent = true
|
||||
end
|
||||
%>
|
||||
|
||||
<script type="text/javascript" src="<%=resource%>/luci.js"></script>
|
||||
<script type="text/javascript">
|
||||
L = new LuCI(<%= luci.http.write_json({
|
||||
token = token,
|
||||
resource = resource,
|
||||
scriptname = luci.http.getenv("SCRIPT_NAME"),
|
||||
pathinfo = luci.http.getenv("PATH_INFO"),
|
||||
requestpath = luci.dispatcher.context.requestpath
|
||||
}) %>);
|
||||
</script>
|
||||
|
|
Loading…
Reference in a new issue