diff options
author | Jo-Philipp Wich <jo@mein.io> | 2019-01-06 16:40:59 +0100 |
---|---|---|
committer | Jo-Philipp Wich <jo@mein.io> | 2019-07-07 15:25:49 +0200 |
commit | fee8c1fc9c9b1334ada08e94d4b75c9d418c098b (patch) | |
tree | b2578d11b4754d26e46c07a96968f64417cb2ce2 /modules | |
parent | 9be6bb05771dbd5f901e182eb645614a8135d898 (diff) |
luci-base: luci.js: add class construction helper
Signed-off-by: Jo-Philipp Wich <jo@mein.io>
Diffstat (limited to 'modules')
-rw-r--r-- | modules/luci-base/htdocs/luci-static/resources/luci.js | 114 |
1 files changed, 113 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 a481f886c..8d7f88d77 100644 --- a/modules/luci-base/htdocs/luci-static/resources/luci.js +++ b/modules/luci-base/htdocs/luci-static/resources/luci.js @@ -21,6 +21,116 @@ }); } + /* + * Class declaration and inheritance helper + */ + + var toCamelCase = function(s) { + return s.replace(/(?:^|[\. -])(.)/g, function(m0, m1) { return m1.toUpperCase() }); + }; + + var superContext = null, Class = Object.assign(function() {}, { + extend: function(properties) { + var props = { + __base__: { value: this.prototype }, + __name__: { value: properties.__name__ || 'anonymous' } + }; + + var ClassConstructor = function() { + if (!(this instanceof ClassConstructor)) + throw new TypeError('Constructor must not be called without "new"'); + + if (Object.getPrototypeOf(this).hasOwnProperty('__init__')) { + if (typeof(this.__init__) != 'function') + throw new TypeError('Class __init__ member is not a function'); + + this.__init__.apply(this, arguments) + } + else { + this.super('__init__', arguments); + } + }; + + for (var key in properties) + if (!props[key] && properties.hasOwnProperty(key)) + props[key] = { value: properties[key], writable: true }; + + ClassConstructor.prototype = Object.create(this.prototype, props); + ClassConstructor.prototype.constructor = ClassConstructor; + Object.assign(ClassConstructor, this); + ClassConstructor.displayName = toCamelCase(props.__name__.value + 'Class'); + + return ClassConstructor; + }, + + singleton: function(properties /*, ... */) { + return Class.extend(properties) + .instantiate(Class.prototype.varargs(arguments, 1)); + }, + + instantiate: function(args) { + return new (Function.prototype.bind.apply(this, + Class.prototype.varargs(args, 0, null)))(); + }, + + call: function(self, method) { + if (typeof(this.prototype[method]) != 'function') + throw new ReferenceError(method + ' is not defined in class'); + + return this.prototype[method].apply(self, self.varargs(arguments, 1)); + }, + + isSubclass: function(_class) { + return (_class != null && + typeof(_class) == 'function' && + _class.prototype instanceof this); + }, + + prototype: { + varargs: function(args, offset /*, ... */) { + return Array.prototype.slice.call(arguments, 2) + .concat(Array.prototype.slice.call(args, offset)); + }, + + super: function(key, callArgs) { + for (superContext = Object.getPrototypeOf(superContext || + Object.getPrototypeOf(this)); + superContext && !superContext.hasOwnProperty(key); + superContext = Object.getPrototypeOf(superContext)) { } + + if (!superContext) + return null; + + var res = superContext[key]; + + if (arguments.length > 1) { + if (typeof(res) != 'function') + throw new ReferenceError(key + ' is not a function in base class'); + + if (typeof(callArgs) != 'object') + callArgs = this.varargs(arguments, 1); + + res = res.apply(this, callArgs); + } + + superContext = null; + + return res; + }, + + toString: function() { + var s = '[' + this.constructor.displayName + ']', f = true; + for (var k in this) { + if (this.hasOwnProperty(k)) { + s += (f ? ' {\n' : '') + ' ' + k + ': ' + typeof(this[k]) + '\n'; + f = false; + } + } + return s + (f ? '' : '}'); + } + } + }); + var modalDiv = null, tooltipDiv = null, tooltipTimeout = null, @@ -200,7 +310,9 @@ this.dom.content(node, children); return node; - } + }, + + Class: Class }; /* Tabs */ |