Created
July 17, 2018 12:26
-
-
Save sweikenb/27eea47d4f4a719c2e8d88e5957d4b2a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* strap.js by Simon Schroeer | http://strapjs.org | License: MIT | |
*/ | |
var strapjs_root = {}, | |
strapjs_eventstack = {}, | |
strapjs = {}; | |
/** | |
* Get the global root object | |
*/ | |
(function () { | |
strapjs_root = this; | |
strapjs_root.strapjs = strapjs; | |
})(); | |
/** | |
* Foreach wrapper for objects and arrays | |
* | |
* @param loopable | |
* @param callback | |
* @constructor | |
*/ | |
strapjs.Foreach = function (loopable, callback) { | |
if (loopable instanceof Array) { | |
var count = loopable.length, i; | |
for (i = 0; i < count; ++i) { | |
if (typeof loopable[i] !== 'undefined') { | |
if (false === callback(i, loopable[i])) { | |
return; | |
} | |
} | |
} | |
} | |
else { | |
for (var index in loopable) { | |
if (loopable.hasOwnProperty(index)) { | |
if (false === callback(index, loopable[index])) { | |
return; | |
} | |
} | |
} | |
} | |
}; | |
/** | |
* Detects the length of the given variable. | |
* If an array or object is given, the elements-count will be returned. | |
* | |
* @param countable | |
* @constructor | |
*/ | |
strapjs.Length = function (countable) { | |
var isObject = (typeof countable === 'object'); | |
if (!isObject || countable instanceof Array) { | |
return countable.length; | |
} | |
else { | |
var count = 0; | |
strapjs.Foreach(countable, function () { | |
++count; | |
}); | |
return count; | |
} | |
}; | |
/** | |
* Fork the script execution for the given callback (in fact it's an parallelisation, not a 'real' fork) | |
* | |
* @param callback | |
* @constructor | |
*/ | |
strapjs.Fork = function (callback) { | |
strapjs_root.setTimeout(callback, 0); | |
}; | |
/** | |
* Executes the given callbacks in parallel | |
* | |
* @param callbacks | |
* @param callbackFinished | |
* @param timeout | |
* @param provideDoneHandler | |
* @constructor | |
*/ | |
strapjs.Parallelize = function (callbacks, callbackFinished, timeout, provideDoneHandler) { | |
var Service = strapjs.Class({ | |
// resolver setup | |
initialize: function (callbacks, callbackFinished, timeout, provideDoneHandler) { | |
// set attributes | |
this.callbackFinished = callbackFinished || null; | |
this.processesTimeout = timeout ? (parseInt(timeout) + this.getTime()) : 0; | |
this.processesAll = callbacks.length; | |
this.processesDone = 0; | |
this.watchTimer = false; | |
this.provideDoneHandler = provideDoneHandler || false; | |
var watchTimeoutNeeded = (this.processesTimeout > 0 && this.callbackFinished); | |
strapjs.Foreach(callbacks, function (index, callback) { | |
if (watchTimeoutNeeded) { | |
strapjs.Fork(this.callbackWrapper.bind(this, callback)) | |
} | |
else { | |
strapjs.Fork(callback); | |
} | |
}.bind(this)); | |
if (watchTimeoutNeeded) { | |
this.watchCallbacks(); | |
} | |
}, | |
getTime: function () { | |
return Math.floor((new Date()).getTime() / 1000); | |
}, | |
callbackWrapper: function (callback) { | |
if (false !== this.provideDoneHandler) { | |
// run the callback with the custom handler | |
callback.apply(null, [this._incrementFinished.bind(this)]); | |
} | |
else { | |
// run the callback | |
callback.apply(null); | |
// increment finished | |
this._incrementFinished(); | |
} | |
}, | |
_incrementFinished: function () { | |
// increment 'done'-count | |
++this.processesDone; | |
// all processes done? | |
if (this.processesDone >= this.processesAll) { | |
this.markAsDone(); | |
} | |
}, | |
watchCallbacks: function () { | |
// all processes done? | |
if (this.processesDone >= this.processesAll || this.processesTimeout < this.getTime()) { | |
this.markAsDone(); | |
} | |
else { | |
this.watchTimer = strapjs_root.setTimeout(this.watchCallbacks.bind(this), 10); | |
} | |
}, | |
markAsDone: function () { | |
if (this.watchTimer) { | |
strapjs_root.clearTimeout(this.watchTimer); | |
this.watchTimer = false; | |
} | |
// need to trigger the finish callback? | |
if (this.callbackFinished) { | |
var finished = this.callbackFinished; | |
this.callbackFinished = null; | |
// trigger callback | |
finished(this.processesAll, this.processesDone); | |
} | |
} | |
}); | |
return new Service(callbacks, (callbackFinished || null), (timeout || 0), (provideDoneHandler || false)); | |
}; | |
/** | |
* Creates a new class with the given implementation and optional parent inheritance | |
* | |
* @param parent | |
* @param implementation | |
* @returns object | |
* @constructor | |
*/ | |
strapjs.Class = function (parent, implementation) { | |
parent = parent || null; | |
implementation = implementation || null; | |
if (!implementation) { | |
implementation = parent; | |
parent = null; | |
} | |
// create a protoclass | |
var protoclass = function () { | |
this.initialize.apply(this, arguments); | |
}; | |
if (parent) { | |
if (typeof parent.prototype !== 'undefined') { | |
parent = parent.prototype; | |
} | |
strapjs.Foreach(parent, function (prop, method) { | |
protoclass.prototype[prop] = method; | |
}); | |
} | |
if (implementation) { | |
strapjs.Foreach(implementation, function (prop, method) { | |
protoclass.prototype[prop] = method; | |
}); | |
} | |
return protoclass; | |
}; | |
/** | |
* Registers the given definitions within the namespace | |
* @param ns | |
* @param definitions | |
* @returns {*} | |
* @constructor | |
*/ | |
strapjs.Namespace = function (ns, definitions) { | |
var pointer = null, | |
path = '', | |
frags = ns.split('.'); | |
frags.unshift('strapjs_root'); | |
strapjs.Foreach(frags, function (index, space) { | |
path += (index ? '.' : '') + space; | |
pointer = eval(path); | |
if (typeof pointer === 'undefined') { | |
eval(path + " = {};"); | |
} | |
}); | |
strapjs.Foreach(definitions, function (prop, definition) { | |
eval(path + "[prop] = definition;"); | |
}); | |
return eval(path); | |
}; | |
/** | |
* Fire the given event with the optional payload | |
* | |
* @param eventName | |
* @param payload | |
* @constructor | |
*/ | |
strapjs.EventFire = function (eventName, payload) { | |
if (typeof eventName !== 'string') { | |
return; | |
} | |
if (typeof strapjs_eventstack[eventName] !== 'undefined') { | |
var event = { | |
'name': eventName, | |
'payload': (payload || null) | |
}; | |
strapjs.Foreach(strapjs_eventstack[eventName], function (index, callback) { | |
return callback(event); | |
}); | |
} | |
}; | |
/** | |
* Remove the whole event with all registred subscribers | |
* | |
* @param eventName | |
* @constructor | |
*/ | |
strapjs.EventErase = function (eventName) { | |
if (typeof eventName !== 'string') { | |
return; | |
} | |
if (typeof strapjs_eventstack[eventName] !== 'undefined') { | |
delete strapjs_eventstack[eventName]; | |
} | |
}; | |
/** | |
* Subscribe to the given event | |
* | |
* @param eventName | |
* @param callback | |
* @constructor | |
*/ | |
strapjs.EventSubscribe = function (eventName, callback) { | |
if (typeof eventName !== 'string') { | |
return; | |
} | |
if (typeof strapjs_eventstack[eventName] === 'undefined') { | |
strapjs_eventstack[eventName] = []; | |
} | |
strapjs_eventstack[eventName].push(callback); | |
}; | |
/** | |
* Unsubscribe to the given event | |
* | |
* @param eventName | |
* @param callback | |
* @constructor | |
*/ | |
strapjs.EventUnsubscribe = function (eventName, callback) { | |
if (typeof eventName !== 'string') { | |
return; | |
} | |
if (typeof strapjs_eventstack[eventName] !== 'undefined') { | |
strapjs.Foreach(strapjs_eventstack[eventName], function (index, current) { | |
if (current === callback) { | |
delete strapjs_eventstack[eventName][index]; | |
if (1 > strapjs_eventstack[eventName].length) { | |
delete strapjs_eventstack[eventName]; | |
} | |
} | |
}); | |
} | |
}; | |
/** | |
* Creates a namespace resolver instance | |
* | |
* @returns {strapjs.Class} | |
* @constructor | |
*/ | |
strapjs.Use = function () { | |
// create a new namespace resolver | |
var Resolver = strapjs.Class({ | |
// resolver setup | |
initialize: function () { | |
this.namespaces = {}; | |
}, | |
// register the given namespace | |
registerNamespace: function (ns, alias) { | |
alias = alias || ns; | |
if (typeof this.namespaces[alias] !== 'undefined') { | |
throw "Can't use alias '" + alias + "' for '" + ns + "': already defined."; | |
} | |
this.namespaces[alias] = ns; | |
}, | |
// use (register) the given namespaces | |
use: function () { | |
strapjs.Foreach(arguments, function (index, argument) { | |
switch (typeof argument) { | |
case 'object': | |
if (argument instanceof Array) { | |
strapjs.Foreach(argument, function (subIndex, value) { | |
this.use(value); | |
}.bind(this)); | |
} | |
else { | |
strapjs.Foreach(argument, function (subIndex, value) { | |
if (typeof value === 'object') { | |
this.use(value); | |
} | |
else { | |
this.registerNamespace(value, subIndex); | |
} | |
}.bind(this)); | |
} | |
break; | |
case 'string': | |
if (isNaN(parseInt(argument))) { | |
this.registerNamespace(argument); | |
} | |
} | |
}.bind(this)); | |
// return current instance for chaining | |
return this; | |
}, | |
// resolve the given classname and return its definition | |
get: function (className) { | |
// keep the matching class | |
var matchingClass; | |
// function passed? | |
if (typeof className === 'function') { | |
matchingClass = className; | |
} | |
if (!matchingClass) { | |
// direct alias/namespace found? | |
if (typeof this.namespaces[className] !== 'undefined') { | |
matchingClass = eval(this.namespaces[className]); | |
} | |
if (!matchingClass) { | |
// check registered namespaces with classes | |
var pattern = new RegExp('(.*)\\.' + className + '$', 'g'); | |
strapjs.Foreach(this.namespaces, function (alias, ns) { | |
if (-1 !== alias.search(pattern)) { | |
matchingClass = eval(ns); | |
return false; | |
} | |
return true; | |
}); | |
if (!matchingClass) { | |
// check registered namespaces without classes | |
strapjs.Foreach(this.namespaces, function (alias, ns) { | |
matchingClass = eval("strapjs_root." + ns + "." + className); | |
return (typeof matchingClass === 'undefined'); | |
}); | |
if (!matchingClass) { | |
// not found | |
throw "strap.js: Class not found: " + className; | |
} | |
} | |
} | |
} | |
return matchingClass; | |
}, | |
// create an new instance of the given class | |
create: function (className, params, callback) { | |
// create instance | |
var instance = this.get(className).apply(null, params || []); | |
// trigger callback if required | |
if (callback) { | |
callback(instance); | |
} | |
// return instance | |
return instance; | |
} | |
}); | |
// pass the given arguments to the resolver and return it | |
var loader = new Resolver(); | |
loader.use(arguments); | |
return loader; | |
}; | |
/** | |
* Merges two or more objects/arrays into each other and returns the result. | |
* | |
* NOTICE: The original objects/arrays WON'T be changed! | |
* | |
* If the last parameter is set to 'true', a recursive deep-merge will be done. | |
* | |
* @param base | |
* @param ext | |
* @param deep | |
* @constructor | |
*/ | |
strapjs.Merge = function (base, ext, deep) { | |
// validate parameter count | |
var argc = arguments.length; | |
if (argc < 2) { | |
return base; | |
} | |
// prepare the affected object | |
var affected = arguments, | |
deepMerge = (true === deep), | |
lastPost = (argc - 1); | |
if (true !== deep && typeof affected[lastPost] === 'boolean') { | |
deepMerge = (true === affected[lastPost]); | |
delete affected[lastPost]; | |
--argc; | |
} | |
// merge objects | |
var target = {}; | |
for (var i = 0; i < argc; i++) { | |
// get the current object | |
var current = affected[i]; | |
// skipp unprocessable types | |
if (typeof current !== 'object') { | |
continue; | |
} | |
// merge | |
strapjs.Foreach(current, function (key, value) { | |
if (deepMerge && typeof value === 'object') { | |
target[key] = strapjs.Merge(target[key], value, true); | |
} | |
else { | |
target[key] = value; | |
} | |
}); | |
} | |
// return the merged target | |
return target; | |
}; | |
// add shortcuts | |
if (typeof strapjs_isolation === 'undefined' || strapjs_isolation !== true) { | |
// support module loaders and define shortcut functions | |
var exprotEnabled = (typeof module !== 'undefined' && typeof module.exports !== 'undefined'); | |
// export strap.js itself | |
if (exprotEnabled) { | |
module.exports = strapjs_root.strapjs; | |
} | |
// add/export shortcuts | |
strapjs.Foreach({ | |
'foreach_': 'Foreach', | |
'length_': 'Length', | |
'fork_': 'Fork', | |
'parallelize_': 'Parallelize', | |
'namespace_': 'Namespace', | |
'class_': 'Class', | |
'use_': 'Use', | |
'fire_': 'EventFire', | |
'erase_': 'EventErase', | |
'subscribe_': 'EventSubscribe', | |
'unsubscribe_': 'EventUnsubscribe', | |
'merge_': 'Merge' | |
}, function (shortcut, origin) { | |
strapjs_root[shortcut] = strapjs[origin]; | |
if (exprotEnabled) { | |
module.exports[shortcut] = strapjs[origin]; | |
} | |
}); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment