UNPKG

mold-js

Version:

MoldJS structure and pattern framework commandline interface

1,919 lines (1,642 loc) 87.9 kB
"use strict"; //irgendasein test (function(global){ /** ERROR TYPES */ var SeedError = function SeedError (message, seedName) { if(__Mold && __Mold.getInstanceDescription){ message += " [" + seedName + "] " + __Mold.getInstanceDescription(); } this.name = 'SeedError'; this.message = message; this.seedName = seedName; this.stack = message + "\n" + (new Error()).stack; } SeedError.prototype = Object.create(Error.prototype); SeedError.prototype.constructor = SeedError; var CommandError = function CommandError (message, command) { if(__Mold && __Mold.getInstanceDescription){ message += " "+ __Mold.getInstanceDescription(); } this.name = 'CommandError'; this.message = message; this.stack = message + "\n" + (new Error()).stack; this.command = command; } CommandError.prototype = Object.create(Error.prototype); CommandError.prototype.constructor = CommandError; var PolicyError = function PolicyError (message) { if(__Mold && __Mold.getInstanceDescription){ message += " "+ __Mold.getInstanceDescription(); } this.name = 'PolicyError'; this.message = message; this.stack = message + "\n" + (new Error()).stack; } PolicyError.prototype = Object.create(Error.prototype); PolicyError.prototype.constructor = PolicyError; var DNAError = function DNAError (message) { this.name = 'DNAError'; this.message = message; this.stack = (new Error()).stack; } DNAError.prototype = new Error; var SeedTypeError = function SeedTypeError (message) { this.name = 'SeedTypeError'; this.message = message; this.stack = (new Error()).stack; } SeedTypeError.prototype = new Error; var _isNodeJS = (global.global) ? true : false; if(!_isNodeJS){ var _currentScript = document.currentScript || (function() { var scripts = document.getElementsByTagName('script'); return scripts[scripts.length - 1]; })(); } //load required node modules if(_isNodeJS){ var fs = require('fs'); var vm = require('vm'); var request = require('request'); var url = require('url'); } /** MOLD CONTRUCTOR */ var Mold = function Mold(){ //create storages this.seeds = []; this.seedIndex = {}; this.seedTypeIndex = {}; this.Errors = { SeedError : SeedError, DNAError : DNAError, SeedTypeError : SeedTypeError, CommandError : CommandError, PolicyError : PolicyError } this.EXIT = '---exit---'; this.isNodeJS = _isNodeJS; this.ready = this._reCreateReadyPromise(); if(_isNodeJS){ this.globalProperties = { require : require, __dirname : __dirname, setTimeout : setTimeout, clearTimeout : clearTimeout, process : process, Buffer : Buffer } } this.init(); } var __Mold = Mold.prototype; Mold.prototype = { /** * @methode getId * @desc returns a uinque ID * @return (Object) - returns the uinque ID **/ ident : 0, getId : function (){ this.ident++; return this.ident; }, copyGlobalProperties : function(global){ for(var prop in this.globalProperties){ global[prop] = this.globalProperties[prop]; } }, getInstanceDescription : function(){ return " [instance:" + ((global && global.vmInstance) ? 'vm' : 'origin') + ((global && global.vmInstance) ? ":" + global.vmInstance : '') + "]"; }, /** SEED HANDLING */ load : function(name){ var seed; if((seed = this.Core.SeedManager.get(name))){ return seed.isReady; } seed = this.Core.SeedFactory({ name : name, state : this.Core.SeedStates.NEW }); this.Core.SeedFlow.exec(seed); return seed.isReady; }, _reCreateReadyPromise : function (){ var id = this.getId(); this.ready = new this.Core.Promise(false, { throwError : true, name : "Mold.ready"}).all([ this.Core.Config.isReady, this.Core.SeedManager.isReady ]) }, /** OBJECT HANDLING **/ extend : function(superClass, subClass){ subClass.prototype = Object.create(superClass); subClass.prototype.constructor = subClass; return subClass; }, /** * @method mixin * @desc Adds methods adn properties from one object to another * @param {object} target - Expects the target object * @param {object} origin - Expects the origin object * @param {[array]} selected - Expects an array with the property- and methodnames that will be copied, the parameter is optional, if it is not given, all methodes an parametes will be copied * @return {object} - returns the target object with the new methodes an properties **/ mixin : function(target, origin, selected, config){ for(var property in origin){ if(selected && selected.length > 0){ if(selected.indexOf(property) > -1){ target[property] = origin[property]; } }else{ if(config && config.protected){ if(!target[property]){ target[property] = origin[property]; } }else{ target[property] = origin[property]; } } } return target; }, merge : function(target, origin, conf){ if(Array.isArray(origin)){ if(conf && conf.concatArrays){ if(Array.isArray(target)){ target = target.concat(origin); }else{ target = origin; } }else{ for(var i = 0; i < origin.length; i++){ if(target[i]){ if(this.isObject(target[i]) || Array.isArray(target[i])){ target[i] = this.merge(target[i], origin[i], conf); }else{ target[i] = origin[i]; } }else{ target[i] = origin[i]; } } } }else if(this.isObject(origin)){ for(var prop in origin){ if(conf && conf.without && !!~conf.without.indexOf(prop)){ continue; } if(target[prop]){ if(conf && conf.merger && conf.merger[prop]){ target[prop] = conf.merger[prop](target[prop], origin[prop], conf); }else{ if(this.isObject(target[prop]) || Array.isArray(target[prop])){ target[prop] = this.merge(target[prop], origin[prop], conf); }else{ target[prop] = origin[prop]; } } }else{ target[prop] = origin[prop]; } } } return target; }, /** * @method diff * @description compares two objects and returns a new one that only has the datat from target which not apears in source * @param {object} target - the target object * @param {object} source - the source object * @return {object} returns a new diff object */ diff : function(target, source){ var output = null; if(Array.isArray(target)){ for(var i = 0; i < target.length; i++){ var skip = false; for(var x = 0; x < source.length; x++){ if(typeof source[x] === "object"){ var result = this.diff(target[i], source[x]); if(!result){ skip = true; break; } }else{ if(target[i] === source[x]){ skip = true; break; } } } if(!skip){ output = output || []; output.push(target[i]); } } }else if(typeof target === "object"){ for(var targetProp in target){ if(!source[targetProp]){ output = output || {}; output[targetProp] = target[targetProp] }else if(typeof source[targetProp] === "object"){ var result = this.diff(target[targetProp], source[targetProp]); if(result){ output = output || {}; output[targetProp] = result; } } } } return output; }, /** * @method clone * @description clones an given object * @param {object} target - the object that should be cloned * @return {object} returns the cloned object */ clone : function(target) { if(!target || typeof(target) != 'object'){ return target; } var newObj = target.constructor(); for(var prop in target){ newObj[prop] = this.clone(target[prop]); } return newObj; }, /** * @methode wrap * @desc Wraps a Class with a second constructor, so you can execute methods in the scope of the targetclass * @param (function) targetClass - Expects the class will be wraped * @param (function) wrappingMethode - Expects the method that will be executed, as parameter the scope of the instance will transfered * @return (function) wrapperClass - Returns a new Class that wrapped the target class **/ wrap : function(targetClass, wrappingMethode){ var wrapperClass = function() { var constructor = targetClass.apply(this, arguments); wrappingMethode(this); return constructor; } wrapperClass.prototype = targetClass.prototype; return wrapperClass; }, /** * @method watch * @description watches a property of an object or html element * @param {object} obj - the ojbect to watch * @param {string} property - the property to watch * @param {function} callback - the callback wich will be called if the property has changed * @param {boolean} [handleAsObject] - if it is set to true the object will handelt like a object even if it is a html element */ watch : function(obj, property, callback, handleAsObject){ if(Object.prototype.watch && !__Mold.isNode(obj)){ obj.watch(property, callback); }else{ var oldval = obj[property]; var newval = oldval; /*use mutation observer for HTML elements*/ if(__Mold.isNode(obj) && !handleAsObject && obj !== window){ if(!!window.MutationObserver){ var observer = new MutationObserver(function(mutations) { __Mold.each(mutations, function(mutation) { if( mutation.target === obj && mutation.type === 'attributes' && mutation.attributeName === property ){ callback.call(obj, property, mutation.oldValue, obj.getAttribute(property)); } }); }); observer.observe(obj, { attributes: true, childList: true, characterData: true , attributeOldValue: true, }); }else{ obj.addEventListener('DOMAttrModified', function(e){ if(e.attrName === property){ callback.call(obj, property, e.prevValue, e.newValue); } }) } }else{ var getter = function () { return newval; } var setter = function (val) { oldval = newval; return newval = callback.call(obj, property, oldval, val); } if (delete obj[property]) { Object.defineProperty(obj, property, { get: getter, set: setter, enumerable: true, configurable: true }); } } } }, /** * @method unwatch * @description unwatches a object property * @param {object} obj - the object to unwatch * @param {string} property - the property to unwatch * @param {function} callback - the callback that has to be removed */ unwatch : function(obj, property, callback){ if(Object.prototype.unwatch) { obj.unwatch(property); }else{ Object.defineProperty(obj, "unwatch", { enumerable: false, configurable: true, writable: false, value: function (prop) { var val = obj[property]; delete obj[property]; obj[property] = val; } }); } }, /** TEST METHODS **/ /** * @namespace Mold * @methode isObject * @desc checks if the give value is an object * @public * @return (Boolean) * @param (Object) collection - the value **/ isObject : function(collection){ if(Object.prototype.toString.call(collection) === "[object Object]"){ return true; } return false; }, isString : function(value){ if(typeof value === "string"){ return true; } return false; }, /** * @namespace Mold * @methode isNodeList * @desc checks if the give value is a NodeListe * @public * @return (Boolean) * @param (Object) collection - the value **/ isNodeList : function(collection){ if( Object.prototype.toString.call(collection) === "[object NodeList]" || Object.prototype.toString.call(collection) === "[object HTMLCollection]" ){ return true; } return false; }, /** * @method isNode * @description checks if an object is in a html element node * @param {object} element - the node to check * @return {boolean} returns true if the element is a element node otherwise it returns false */ isNode : function(element){ if(_isNodeJS) { return false; } if(!element) { return false; } if(element === window){ return true } return( (typeof element === "object") ? element instanceof Node : ( element && typeof element === "object" && typeof element.nodeType === "number" && typeof element.nodeName === "string" ) ) }, } __Mold = Mold.prototype; //Build-in core modules Mold.prototype.Core = {}; /** * @module Mold.Core.Promise * @description implements a Promise A+ * @param {function} */ Mold.prototype.Core.Promise = function Promise(setup, config){ var _state = "pending", _value = false, _callbacks = [], undefined, config = config || {}; var _name = config.name || __Mold.getId(); var PromiseError = function(message){ return { name : "PromiseError", message : message } } var _changeState = function(state, value){ if ( _state === state ) { throw Error("can't transition to same state: " + state + " [" + _name + "]"); } if ( _state === "fulfilled" || _state === "rejeced" ) { throw Error("can't transition from current (" + _state + ") state: " + state + " [" + _name + "]"); } if ( state === "fulfilled" && arguments.length < 2 ) { throw Error("transition to fulfilled must have a non null value." + " [" + _name + "]"); } if ( state === "rejeced" && value === null ) { throw Error("transition to rejected must have a non null reason." + " [" + _name + "]"); } _state = state; _value = value; _resolve(); return _state; }; var _resolve = function(){ if ( _state === "pending" ) { return false; } _callbacks.eachShift(function(callbackObject){ var nextCall = (_state === "fulfilled") ? callbackObject.onFulFilled : callbackObject.onRejected; if(typeof nextCall !== "function" ){ callbackObject.promise.changeState(_state, _value); }else{ try { var value = nextCall.call(null, _value); if (value && typeof value.then === 'function' ){ value.then(function(value){ callbackObject.promise.changeState("fulfilled", value); }, function(error){ callbackObject.promise.changeState("rejected", error); }); }else{ callbackObject.promise.changeState("fulfilled", value); } }catch(error){ callbackObject.promise.changeState("rejected", error); if(config.throwError){ throw error; } } } }); }; var _then = function(onFulFilled, onRejected){ var promise = new Promise(false, config), that = this; _async(function(){ _callbacks.push({ onFulFilled : onFulFilled, onRejected : onRejected, promise : promise }); _resolve(); }); return promise; } var _fulfill = function(value){ _changeState("fulfilled", value ); } var _reject = function(reason){ _changeState( "reject", reason ); } if(setup && typeof setup === "function"){ setup.call(null, _fulfill, _reject); } var _async = function(callback) { setTimeout(callback, 5); }; return { changeState : _changeState, async : _async, /** * @method state * @description returns the current state possible values are reject, fulfilled, reject * @return {string} returns a string with current state */ state : function(){ return _state; }, /** * @method all * @description returns a promise wich will be resolved when all promises in the given list are resolved * @param {[type]} promises [description] * @return {[type]} [description] */ all : function(promises){ var fullfillCount = 0; var result = []; var promise = new Promise(function(resolve, reject){ var fullfillAll = function(data){ result.push(data); fullfillCount++; if(fullfillCount === promises.length){ resolve(result); } } if(!promises.length){ resolve([]); } for(var i = 0; i < promises.length; i++){ if(!promises[i].then){ } promises[i].then( fullfillAll, reject ); } }, config); return promise; }, waterfall : function(stack){ var promise = new Promise(function(resolve, reject){ var results = []; var nextFromStack = function(counter){ counter = counter || 0; if(counter === stack.length){ resolve(results); return; } stack[counter]() .then(function(result){ results.push(result); nextFromStack(++counter); }) .catch(reject); } nextFromStack(0); }); return promise; }, /** * @method fail * @description the given onfail callback will be called when the promise will be rejected * @param {function} onfail the onfail callback */ fail : function(onfail){ return _then(undefined, onfail); }, /** * @method then * @description executes the given callbacks when the promise will be resolved / rejected * @param {[type]} onFulFilled - will be executed if the will resolved * @param {[type]} onRejected - will be executed if the will rejected */ then : function(onFulFilled, onRejected){ return _then(onFulFilled, onRejected); }, /** * @method success * @description the give callback will called if the promise will be resolved * @param {[type]} onsuccess - the onsuccess callback */ success : function(onsuccess){ return _then(onsuccess, undefined); }, /** * @method catch * @alias for fail */ catch : function(onfail){ return _then(undefined, onfail); }, /** * @method reject * @description rejects the current promise * @param {mixed} reason - a error message */ reject : function(reason) { _reject(reason); }, /** * @method resolve * @description] resolves the given promise * @param {mixed} value - a value */ resolve : function(value){ if(value && typeof value.then === "function"){ var promise = new Promise(function(resolve, reject){ value.then(resolve, reject); }, config) return promise } _fulfill(value); return this; } } }; //set static methods Mold.prototype.Core.Promise.waterfall = Mold.prototype.Core.Promise().waterfall; Mold.prototype.Core.Promise.all = Mold.prototype.Core.Promise().all; Mold.prototype.Core.Base64 = function(){ return { btoa : function(str){ if(_isNodeJS){ return Buffer(str).toString('base64'); }else{ return btoa(str); } } } }() Mold.prototype.Core.VLQ = function VLQ(){ var VLQ_BASE_SHIFT = 5; var VLQ_BASE = 1 << VLQ_BASE_SHIFT; var VLQ_BASE_MASK = VLQ_BASE - 1; var VLQ_CONTINUATION_BIT = VLQ_BASE; var _base64 = function(number){ var intToCharMap = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'.split(''); if (0 <= number && number < intToCharMap.length) { return intToCharMap[number]; } throw new TypeError("Must be between 0 and 63: " + number); } var _toVLQSigned = function(aValue) { return aValue < 0 ? ((-aValue) << 1) + 1 : (aValue << 1) + 0; } var _fromVLQSigned = function(aValue) { var isNegative = (aValue & 1) === 1; var shifted = aValue >> 1; return isNegative ? -shifted: shifted; } /* var _base64Encode = function(value){ return __Mold.Core.Base64.btoa(encodeURIComponent(value).replace(/%([0-9A-F]{2})/g, function(match, p1) { return String.fromCharCode('0x' + p1); })); }*/ return { encode : function base64VLQ_encode(aValue) { var encoded = ""; var digit; var vlq = _toVLQSigned(aValue); do { digit = vlq & VLQ_BASE_MASK; vlq >>>= VLQ_BASE_SHIFT; if (vlq > 0) { digit |= VLQ_CONTINUATION_BIT; } encoded += _base64(digit); } while (vlq > 0); //return aValue; return encoded; }, decode : function base64VLQ_decode(aStr, aIndex, aOutParam) { var strLen = aStr.length; var result = 0; var shift = 0; var continuation, digit; do { if (aIndex >= strLen) { throw new Error("Expected more digits in base 64 VLQ value."); } digit = base64.decode(aStr.charCodeAt(aIndex++)); if (digit === -1) { throw new Error("Invalid base64 digit: " + aStr.charAt(aIndex - 1)); } continuation = !!(digit & VLQ_CONTINUATION_BIT); digit &= VLQ_BASE_MASK; result = result + (digit << shift); shift += VLQ_BASE_SHIFT; } while (continuation); aOutParam.value = fromVLQSigned(result); aOutParam.rest = aIndex; } } } /** * @class SourceMap * @description creates a new source map * @param {} file * @param {[type]} content */ Mold.prototype.Core.SourceMap = function(file, content){ this.names = []; this.mappings = []; this.rawMappings = []; this.lastGeneratedLine = 1; this.lastOriginalColumn = 0; this.lastGeneratedColumn = 0; this.lastOriginalLine = 0; this.vlq = new __Mold.Core.VLQ(); this.files = []; this.files.push(file); this.mapFile = file + ".map"; this.sourcesContent = []; this.sourcesContent.push(content); this.addMapping = function(generatedLine, generatedColumn, originalLine, originalColumn, originalFile, name, code){ var output = ""; if(this.lastGeneratedLine !== generatedLine){ while(this.lastGeneratedLine < generatedLine){ output += ";" this.lastGeneratedLine++; } } output += this.vlq.encode(generatedColumn - this.lastGeneratedColumn); this.lastGeneratedColumn = generatedColumn; output += this.vlq.encode(0); output += this.vlq.encode(originalLine - 1 - this.lastOriginalLine); this.lastOriginalLine = originalLine - 1; output += this.vlq.encode(originalColumn ); this.lastOriginalColumn = originalColumn; if(name){ this.names.push(name); output += this.vlq.encode(this.names.length - 1); } this.mappings.push(output); } this.createMapping = function(){ var output = ""; var len = this.mappings.length; for(var i = 0; i < len; i++){ output += this.mappings[i]; if((i + 1) < this.mappings.length && this.mappings[i + 1].indexOf(';') < 0){ output += ","; } } return output; } this.create = function(){ return { version : 3, file : this.mapFile, sourceRoot : "", sources: this.files, names: this.names, mappings: this.createMapping(), sourcesContent : this.sourcesContent } } } /** * @module Mold.Core.SeedFactory * @description creates a seed object from a configuration * @param {object} seedConf * - the cofiguration object can contain the following properties * - 'name' the name of the seed including the full object path, this property is mandatory * - 'type' the seed dna, the dna describes the way how the module will be executed * - 'code' the code of the seed, this property is mandatory * * @return {object} seedObject * - returns a seed object with the config properties plus the following methods and properties * - 'state' the current state of the seed */ Mold.prototype.Core.SeedFactory = function SeedFactory(conf){ if(!conf){ throw new Error("seedConf must be defined!" + __Mold.getInstanceDescription()); } if(typeof conf !== 'object'){ throw new Error("seedConf must be an object!" + __Mold.getInstanceDescription()); } /** * @class Seed * @description defineds methods and properties of a seed * @param {[type]} properies - properties configuration */ var Seed = function Seed(properties){ if(!properties.name){ throw new Error('A seed needs a name!' + __Mold.getInstanceDescription()) } this.fileMap = []; this.path = null; this.fileData = null; this._sid = __Mold.getId(); this._isCreatedPromise = new __Mold.Core.Promise(false, { throwError : true }); this.isReady = new __Mold.Core.Promise(false, { throwError : true }); this._isReady = false; this.dependenciesLoaded = new __Mold.Core.Promise(false, { throwError : true }); this._dependenciesAreLoaded = false; var that = this; this.dependencies = []; this.injections = {}; this._state = properties.state || __Mold.Core.SeedStates.LOAD; this._addedLines = 0; //append propetises for(var prop in properties){ this[prop] = properties[prop]; } } Seed.prototype = { get sid(){ return this._sid; }, set sid(sid){ throw new Error("The property 'sid' is not writeable! [Mold.Core.SeedManager]" + __Mold.getInstanceDescription()); }, /** * @method state * @description get for the state property * @return {number} returns the current state */ get state(){ return this._state; }, set state(state){ this._state = state; if(state === __Mold.Core.SeedStates.READY && !this._isReady){ this.isReady.resolve(this); __Mold.Core.SeedManager.checkReady(); this._isReady = true; } }, /** * @method hasDependency * @description checks if a specific dependency exists * @param {string} name - the name of the dependency * @return {boolean} returns true if the seed has the given dependency otherwise it return false */ hasDependency : function(name){ for(var i = 0; i < this.dependencies.length; i++){ var value = this.dependencies[i]; if( (typeof value === "string" && value === name) || (typeof value === "object" && value.name === name) ){ return true; } } return false; }, /** * @method hasDependencies * @description checks if the seed has dependcies * @return {boolean} return true if the seed has dependencies */ hasDependencies : function(){ if(this.dependencies && this.dependencies.length){ return true; } return false; }, /** * @addDependency * @description adds a dependency to the seed * @param {string} dependency - the name of the dependency */ addDependency : function(dependency){ if(this.hasDependency(dependency)){ return false; } if(!this.dependencies){ this.dependencies = []; } this.dependencies.push(dependency); }, addInjection : function(injection){ for(var inject in injection){ if(!this.injections[inject]){ this.injections[inject] = injection[inject]; } } }, /** * @property isLoaded * @description true if the seed is loaded * @return {boolean} */ get isLoaded(){ return (this.state > __Mold.Core.SeedStates.LOAD) ? true : false; }, /** * @method load * @description loads the seed if it is not loaded * @return {promise} */ load : function(){ if(!this.isLoaded){ var that = this; try { //try to get the path this.path = __Mold.Core.Pathes.getPathFromName(this.name); }catch(e){ throw new Error("Can not resolve path for [" + this.name + "], maybe the seed is not installed!") } var file = new __Mold.Core.File(this.path); var promise = file.load(); var that = this; return new Promise(function(resolve, reject){ promise .then(function(data){ that.fileData = data that.mapFileData(); resolve(that); }) .catch(function(err){ try{ if(__Mold.Core.Config.get('disable-dependency-errors')){ //if there is an error and disabling errors is active resolve seed that.loadingError = true; that.state = __Mold.Core.SeedStates.READY; that._isCreatedPromise.resolve(that); resolve(that); }else{ var error = new Error("Can not load seed: '" + that.path + "'! [" + that.name + "]" + __Mold.getInstanceDescription()); reject(error) //throw error; } }catch(e){ reject(e) } }) }); } }, /** * @method mapFileData * @description creates a filemap from the raw file */ mapFileData : function(){ if(this.fileData){ var line = 0, len = this.fileData.length, i = 0; var stopCollection = false, collected = "", charNumber = 0, current = ""; var that = this; var addEntry = function(name, line, charNumber){ that.fileMap.push({ name : name, line : line, charNumber : charNumber - name.length }); } for(; i < len; i++){ current = this.fileData[i]; switch(current){ case "\n": line++; addEntry(collected, line, charNumber); collected = ""; charNumber = 1; break; case " ": case "\t": addEntry(collected, line, charNumber); collected = ""; break; default: collected += current; stopCollection = false; } charNumber++; } } }, /** * @method buildSourceMap * @description builds a source map from a filemap, used for pre-transpiled seeds debugging * @return {string} returns the source map */ buildSourceMap : function(){ var map = new __Mold.Core.SourceMap(this.path, this.fileData); var that = this; this.fileMap.forEach(function(entry){ map.addMapping(entry.line + that._addedLines, entry.charNumber, entry.line, entry.charNumber, 0, entry.name, ''); }); return "\n//# sourceMappingURL=data:application/json;base64," + __Mold.Core.Base64.btoa(JSON.stringify(map.create())) + ""; }, /** * @method checkDependencies * @description checks the seed dependencies * @return {promise} returns a promise wich will be resolved if all dependencies are loaded */ checkDependencies : function(){ var that = this; __Mold.Core.DependencyManager.checkDependencies(this).then(function(){ that.dependenciesLoaded.resolve(that.dependencies); that._dependenciesAreLoaded = true; }) return this.dependenciesLoaded; }, /** * @method catched * @description adds catched information to the seed * @param {object} info - an object with the seed meta informations * @param {function} code - the seed code * @return {promise} returns the isCreated promise */ catched : function(info, code){ if(this.code){ throw new Error("The seed code is already created! [" + this.name + "] " + __Mold.getInstanceDescription()); } this.code = code; for(var prop in info){ if(prop !== "name"){ this[prop] = info[prop]; } } this._isCreatedPromise.resolve(this); }, /** * @method create * @description executes a pre-transpiled seed to catche it's informations * @return {promise} returns a promise which will be resolved if the seed is created */ create : function(){ if(this.loadingError){ return this._isCreatedPromise; } if(!this.fileData){ throw new Error("Can not created script without file data! [" + this.name + "]"); } var fileData = "//" + this.name + " \n"; var typeHandler = __Mold.Core.SeedTypeManager.get(this.type); if(typeHandler && typeHandler.preCreate){ this.fileData = typeHandler.preCreate(this.fileData); } fileData += "(function() { var Seed = function(info, code) { Mold.Core.SeedManager.catchSeed(info, code, " + this.sid + ")}\n" + this.fileData + "})()"; this._addedLines = this._addedLines + 2; fileData += this.buildSourceMap(); if(_isNodeJS){ try{ var scriptBox = { Mold : __Mold, console : console } __Mold.copyGlobalProperties(scriptBox); var context = new vm.createContext(scriptBox); var script = new vm.Script(fileData); script.runInContext(context, { filename: this.path }); }catch(e){ e.message += " [" + this.path + "]"; throw e; } }else{ this.scriptFile = document.createElement('script'); this.scriptFile.src = 'data:text/javascript,' + encodeURIComponent(fileData) document.body.appendChild(this.scriptFile); } return this._isCreatedPromise; }, /** * @method execute * @description executes the seed, injects dependencies if defined and add it to the name space * @return {[type]} [description] */ execute : function(){ if(this.loadingError || __Mold.Core.Config.get('stop-seed-executing')){ return; } var typeHandler = __Mold.Core.SeedTypeManager.get(this.type); if(!typeHandler){ throw new SeedError("SeedType '" + this.type + "' not found!", this.name); } if(!this.code){ throw new SeedError("Code property is not defined!", this.name); } if(Object.keys(this.injections).length){ var closure = "//" + this.name + "\n"; this._addedLines++; for(var inject in this.injections){ //check if injection has no loadingError if(!__Mold.Core.SeedManager.get(this.injections[inject] ).loadingError){ closure += " var " + inject + " = " + this.injections[inject] + "; \n" ; this._addedLines++; } } closure += " return " + this.code.toString() + "\n"; closure += this.buildSourceMap(); if(_isNodeJS){ try{ var sandbox = { output : function(){}, Mold : __Mold, console : console } __Mold.copyGlobalProperties(sandbox); var context = new vm.createContext(sandbox); var script = new vm.Script("var output = function() { " + closure + "\n}()", { filename: this.path, lineOffset : this.fileData.split("\n").length - closure.split("\n").length + 1}); var test = script.runInContext(sandbox, { filename: this.path }); this.code = sandbox.output; }catch(e){ e.message += " [" + this.path + "]"; throw e; } }else{ this.code = new Function(closure)(); } } this.executedValue = typeHandler.create(this); __Mold.Core.NamespaceManager.addCode(this.name, this.executedValue); } } return new Seed(conf); } /** * @module SeedManager * @description manage all appended seeds */ Mold.prototype.Core.SeedManager = function(){ var _seeds = []; var _seedIndex = {}; var _readyPromise = new __Mold.Core.Promise(false, { throwError : true }); var _isReady = false; var _deleteSeedByName = function(name){ for(var i = 0; i < _seeds.length; i++){ if(_seeds[i].name === name){ _seeds.splice(i, 1); return true; } } return false; } return { /** * @property {number} len * @description contains the amount of added seeds */ get count(){ return _seeds.length; }, set count(value){ throw new Error("The property 'count' is not writeable! [Mold.Core.SeedManager]" + __Mold.getInstanceDescription()); }, /** * @method getBySid * @description returns a seed be the given seed id, if no seed is found it returns undefined * @param {number} sid - the seed id * @return {mixed} returns a seed or undefined */ getBySid : function(sid){ return _seeds.find(function(seed){ return (seed.sid === sid) ? true : false; }); }, /** * @method catchSeed * @description catches a seed and ad the catched information to the registerd seed * @param {object} seedInfo - an object with seed informations * @param {function} seedCode - the code of the seed * @param {number} [ident] - the seed id */ catchSeed : function(seedInfo, seedCode, ident){ var seed; if(seedInfo.name){ seed = this.get(seedInfo.name); //if no seed found with this name create a new one if(!seed){ seed = __Mold.Core.SeedFactory({ name : seedInfo.name, state : __Mold.Core.SeedStates.LOAD_DEPENDENCIES, }); this.add(seed); seed.catched(seedInfo, seedCode); __Mold.Core.SeedFlow.exec(seed) return this; } }else{ //if no name is defined get seed by ident seed = this.getBySid(ident); } if(seed){ seed.catched(seedInfo, seedCode); } return this; }, /** * @method add * @description adds a seed to the seedmangaer * @param {object} seed - the seed */ add : function(seed){ if(_seedIndex[seed.name]){ if(seed.overwrite){ _deleteSeedByName(seed.name); }else{ return false; } } _seeds.push(seed); _seedIndex[seed.name] = seed; return this; }, /** * @method get * @description returns a seed by the given name * @param {string} name - the seed name, expects the full name for example Mold.Core.Test * @return {object | null} returns the selected seed if found */ get : function(seed){ var name = (typeof seed === 'object') ? seed.name : seed; return _seedIndex[name] || null; }, /** * @method each * @description iterates through the all added seeds and executes a callback per seed with the as argument * @param {dunction} callback - a function with the seed as argument */ each : function(callback){ for(var name in _seedIndex){ callback(_seedIndex[name], name) } return this; }, /** * @method remove * @description removes a seed * @param {mixed} seed - expects a seed or a seedname */ remove : function(seed){ var name = (typeof seed === 'object') ? seed.name : seed; delete _seedIndex[name]; _deleteSeedByName(name); return this; }, /** * @method checkReady * @description checks if all seeds has the state ready * @return {boolean} returns true if all seeds are ready otherwise it returns false */ checkReady : function(){ //renew promise if the last check was true; if(this._lastReadyState){ _readyPromise = new __Mold.Core.Promise(false, { throwError : true }); this.isReady = _readyPromise; __Mold._reCreateReadyPromise(); } var i = 0, len = _seeds.length; for(; i < len; i++){ if(_seeds[i].state !== __Mold.Core.SeedStates.READY){ this._lastReadyState = false; return false; } } //resolve promise and create a new one _readyPromise.resolve(_seeds); this._lastReadyState = true; return true; }, /** * @properts isReady * description returns a promise which will resolved if all seeds are ready * @return {Boolean} [description] */ isReady : _readyPromise } }(); /** * @module Mold.Core.NamespaceManger * @description provide methods for creating / validating a */ Mold.prototype.Core.NamespaceManager = function(){ return { /** * @method validateName * @description checks if the given name is a valid Mold namespace name * @param {string} name - a string with the name * @return {boolean} returns true if the name is valid and false if not */ validate : function(name){ if(/^[A-Z]{1}[a-z|A-Z]*$/.test(name)){ return true; }else{ return false; } }, /** * @method create * @description creates a new namespace with the given name * @param {string} name - the name of the namespace * @param {object} [root] - an optional root namespace if this parameter is not set the new namespace will be created inside the globale space * @return {object} returns the create namespace */ create : function(name, root){ if(!this.validate(name)){ throw new Error("'" + name + "' is not a valid Namespace name!" + __Mold.getInstanceDescription()); } root = root || global; root[name] = {}; return root[name]; }, /** * @method exists * @description checks if a namespace exists * @param {string} name - a string with the namespace name * @param {object} [root] - an optional object with the root namepsace * @return {boolean} returns true if the namespace existes otherwise false */ exists : function(name, root){ root = root || global; if(root[name]){ return true; } return false; }, /** * @method addCode * @description adds code to the end of the given namespace chain, non existing namespaces will be created * @param {string} chainName - a string with the namespaces seperated by . * @param {mixed} code - the code */ addCode : function(chainName, code){ var parts = chainName.split('.'); var root = global; for(var i = 0; i < parts.length - 1; i++){ var part = parts[i]; if(!this.exists(part, root)){ root = this.create(part, root); }else{ root = root[part]; } } root[parts[parts.length -1]] = code; } } }(); Mold.prototype.Core.SeedTypeManager = function(){ var _seedTypeIndex = {}; return { /** * @method validateSeedType * @description validates a seed type object * @param {object} type - a seed type object * @throws {SeedTypeError} if the type object miss mandantory properties */ validate: function(type){ if(!type.name){ throw new SeedTypeError('SeedType \'name\' is not defined!' + __Mold.getInstanceDescription()); } if(!type.create){ throw new SeedTypeError('SeedType \'create\' is not defined! [' + type.name + ']' + __Mold.getInstanceDescription()); } if(typeof type.create !== 'function'){ throw new SeedTypeError('SeedType \'create\' is not a function! [' + type.name + ']' + __Mold.getInstanceDescription()); } }, /** * @property {number} count * @description returns the amount of stored seed types */ get count(){ return Object.keys(_seedTypeIndex).length; }, set count(value){ throw new Error("the property 'len' is not writeable! [Mold.Core.SeedTypeManger]" + __Mold.getInstanceDescription()); }, /** * @method addSeedType * @description adds a new seed type * @param {object} type - expects a seed typ object */ add : function(type){ this.validate(type); _seedTypeIndex[type.name] = type; return this; }, /** * @method removeSeedType * @description removes a seed type by name * @param {type} name - the seed type name */ remove : function(name){ var type = _seedTypeIndex[name]; if(type && typeof type.destruct === 'function'){ type.destruct(); } delete _seedTypeIndex[name]; }, /** * @method getSeedType * @description returns a seed type object by the given name * @param {string} name - a string with the name * @return {name} returns the seed type obejct or null with no object was found */ get : function(name){ return _seedTypeIndex[name] || null; } } }(); Mold.prototype.Core.DependencyManager = function(){ var _dependenyPropertys = {}; var _getRelativeDependencies = function(dep, seed){ var depParts = dep.split("."); var seedName = seed.name; if(depParts[0] === ""){ var nameParts = seedName.split("."); depParts.shift(); nameParts.pop(); return nameParts.concat(depParts).join('.'); } return dep; } return { /** * @method find * @description find all dependecy properties inside the seed and add them as dependencx * @param {[type]} seed the seed */ find : function(seed){ for(var prop in _dependenyPropertys){ if(seed[prop]){ if(Array.isArray(seed[prop])){ for(var i = 0; i < seed[prop].length; i++){ if(__Mold.isObject(seed[prop][i])){ for(var injection in seed[prop][i]){ seed[prop][i][injection] = _getRelativeDependencies(seed[prop][i][injection], seed) seed.addInjection(seed[prop][i]); seed.addDependency(seed[prop][i][injection]); } }else{ seed.addDependency(_getRelativeDependencies(seed[prop][i], seed)); } } } } } return this; }, /** * @method checkDependencies * @description checks if all dependencies are loaded * @param {Seed} seed - the seed to check * @return {boolean} returns true if all dependecies are loaded, otherwise false */ checkDependencies : function(seed){ var seedsLoades = []; for(var i = 0; i < seed.dependencies.length; i++){ var depSeed = __Mold.Core.SeedManager.get(seed.dependencies[i]); if(depSeed){ if(depSeed.hasDependency(seed.name)){ throw new Error("There is a circular dependency between " + seed.name + " and " + depSeed.name); } seedsLoades.push(depSeed.isReady); }else{ var promise = __Mold.load(seed.dependencies[i]); seedsLoades.push(promise); } } var promise = new __Mold.Core.Promise(false, { throwError : true }); if(seedsLoades.length){ promise = promise.all(seedsLoades, { throwError : true }); }else{ promise.resolve(); } return promise; }, /** * @method addDependencyProperty * @description adds a dependency property * @param {string} name - name of the property */ addDependencyProperty : function(name){ if(!_dependenyPropertys[name]){ _dependenyPropertys[name] = true; } return this; }, } }(); /** * @module Mold.Core.SeedStates * @description includes all available seed states * @type {enum} */ Mold.prototype.Core.SeedStates = { NEW : 1, LOAD : 2, LOADED : 2, PREPARSE : 3, PARSE : 4, INSPECT: 5, LOAD_DEPENDENCIES : 6, TRANSFORM : 7, VALIDATE : 8, TRANSPILING : 9, EXECUTE : 10, READY : 11, ERROR : 12, } /** * @module Mold.Core.SeedFlow * @static * @description static module provides methods to control the seed flow */ Mold.prototype.Core.SeedFlow = function(){ var _stateFlows = {}; var _afterFlow = {}; var _executedSeedFlows = {}; return { /** * @method on * @description adds a flow middleware to the seed flow * @param {number} state - the state where the middleware should be executed * @param {function} action - the middleware action * @return {this} returns this for chaining */ on : function(state, action){ _stateFlows[state] = _stateFlows[state] || []; _stateFlows[state].push(action); return this; }, /** * @method onAfter * @description adds a after-flow middleware to the seed flow, use this to change the state of a seed * @param {number} state - the state where the middleware should be executed * @param {function} action - the middleware action * @return {this} returns this for chaining */ onAfter : function(state, action){ _afterFlow[state] = action; return this; }, /** * @method exec * @description starts to execute the seed flow for thegiven seed * @param {object} seed - the seed * @param {number} [index] - the index of the current states middleware, this property is optional if not set the index is 0 * @return {this} returns this for chaining */ exec : function(seed, index){ var index = index || 0; if(_executedSeedFlows[seed.name] && _executedSeedFlows[seed.name][seed.state] >= index){ return; } if(!_stateFlows[seed.state] || !_stateFlows[seed.state][index]){ if(_afterFlow[seed.state]){ index = null; var flow = _afterFlow[seed.state]; }else{ return; } }else{ var flow = _stateFlows[seed.state][index]; } var done = function(){ var next = (index === null) ? null : index + 1; this.exec(seed, next); }.bind(this); _executedSeedFlows[seed.name] = _executedSeedFlows[seed.name] || {}; _executedSeedFlows[seed.name][seed.state] = index; flow(seed, done); return this; } } }(); /** * @module Mold.Core.Pathes * @description provides methods to transform an generate pathes */ Mold.prototype.Core.Pathes = function(){ var _pathHandler = {}; return { /** * @method on * @description registers a path parsing function for a specific path type * @param {string} type - the path type * @param {Function} callback - the function that should be executed on this type of pathes */ on : function(type, callback){ _pathHandler[type] = callback; return this; }, /** * @method getPathFromName * @description translite a seed loading string into a path * @param {string} name - the string to convert * @return {string} returns a seed path */ getPathFromName : function(name, ignorExistingCheck){ if(typeof name !== "string"){ throw new TypeError("Name must be a string. [Mold.Core.Pathes] " + __Mold.getInstanceDescription()); } var type = 'mold'; if(~name.indexOf(":")){ var parts = name.split(":"); type = parts[0]; name = parts[1]; } if(!_pathHandler[type]){ throw new Error("Path type '" + type + "' is not supported! [Mold.Core.Pathes]" + __Mold.getInstanceDescription()); } return _pathHandler[type](name, ignorExistingCheck); }, getNameFromPath : function(path){ var type = "mold-backwards"; if(!_pathHandler[type]){ throw new Error("Path type '" + type + "' is not supported! [Mold.Core.Pathes]" + __Mold.getInstanceDescription()); } return _pathHandler[type](path); }, /** * @method isMoldPath * @