/** * Module provides methods to create a new module. Any module created here has * the ability of being extended by other modules. Existing properties are * overridden. * * Creating a module * @example * var NewModule = Module.create(function() { * this.value = 100; * }); * NewModule.prototype.print = function() { console.log(this.value); }; * * var instance = NewModule(); * instance.print(); // 100 * * Extending a module * @example * var AnotherModule = Module.create(); * AnotherModule.prototype.inc = function() { this.value++; }; * var ExtendingModule = NewModule.extend(AnotherModule); * * var instance = ExtendingModule(); * instance.inc(); * instance.print(); // 101 * * @module modules/base/module */ define(function() { 'use strict'; const LOG_LEVEL = { NONE: 0, DEBUG: 1, INFO: 2, WARN: 3, ERROR: 4, ALL: 5 }; var _constructorMap = (function() { var _ctorMap = new Map(); var _getConstructor = function(Module) { return _ctorMap.get(Module) || function() {}; }; var _registerConstructor = function(Module, constructor) { _ctorMap.set(Module, constructor || function() {}); }; return { getConstructor: _getConstructor, registerConstructor: _registerConstructor }; })(); var _emptyFunction = function() {}; var _createLogger = function(name) { switch (name) { case 'DEBUG': return function(msg) { console.log(this._msgPrefix + msg); }; case 'INFO': return function(msg) { console.info(this._msgPrefix + msg); }; case 'WARN': return function(msg) { console.warn(this._msgPrefix + msg); }; case 'ERROR': return function(msg) { console.error(this._msgPrefix + msg); }; } }; var ModulePrototype = { get _msgPrefix() { return '[' + this.$name + ']: '; }, debug: _emptyFunction, info: _emptyFunction, warn: _emptyFunction, error: _emptyFunction, throw: function(msg) { throw new Error(this._msgPrefix + msg); }, set _logLevel(value) { Object.keys(LOG_LEVEL).forEach((name) => { var level = LOG_LEVEL[name]; if (value >= level && value > 0) { this[name.toLowerCase()] = _createLogger(name); } }); }, super: _constructorMap.getConstructor }; var _extend = function() { switch (arguments.length) { case 0: break; case 1: var Module = arguments[0]; for (var prop in Module.prototype) { if (prop === '$name') { continue; } var pd = Object.getOwnPropertyDescriptor(Module.prototype, prop); if (pd) { Object.defineProperty(this.prototype, prop, pd); } } break; default: Array.prototype.forEach.call(arguments, (Module) => { _extend.call(this, Module); }); break; } return this; }; // Returns a module function. The function returns an instance of the // module when it is called. The passed constructor is called using the // instance as "this". function _create(constructor) { if (constructor && typeof constructor !== 'function') { throw new Error('[Module]: Invalid constructor'); } var ModuleFunc = function() {}; ModuleFunc.prototype = Object.create(ModulePrototype); ModuleFunc.prototype.$name = constructor && constructor.name || ''; var Module = function() { var instance = new ModuleFunc(); if (constructor) { constructor.apply(instance, arguments); } return instance; }; _constructorMap.registerConstructor(Module, constructor); Module.extend = _extend; Module.prototype = ModuleFunc.prototype; return Module; } return { LOG_LEVEL: LOG_LEVEL, create: _create }; });