summaryrefslogtreecommitdiff
path: root/injector.js
diff options
context:
space:
mode:
Diffstat (limited to 'injector.js')
-rw-r--r--injector.js426
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;