mold-js
Version:
MoldJS structure and pattern framework commandline interface
1,919 lines (1,642 loc) • 87.9 kB
JavaScript
"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
* @