summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJo-Philipp Wich <jo@mein.io>2019-01-07 14:48:19 +0100
committerJo-Philipp Wich <jo@mein.io>2019-07-07 15:25:49 +0200
commitec6d4094b988818faf6d5d06f6b26d3e1bcbcd6f (patch)
treec0aa947cddafe54c31a85bc0b2eaae9bda3f51a0
parent992638947d0489ea886d2b603855dff1e4310141 (diff)
luci-base: luci.js: add dynamic class loader
Introduce L.require() to fetch additional JavaScript classes. Signed-off-by: Jo-Philipp Wich <jo@mein.io>
-rw-r--r--modules/luci-base/htdocs/luci-static/resources/luci.js90
1 files changed, 89 insertions, 1 deletions
diff --git a/modules/luci-base/htdocs/luci-static/resources/luci.js b/modules/luci-base/htdocs/luci-static/resources/luci.js
index 7cddc4e10..610cbcb62 100644
--- a/modules/luci-base/htdocs/luci-static/resources/luci.js
+++ b/modules/luci-base/htdocs/luci-static/resources/luci.js
@@ -428,7 +428,8 @@
tooltipTimeout = null,
dummyElem = null,
domParser = null,
- originalCBIInit = null;
+ originalCBIInit = null,
+ classes = {};
LuCI = Class.extend({
__name__: 'LuCI',
@@ -464,6 +465,93 @@
window.cbi_init = function() {};
},
+ /* Class require */
+ require: function(name, from) {
+ var L = this, url = null, from = from || [];
+
+ /* Class already loaded */
+ if (classes[name] != null) {
+ /* Circular dependency */
+ if (from.indexOf(name) != -1)
+ throw new Error('Circular dependency: class "%s" depends on "%s"'
+ .format(name, from.join('" which depends on "')));
+
+ return classes[name];
+ }
+
+ document.querySelectorAll('script[src$="/luci.js"]').forEach(function(s) {
+ url = '%s/%s.js'.format(
+ s.getAttribute('src').replace(/\/luci\.js$/, ''),
+ name.replace(/\./g, '/'));
+ });
+
+ if (url == null)
+ throw new Error('Cannot find url of luci.js');
+
+ from = [ name ].concat(from);
+
+ var compileClass = function(res) {
+ if (!res.ok)
+ throw new Error('HTTP error %d while loading class file "%s"'
+ .format(res.status, url));
+
+ var source = res.text(),
+ reqmatch = /(?:^|\n)[ \t]*(?:["']require[ \t]+(\S+)(?:[ \t]+as[ \t]+([a-zA-Z_]\S*))?["']);/g,
+ depends = [],
+ args = '';
+
+ /* find require statements in source */
+ for (var m = reqmatch.exec(source); m; m = reqmatch.exec(source)) {
+ var dep = m[1], as = m[2] || dep.replace(/[^a-zA-Z0-9_]/g, '_');
+ depends.push(L.require(dep, from));
+ args += ', ' + as;
+ }
+
+ /* load dependencies and instantiate class */
+ return Promise.all(depends).then(function(instances) {
+ try {
+ _factory = eval(
+ '(function(window, document, L%s) { %s })\n\n//# sourceURL=%s\n'
+ .format(args, source, res.url));
+ }
+ catch (error) {
+ throw new SyntaxError('%s\n in %s:%s'
+ .format(error.message, res.url, error.lineNumber || '?'));
+ }
+
+ _factory.displayName = toCamelCase(name + 'ClassFactory');
+ _class = _factory.apply(_factory, [window, document, L].concat(instances));
+
+ if (!Class.isSubclass(_class))
+ throw new TypeError('"%s" factory yields invalid constructor'
+ .format(name));
+
+ if (_class.displayName == 'AnonymousClass')
+ _class.displayName = toCamelCase(name + 'Class');
+
+ var ptr = Object.getPrototypeOf(L),
+ parts = name.split(/\./),
+ instance = new _class();
+
+ for (var i = 0; ptr && i < parts.length - 1; i++)
+ ptr = ptr[parts[i]];
+
+ if (!ptr)
+ throw new Error('Parent "%s" for class "%s" is missing'
+ .format(parts.slice(0, i).join('.'), name));
+
+ classes[name] = ptr[parts[i]] = instance;
+
+ return instance;
+ });
+ };
+
+ /* Request class file */
+ classes[name] = Request.get(url, { cache: true }).then(compileClass);
+
+ return classes[name];
+ },
+
/* DOM setup */
setupDOM: function(ev) {
this.tabs.init();