diff options
Diffstat (limited to 'injector.js')
-rw-r--r-- | injector.js | 426 |
1 files changed, 0 insertions, 426 deletions
diff --git a/injector.js b/injector.js deleted file mode 100644 index e644539..0000000 --- a/injector.js +++ /dev/null @@ -1,426 +0,0 @@ -/*global module, setTimeout*/ - -var Injector = (function() { - - var Deferred = (function() { - var defer = function(fn, value) {setTimeout(function() {fn(value);});}; - var runFn = function(fn, deferred, action) { - return function(value) { - try { - var result = fn(value); - if (result && result.then) { - result.then(function(value) { - deferred.resolve(value); - }, function(value) { - deferred.reject(value); - }); - } else { - deferred.resolve(result); - } - } catch (e) { - deferred.reject(e); - } - }; - }; - - var Promise = function() { - this.resolved = false; - this.rejected = false; - this.onSuccess = []; - this.onError = []; - }; - Promise.prototype.then = function(success, error) { - success = success || function(x) {return x;}; - error = error || function(x) {throw x;}; - var deferred = new Deferred(); - var successFn = runFn(success, deferred); - var errorFn = runFn(error, deferred); - if (this.resolved) { - defer(successFn, this.value); - } else if (this.rejected) { - defer(errorFn, this.value); - } else { - if (this.onSuccess != null) - this.onSuccess.push(successFn); - if (this.onError != null) - this.onError.push(errorFn); - } - return deferred.promise; - }; - - var Deferred = function() { - this.promise = new Promise(); - }; - Deferred.prototype.resolve = function(value) { - if (this.promise.resolved) { - throw new Error("Cannot re-resolve already resolved promise"); - } else if (this.promise.rejected) { - throw new Error("Cannot resolve a rejected promise"); - } else { - this.promise.resolved = true; - this.promise.value = value; - var handlers = this.promise.onSuccess; - this.promise.onSuccess = null; - this.promise.onError = null; - setTimeout(function() { - handlers.forEach(function(handler) { - handler(value); - }); - }); - } - }; - Deferred.prototype.reject = function(value) { - if (this.promise.resolved) { - throw new Error("Cannot reject an already resolved promise"); - } else if (this.promise.rejected) { - throw new Error("Cannot re-reject a rejected promise"); - } else { - this.promise.rejected = true; - this.promise.value = value; - var handlers = this.promise.onError; - this.promise.onSuccess = null; - this.promise.onError = null; - setTimeout(function() { - handlers.forEach(function(handler) { - handler(value); - }); - }); - } - }; - - Deferred.waitForAll = function(promises) { - var deferred = new Deferred(); - var successes = 0, errors = 0; - var errorResults = {}, successResults = {}; - var expected = promises.length; - promises.forEach(function(promise, i) { - promise.then(function(value) { - successResults[i] = value; - successes++; - maybeFinish(); - }, function(error) { - errorResults[i] = error; - errors++; - maybeFinish(); - }); - }); - var maybeFinish = function() { - if (successes == expected) { - var array = []; - for (var i = 0, l = expected; i < l; ++i) - array.push(successResults[i]); - deferred.resolve(array); - } else if (successes + errors == expected) { - deferred.reject({ - errors: errorResults, - values: successResults - }); - } - }; - maybeFinish(); - return deferred.promise; - }; - - return Deferred; - })(); - - var DepsParser = (function() { - var fnArgRegex = /^function[^(]*\(([^)]*)\)/; - var parseFnArgs = function(fn) { - var parts = fnArgRegex.exec(fn.toString().replace(/\s+/g, "")); - if (parts == null) { - throw new Error("Unable to parse fn definition"); - } else { - return parts[1] ? parts[1].split(/,/) : []; - } - }; - - var parseSpec = function(spec) { - var fn, dependencies; - if (typeof(spec) == "function" || spec instanceof Function) { - dependencies = parseFnArgs(spec); - fn = spec; - } else { - fn = spec[spec.length - 1]; - dependencies = spec.slice(0, spec.length - 1); - } - return [fn, dependencies]; - }; - - var parseDep = function(dep) { - var parts = dep.split("!"); - if (parts.length == 0) { - throw new Error("Invalid dependency: " + dep); - } else if (parts.length == 1) { - return {prefix: "", name: parts[0]}; - } else { - return {prefix: parts[0], name: parts.slice(1).join("!")}; - } - }; - - return { - spec: parseSpec, - dep: parseDep - }; - })(); - - var Injector = (function(Deferred, DepsParser) { - - var Injector = function(plugins) { - // plugins are a list to be executed in order - this.plugins = plugins; - }; - - Injector.rejected = function(message, cause) { - var deferred = this.defer(); - deferred.reject(this.error(message, cause)); - return deferred.promise; - }; - Injector.prototype.rejected = Injector.rejected; - - Injector.resolved = function(value) { - var deferred = this.defer(); - deferred.resolve(value); - return deferred.promise; - }; - Injector.prototype.resolved = Injector.resolved; - - Injector.prototype.get = function(dep, stack) { - stack = [dep].concat(stack || []); - if (stack && stack.lastIndexOf(dep) != 0) { - return this.rejected("Cyclic dependency: " + stack.join(" <- ")); - } - for (var i = 0, l = this.plugins.length; i < l; ++i) { - var value = this.plugins[i].get(this, dep, stack); - if (value) { - if (!value.destroy) - value.destroy = function(){return Injector.resolved(true);}; - return value; - } - } - return this.rejected("Unknown dependency: " + stack.join(" <- ")); - }; - - Injector.prototype.register = function(name, value) { - for (var i = 0, l = this.plugins.length; i < l; ++i) { - if (this.plugins[i].register(this, name, value)) - return this; - } - throw this.error("No plugin handled registration of: " + name); - return this; - }; - - Injector.prototype.invoke = function(spec) { - var injector = this; - var parsed = this.parseSpec(spec); - var fn = parsed[0]; - var dependencies = parsed[1]; - var depPromises = dependencies.map(function(dep) { - return injector.get(dep, []); - }); - var depPromise = this.waitForAll(depPromises); - var result = depPromise.then(function(results) { - return fn.apply(injector, results); - }); - result.destroy = function() { - return Injector.waitForAll(depPromises.map(function(promise) { - return promise.destroy(); - })).then(function() { - return true; - }); - }; - return result; - }; - - - - /* static utility functions */ - - Injector.error = function(message, cause) {return new InjectorError(message, cause);}; - Injector.prototype.error = Injector.error; - - Injector.defer = function() {return new Deferred();}; - Injector.prototype.defer = Injector.defer; - - Injector.parseSpec = DepsParser.spec; - Injector.prototype.parseSpec = DepsParser.spec; - - Injector.waitForAll = Deferred.waitForAll; - Injector.prototype.waitForAll = Deferred.waitForAll; - - /* the injector error type */ - - var InjectorError = function(message, cause) { - this.name = "InjectorError"; - this.message = message; - this.cause = cause; - }; - InjectorError.prototype = new Error(); - InjectorError.prototype.constructor = InjectorError; - InjectorError.prototype.toString = function() { - return "InjectorError: " + this.message + (this.cause ? " [caused by " + this.cause + "]" : ""); - }; - - return Injector; - - })(Deferred, DepsParser); - - Injector.prefixPlugin = function(prefix, plugin) { - return { - register: function(injector, name, spec) { - if (name.indexOf(prefix) == 0) { - return plugin.register(injector, - name.substr(prefix.length), - spec); - } else { - return false; - } - }, - get: function(injector, name, stack) { - if (name.indexOf(prefix) == 0) { - return plugin.get(injector, - name.substr(prefix.length), - stack); - } else { - return null; - } - } - }; - }; - - Injector.DOMPlugin = (function() { - var DOMPlugin = function() { - this.aliases = {}; - }; - - DOMPlugin.prototype.register = function(injector, name, spec) { - this.aliases[name] = spec; - return true; - }; - - DOMPlugin.prototype.get = function(injector, name, stack) { - var deferred = injector.defer(); - var interval = setInterval(function() { - var obj = $(this.aliases[name] || name); - if (obj.length) { - clearInterval(interval); - deferred.resolve(obj); - } - }.bind(this), 100); - return deferred.promise; - }; - - return DOMPlugin; - })(); - - Injector.HTTPPlugin = (function() { - var HTTPPlugin = function() { - this.aliases = {}; - }; - - HTTPPlugin.prototype.register = function(injector, name, spec) { - this.aliases[name] = spec; - return true; - }; - - HTTPPlugin.prototype.get = function(injector, name, stack) { - var deferred = injector.defer(); - $.ajax(this.aliases[name] || name).then(function(result) { - deferred.resolve(result); - // deferred.resolve.bind(deferred); - }, deferred.reject.bind(deferred)); - deferred.promise.destroy = function() { - return Injector.resolved(true); - }; - return deferred.promise; - }; - - return HTTPPlugin; - })(); - - Injector.ValuePlugin = (function() { - var ValuePlugin = function() { - this.specs = {}; - this.values = {}; - }; - - ValuePlugin.prototype.register = function(injector, name, spec) { - this.specs[name] = spec; - return true; - }; - - ValuePlugin.prototype.get = function(injector, name, stack) { - if (name in this.values) { - this.values[name].references++; - return this.values[name]; - } else if (name in this.specs) { - var spec = this.specs[name]; - var parsed = injector.parseSpec(spec); - var constructor = parsed[0]; - var dependencies = parsed[1]; - var depPromises = dependencies.map(function(dep) { - return injector.get(dep, [name].concat(stack)); - }); - var depPromise = injector.waitForAll(depPromises); - - var onDestroy = null; - var readRequestor = false; - var deferred = injector.defer(); - var result = deferred.promise; - depPromise.then(function(results) { - var wrappedInjector = Object.create(injector); - wrappedInjector.requestor = function() { - readRequestor = true; - return stack[1]; - }; - try { - deferred.resolve(constructor.apply(wrappedInjector, results)); - } catch (e) { - deferred.reject(e); - } finally { - onDestroy = wrappedInjector.onDestroy; - if (result.references <= 0 && onDestroy) - onDestroy(); - } - }, function(e) { - deferred.reject(injector.error("Error constructing " + name)); - }); - - result.references = 1; - var values = this.values; - result.destroy = function() { - this.references--; - if (this.references <= 0) { - if (onDestroy) - onDestroy(); - delete values[name]; - if (!result.resolved && !result.rejected) - deferred.reject(injector.error("Promise destroyed before value completed construction")); - return injector.waitForAll(depPromises.map(function(promise) { - return promise.destroy(); - })).then(function() { - return true; - }); - } else { - return injector.resolved(true); - } - }; - if (readRequestor) { - return result; - } else { - return this.values[name] = result; - } - } else { - return null; - } - }; - - return ValuePlugin; - })(); - - - return Injector; -})(); - -if (typeof(module) !== "undefined") - module.exports = Injector; |