From fab0b505c608d2a553c1ca74553e6a433e453a5d Mon Sep 17 00:00:00 2001
From: Carlo Zancanaro <carlo@zancanaro.id.au>
Date: Mon, 29 Sep 2014 01:32:38 +1000
Subject: Rename to Scorpion: sounds cooler

---
 Gruntfile.js      |  10 +-
 bower.json        |   2 +-
 injector-tests.js | 359 ---------------------------------------------
 injector.js       | 426 ------------------------------------------------------
 injector.min.js   |   1 -
 package.json      |   2 +-
 scorpion-tests.js | 359 +++++++++++++++++++++++++++++++++++++++++++++
 scorpion.js       | 426 ++++++++++++++++++++++++++++++++++++++++++++++++++++++
 scorpion.min.js   |   1 +
 9 files changed, 793 insertions(+), 793 deletions(-)
 delete mode 100644 injector-tests.js
 delete mode 100644 injector.js
 delete mode 100644 injector.min.js
 create mode 100644 scorpion-tests.js
 create mode 100644 scorpion.js
 create mode 100644 scorpion.min.js

diff --git a/Gruntfile.js b/Gruntfile.js
index e2febff..4710066 100644
--- a/Gruntfile.js
+++ b/Gruntfile.js
@@ -32,8 +32,8 @@ module.exports = function (grunt) {
             },
             test: {
                 files: [
-                    "injector.js",
-                    "injector-tests.js"
+                    "scorpion.js",
+                    "scorpion-tests.js"
                 ],
                 tasks: ['test']
             }
@@ -45,14 +45,14 @@ module.exports = function (grunt) {
                     reporter: 'min',
                     require: [
                         function(){
-                            Injector = require('./injector');
+                            Scorpion = require('./scorpion');
                             expect = require('chai').expect;
                         }
                     ]
                 },
                 src: [
-                    "injector.js",
-                    "injector-tests.js"
+                    "scorpion.js",
+                    "scorpion-tests.js"
                 ]
             }
         }
diff --git a/bower.json b/bower.json
index 37f58d6..7ad5f8c 100644
--- a/bower.json
+++ b/bower.json
@@ -1,5 +1,5 @@
 {
-    "name": "injector",
+    "name": "scorpion",
     "version": "0.1.0"
 }
 
diff --git a/injector-tests.js b/injector-tests.js
deleted file mode 100644
index e94126f..0000000
--- a/injector-tests.js
+++ /dev/null
@@ -1,359 +0,0 @@
-/*global describe,it,expect,beforeEach,Injector, setTimeout*/
-
-describe("deferred", function() {
-    var deferred, promise;
-    beforeEach(function() {
-        deferred = Injector.defer();
-        promise = deferred.promise;
-    });
-    this.timeout(100);
-
-    describe("promise", function() {
-        it("should have a 'then' method", function() {
-            expect(promise.then).not.to.equal(undefined);
-        });
-
-        it("should be able to chain 'then' methods", function() {
-            var promise1 = promise;
-            var promise2 = promise1.then(function(x) {
-                return x + 1;
-            });
-            var promise3 = promise1.then(function(x) {
-                return x + 1;
-            });
-            expect(promise1.then).not.to.equal(undefined);
-            expect(promise2.then).not.to.equal(undefined);
-            expect(promise3.then).not.to.equal(undefined);
-        });
-    });
-
-    describe("resolution", function() {
-
-        it("should resolve asynchronously", function(done) {
-            var hasRun = false;
-            promise.then(function(val) {
-                hasRun = true;
-                return val;
-            });
-            deferred.resolve("value");
-            expect(hasRun).to.equal(false);
-            setTimeout(function() {
-                expect(hasRun).to.equal(true);
-                done();
-            });
-        });
-
-        it("should propagate through multiple promises", function(done) {
-            var promise2 = promise.then(function(value) {
-                return value + 1;
-            });
-            var promise3 = promise2.then(function(value) {
-                return value + 1;
-            });
-            promise3.then(function(value) {
-                expect(value).to.equal(3);
-                done();
-            });
-            deferred.resolve(1);
-        });
-
-        it("should call all registered handlers", function(done) {
-            var called = 0;
-            promise.then(function() {called++;});
-            promise.then(function() {called++;});
-            promise.then(function() {called++;});
-            expect(called).to.equal(0);
-            deferred.resolve(null);
-            setTimeout(function() {
-                expect(called).to.equal(3);
-                done();
-            });
-        });
-
-        it("should call handlers, even when added after resolution", function(done) {
-            deferred.resolve(null);
-            promise.then(function() {
-                done();
-            });
-        });
-
-        it("should not allow multiple resolution", function() {
-            deferred.resolve(1);
-            expect(function() {
-                deferred.resolve(2);
-            }).to.throw();
-        });
-
-        it("should not allow rejecting after resolving", function() {
-            deferred.resolve(1);
-            expect(function() {
-                deferred.reject(2);
-            }).to.throw();
-        });
-
-        it("should handle a returned promise by 'unwrapping' it", function(done) {
-            promise.then(function(value) {
-                var deferred = Injector.defer();
-                deferred.resolve(value + 1);
-                return deferred.promise;
-            }).then(function(value) {
-                try {
-                    expect(value).to.equal(2);
-                    done();
-                } catch (e) {
-                    done(e);
-                }
-            });
-            deferred.resolve(1);
-        });
-    });
-
-    describe("rejection", function() {
-        it("should reject asynchronously", function(done) {
-            var hasRun = false;
-            promise.then(null, function(val) {
-                hasRun = true;
-                return val;
-            });
-            deferred.reject("value");
-            expect(hasRun).to.equal(false);
-            setTimeout(function() {
-                expect(hasRun).to.equal(true);
-                done();
-            });
-        });
-
-
-        it("should be turned into resolution by a handler", function(done) {
-            var promise2 = promise.then(function(value) {
-                return value + 1;
-            }, function(value) {
-                return value + 100;
-            });
-            promise2.then(function(value) {
-                try {
-                    expect(value).to.equal(101);
-                    done();
-                } catch (e)  {
-                    done(e);
-                }
-            });
-            deferred.reject(1);
-        });
-
-        it("should call all registered handlers", function(done) {
-            var called = 0;
-            promise.then(null, function() {called++;});
-            promise.then(null, function() {called++;});
-            promise.then(null, function() {called++;});
-            expect(called).to.equal(0);
-            deferred.reject(null);
-            setTimeout(function() {
-                expect(called).to.equal(3);
-                done();
-            });
-        });
-
-        it("should not allow multiple rejection", function() {
-            deferred.reject(1);
-            expect(function() {
-                deferred.reject(2);
-            }).to.throw();
-        });
-
-        it("should not allow resolving after rejecting", function() {
-            deferred.reject(1);
-            expect(function() {
-                deferred.resolve(2);
-            }).to.throw();
-        });
-
-        it("should call handlers, even when added after resolution", function(done) {
-            deferred.reject(null);
-            promise.then(null, function() {
-                done();
-            });
-        });
-
-        it("should handle a returned promise by 'unwrapping' in the error case", function(done) {
-            promise.then(function(value) {
-                var deferred = Injector.defer();
-                deferred.reject(value + 1);
-                return deferred.promise;
-            }).then(null, function(value) {
-                try {
-                    expect(value).to.equal(2);
-                    done();
-                } catch (e) {
-                    done(e);
-                }
-            });
-            deferred.resolve(1);
-        });
-    });
-
-    describe("waitForAll", function() {
-        it("should resolve with an array if all successful", function(done) {
-            var d1 = Injector.defer(),
-                d2 = Injector.defer(),
-                d3 = Injector.defer();
-            Injector
-                .waitForAll([d1.promise, d2.promise, d3.promise])
-                .then(function(values) {
-                    if (values.length == 3 &&
-                        values[0] === 0 &&
-                        values[1] === 1 &&
-                        values[2] === 2) {
-                        done();
-                    } else {
-                        done(new Error("Error in resolved result: " + values));
-                    }
-                }, done);
-            d1.resolve(0);
-            d2.resolve(1);
-            d3.resolve(2);
-        });
-
-        it("should reject with an object of successes/errors if any fail", function(done) {
-            var d1 = Injector.defer(),
-                d2 = Injector.defer(),
-                d3 = Injector.defer();
-            Injector
-                .waitForAll([d1.promise, d2.promise, d3.promise])
-                .then(function(values) {
-                    done(new Error("incorrectly resolved promise"));
-                }, function(e) {
-                    if (e.errors && e.values &&
-                        e.values[0] === 0 &&
-                        e.values[1] === 1 &&
-                        e.errors[2] === 2) {
-                        done();
-                    } else {
-                        done(new Error("incorrect reject value"));
-                    }
-                });
-            d1.resolve(0);
-            d2.resolve(1);
-            d3.reject(2);
-        });
-    });
-});
-
-describe("injector", function() {
-    var injector, valuePlugin;
-    beforeEach(function() {
-        valuePlugin = new Injector.ValuePlugin();
-        injector = new Injector([valuePlugin]);
-    });
-    this.timeout(100);
-
-    describe("value registration and retrieval", function() {
-        it("works", function(done) {
-            injector.register("a", function(){return "the value of a";});
-            injector.get("a").then(function(value){
-                if (value == "the value of a")
-                    done();
-                else
-                    done(new Error("incorrect value for a (" + value + ")"));
-            }, done);
-        });
-
-        it("with dependencies works", function(done) {
-            injector
-                .register("a", function(){return "the value of a";})
-                .register("b", ["a", function(a){return a.replace(/a/g, "b");}]);
-            injector.get("b").then(function(value){
-                if (value == "the vblue of b")
-                    done();
-                else
-                    done(new Error("incorrect value for b (" + value + ")"));
-            }, done);
-        });
-    });
-
-    describe("destroy handlers", function() {
-        it("work with single gets", function(done) {
-            injector.register("a", function(){return "a";});
-            expect("a" in valuePlugin.values).to.equal(false);
-            var result = injector.get("a");
-            result.then(function() {
-                if ("a" in valuePlugin.values) {
-                    result.destroy().then(function() {
-                        if ("a" in valuePlugin.values) {
-                            done(new Error("Value found for a after destruction"));
-                        } else {
-                            done();
-                        }
-                    });
-                } else {
-                    done(new Error("No value found for a"));
-                }
-            }, done);
-        });
-
-        it("works with multiple gets", function(done) {
-            injector.register("a", function(){return "a";});
-            expect("a" in valuePlugin.values).to.equal(false);
-            var result = [injector.get("a"), injector.get("a"), injector.get("a")];
-            result[1].destroy();
-            result[2].destroy();
-            result[0].then(function() {
-                if ("a" in valuePlugin.values) {
-                    result[0].destroy().then(function() {
-                        if ("a" in valuePlugin.values) {
-                            done(new Error("Value found for a after destruction"));
-                        } else {
-                            done();
-                        }
-                    });
-                } else {
-                    done(new Error("No value found for a"));
-                }
-            }, done);
-        });
-
-        it("works through dependencies", function(done) {
-            injector
-                .register("a", function(){return "a";})
-                .register("b", ["a", function(a){return a + "b";}]);
-            expect("a" in valuePlugin.values).to.equal(false);
-            var result = injector.get("b");
-            result.then(function() {
-                if ("a" in valuePlugin.values) {
-                    result.destroy().then(function() {
-                        if ("a" in valuePlugin.values) {
-                            done(new Error("Value found for a after destruction"));
-                        } else {
-                            done();
-                        }
-                    });
-                } else {
-                    done(new Error("No value found for a"));
-                }
-            }, done);
-        });
-    });
-
-    describe("cyclic dependency detection", function() {
-        it("detects simple cycles", function(done) {
-            injector.register("a", ["b", function(b) {return b;}]);
-            injector.register("b", ["a", function(a) {return a;}]); 
-            injector.get("a").then(function() {
-                done(new Error("Promise should have been rejected"));
-            }, function(e) {
-                done();
-            });
-        });
-
-        it("detects cycles with intermediate nodes", function(done) {
-            injector.register("a", ["c", function(c) {return c;}]);
-            injector.register("b", ["a", function(a) {return a;}]);
-            injector.register("c", ["b", function(b) {return b;}]);
-            injector.get("a").then(function() {
-                done(new Error("Promise should have been rejected"));
-            }, function(e) {
-                done();
-            });
-        });
-    });
-});
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;
diff --git a/injector.min.js b/injector.min.js
deleted file mode 100644
index 2f09e0f..0000000
--- a/injector.min.js
+++ /dev/null
@@ -1 +0,0 @@
-/*! injector 0.1.0 */var Injector=function(){var a=function(){var a=function(a,b){setTimeout(function(){a(b)})},b=function(a,b){return function(c){try{var d=a(c);d&&d.then?d.then(function(a){b.resolve(a)},function(a){b.reject(a)}):b.resolve(d)}catch(e){b.reject(e)}}},c=function(){this.resolved=!1,this.rejected=!1,this.onSuccess=[],this.onError=[]};c.prototype.then=function(c,e){c=c||function(a){return a},e=e||function(a){throw a};var f=new d,g=b(c,f),h=b(e,f);return this.resolved?a(g,this.value):this.rejected?a(h,this.value):(null!=this.onSuccess&&this.onSuccess.push(g),null!=this.onError&&this.onError.push(h)),f.promise};var d=function(){this.promise=new c};return d.prototype.resolve=function(a){if(this.promise.resolved)throw new Error("Cannot re-resolve already resolved promise");if(this.promise.rejected)throw new Error("Cannot resolve a rejected promise");this.promise.resolved=!0,this.promise.value=a;var b=this.promise.onSuccess;this.promise.onSuccess=null,this.promise.onError=null,setTimeout(function(){b.forEach(function(b){b(a)})})},d.prototype.reject=function(a){if(this.promise.resolved)throw new Error("Cannot reject an already resolved promise");if(this.promise.rejected)throw new Error("Cannot re-reject a rejected promise");this.promise.rejected=!0,this.promise.value=a;var b=this.promise.onError;this.promise.onSuccess=null,this.promise.onError=null,setTimeout(function(){b.forEach(function(b){b(a)})})},d.waitForAll=function(a){var b=new d,c=0,e=0,f={},g={},h=a.length;a.forEach(function(a,b){a.then(function(a){g[b]=a,c++,i()},function(a){f[b]=a,e++,i()})});var i=function(){if(c==h){for(var a=[],d=0,i=h;i>d;++d)a.push(g[d]);b.resolve(a)}else c+e==h&&b.reject({errors:f,values:g})};return i(),b.promise},d}(),b=function(){var a=/^function[^(]*\(([^)]*)\)/,b=function(b){var c=a.exec(b.toString().replace(/\s+/g,""));if(null==c)throw new Error("Unable to parse fn definition");return c[1]?c[1].split(/,/):[]},c=function(a){var c,d;return"function"==typeof a||a instanceof Function?(d=b(a),c=a):(c=a[a.length-1],d=a.slice(0,a.length-1)),[c,d]},d=function(a){var b=a.split("!");if(0==b.length)throw new Error("Invalid dependency: "+a);return 1==b.length?{prefix:"",name:b[0]}:{prefix:b[0],name:b.slice(1).join("!")}};return{spec:c,dep:d}}(),c=function(a,b){var c=function(a){this.plugins=a};c.rejected=function(a,b){var c=this.defer();return c.reject(this.error(a,b)),c.promise},c.prototype.rejected=c.rejected,c.resolved=function(a){var b=this.defer();return b.resolve(a),b.promise},c.prototype.resolved=c.resolved,c.prototype.get=function(a,b){if(b=[a].concat(b||[]),b&&0!=b.lastIndexOf(a))return this.rejected("Cyclic dependency: "+b.join(" <- "));for(var d=0,e=this.plugins.length;e>d;++d){var f=this.plugins[d].get(this,a,b);if(f)return f.destroy||(f.destroy=function(){return c.resolved(!0)}),f}return this.rejected("Unknown dependency: "+b.join(" <- "))},c.prototype.register=function(a,b){for(var c=0,d=this.plugins.length;d>c;++c)if(this.plugins[c].register(this,a,b))return this;throw this.error("No plugin handled registration of: "+a)},c.prototype.invoke=function(a){var b=this,d=this.parseSpec(a),e=d[0],f=d[1],g=f.map(function(a){return b.get(a,[])}),h=this.waitForAll(g),i=h.then(function(a){return e.apply(b,a)});return i.destroy=function(){return c.waitForAll(g.map(function(a){return a.destroy()})).then(function(){return!0})},i},c.error=function(a,b){return new d(a,b)},c.prototype.error=c.error,c.defer=function(){return new a},c.prototype.defer=c.defer,c.parseSpec=b.spec,c.prototype.parseSpec=b.spec,c.waitForAll=a.waitForAll,c.prototype.waitForAll=a.waitForAll;var d=function(a,b){this.name="InjectorError",this.message=a,this.cause=b};return d.prototype=new Error,d.prototype.constructor=d,d.prototype.toString=function(){return"InjectorError: "+this.message+(this.cause?" [caused by "+this.cause+"]":"")},c}(a,b);return c.prefixPlugin=function(a,b){return{register:function(c,d,e){return 0==d.indexOf(a)?b.register(c,d.substr(a.length),e):!1},get:function(c,d,e){return 0==d.indexOf(a)?b.get(c,d.substr(a.length),e):null}}},c.DOMPlugin=function(){var a=function(){this.aliases={}};return a.prototype.register=function(a,b,c){return this.aliases[b]=c,!0},a.prototype.get=function(a,b){var c=a.defer(),d=setInterval(function(){var a=$(this.aliases[b]||b);a.length&&(clearInterval(d),c.resolve(a))}.bind(this),100);return c.promise},a}(),c.HTTPPlugin=function(){var a=function(){this.aliases={}};return a.prototype.register=function(a,b,c){return this.aliases[b]=c,!0},a.prototype.get=function(a,b){var d=a.defer();return $.ajax(this.aliases[b]||b).then(function(a){d.resolve(a)},d.reject.bind(d)),d.promise.destroy=function(){return c.resolved(!0)},d.promise},a}(),c.ValuePlugin=function(){var a=function(){this.specs={},this.values={}};return a.prototype.register=function(a,b,c){return this.specs[b]=c,!0},a.prototype.get=function(a,b,c){if(b in this.values)return this.values[b].references++,this.values[b];if(b in this.specs){var d=this.specs[b],e=a.parseSpec(d),f=e[0],g=e[1],h=g.map(function(d){return a.get(d,[b].concat(c))}),i=a.waitForAll(h),j=null,k=!1,l=a.defer(),m=l.promise;i.then(function(b){var d=Object.create(a);d.requestor=function(){return k=!0,c[1]};try{l.resolve(f.apply(d,b))}catch(e){l.reject(e)}finally{j=d.onDestroy,m.references<=0&&j&&j()}},function(){l.reject(a.error("Error constructing "+b))}),m.references=1;var n=this.values;return m.destroy=function(){return this.references--,this.references<=0?(j&&j(),delete n[b],m.resolved||m.rejected||l.reject(a.error("Promise destroyed before value completed construction")),a.waitForAll(h.map(function(a){return a.destroy()})).then(function(){return!0})):a.resolved(!0)},k?m:this.values[b]=m}return null},a}(),c}();"undefined"!=typeof module&&(module.exports=Injector);
\ No newline at end of file
diff --git a/package.json b/package.json
index 2ff7b5b..f8e06b2 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
 {
-  "name": "injector",
+  "name": "scorpion",
   "version": "0.1.0",
   "dependencies": {},
   "devDependencies": {
diff --git a/scorpion-tests.js b/scorpion-tests.js
new file mode 100644
index 0000000..0bbe721
--- /dev/null
+++ b/scorpion-tests.js
@@ -0,0 +1,359 @@
+/*global describe,it,expect,beforeEach,Scorpion, setTimeout*/
+
+describe("deferred", function() {
+    var deferred, promise;
+    beforeEach(function() {
+        deferred = Scorpion.defer();
+        promise = deferred.promise;
+    });
+    this.timeout(100);
+
+    describe("promise", function() {
+        it("should have a 'then' method", function() {
+            expect(promise.then).not.to.equal(undefined);
+        });
+
+        it("should be able to chain 'then' methods", function() {
+            var promise1 = promise;
+            var promise2 = promise1.then(function(x) {
+                return x + 1;
+            });
+            var promise3 = promise1.then(function(x) {
+                return x + 1;
+            });
+            expect(promise1.then).not.to.equal(undefined);
+            expect(promise2.then).not.to.equal(undefined);
+            expect(promise3.then).not.to.equal(undefined);
+        });
+    });
+
+    describe("resolution", function() {
+
+        it("should resolve asynchronously", function(done) {
+            var hasRun = false;
+            promise.then(function(val) {
+                hasRun = true;
+                return val;
+            });
+            deferred.resolve("value");
+            expect(hasRun).to.equal(false);
+            setTimeout(function() {
+                expect(hasRun).to.equal(true);
+                done();
+            });
+        });
+
+        it("should propagate through multiple promises", function(done) {
+            var promise2 = promise.then(function(value) {
+                return value + 1;
+            });
+            var promise3 = promise2.then(function(value) {
+                return value + 1;
+            });
+            promise3.then(function(value) {
+                expect(value).to.equal(3);
+                done();
+            });
+            deferred.resolve(1);
+        });
+
+        it("should call all registered handlers", function(done) {
+            var called = 0;
+            promise.then(function() {called++;});
+            promise.then(function() {called++;});
+            promise.then(function() {called++;});
+            expect(called).to.equal(0);
+            deferred.resolve(null);
+            setTimeout(function() {
+                expect(called).to.equal(3);
+                done();
+            });
+        });
+
+        it("should call handlers, even when added after resolution", function(done) {
+            deferred.resolve(null);
+            promise.then(function() {
+                done();
+            });
+        });
+
+        it("should not allow multiple resolution", function() {
+            deferred.resolve(1);
+            expect(function() {
+                deferred.resolve(2);
+            }).to.throw();
+        });
+
+        it("should not allow rejecting after resolving", function() {
+            deferred.resolve(1);
+            expect(function() {
+                deferred.reject(2);
+            }).to.throw();
+        });
+
+        it("should handle a returned promise by 'unwrapping' it", function(done) {
+            promise.then(function(value) {
+                var deferred = Scorpion.defer();
+                deferred.resolve(value + 1);
+                return deferred.promise;
+            }).then(function(value) {
+                try {
+                    expect(value).to.equal(2);
+                    done();
+                } catch (e) {
+                    done(e);
+                }
+            });
+            deferred.resolve(1);
+        });
+    });
+
+    describe("rejection", function() {
+        it("should reject asynchronously", function(done) {
+            var hasRun = false;
+            promise.then(null, function(val) {
+                hasRun = true;
+                return val;
+            });
+            deferred.reject("value");
+            expect(hasRun).to.equal(false);
+            setTimeout(function() {
+                expect(hasRun).to.equal(true);
+                done();
+            });
+        });
+
+
+        it("should be turned into resolution by a handler", function(done) {
+            var promise2 = promise.then(function(value) {
+                return value + 1;
+            }, function(value) {
+                return value + 100;
+            });
+            promise2.then(function(value) {
+                try {
+                    expect(value).to.equal(101);
+                    done();
+                } catch (e)  {
+                    done(e);
+                }
+            });
+            deferred.reject(1);
+        });
+
+        it("should call all registered handlers", function(done) {
+            var called = 0;
+            promise.then(null, function() {called++;});
+            promise.then(null, function() {called++;});
+            promise.then(null, function() {called++;});
+            expect(called).to.equal(0);
+            deferred.reject(null);
+            setTimeout(function() {
+                expect(called).to.equal(3);
+                done();
+            });
+        });
+
+        it("should not allow multiple rejection", function() {
+            deferred.reject(1);
+            expect(function() {
+                deferred.reject(2);
+            }).to.throw();
+        });
+
+        it("should not allow resolving after rejecting", function() {
+            deferred.reject(1);
+            expect(function() {
+                deferred.resolve(2);
+            }).to.throw();
+        });
+
+        it("should call handlers, even when added after resolution", function(done) {
+            deferred.reject(null);
+            promise.then(null, function() {
+                done();
+            });
+        });
+
+        it("should handle a returned promise by 'unwrapping' in the error case", function(done) {
+            promise.then(function(value) {
+                var deferred = Scorpion.defer();
+                deferred.reject(value + 1);
+                return deferred.promise;
+            }).then(null, function(value) {
+                try {
+                    expect(value).to.equal(2);
+                    done();
+                } catch (e) {
+                    done(e);
+                }
+            });
+            deferred.resolve(1);
+        });
+    });
+
+    describe("waitForAll", function() {
+        it("should resolve with an array if all successful", function(done) {
+            var d1 = Scorpion.defer(),
+                d2 = Scorpion.defer(),
+                d3 = Scorpion.defer();
+            Scorpion
+                .waitForAll([d1.promise, d2.promise, d3.promise])
+                .then(function(values) {
+                    if (values.length == 3 &&
+                        values[0] === 0 &&
+                        values[1] === 1 &&
+                        values[2] === 2) {
+                        done();
+                    } else {
+                        done(new Error("Error in resolved result: " + values));
+                    }
+                }, done);
+            d1.resolve(0);
+            d2.resolve(1);
+            d3.resolve(2);
+        });
+
+        it("should reject with an object of successes/errors if any fail", function(done) {
+            var d1 = Scorpion.defer(),
+                d2 = Scorpion.defer(),
+                d3 = Scorpion.defer();
+            Scorpion
+                .waitForAll([d1.promise, d2.promise, d3.promise])
+                .then(function(values) {
+                    done(new Error("incorrectly resolved promise"));
+                }, function(e) {
+                    if (e.errors && e.values &&
+                        e.values[0] === 0 &&
+                        e.values[1] === 1 &&
+                        e.errors[2] === 2) {
+                        done();
+                    } else {
+                        done(new Error("incorrect reject value"));
+                    }
+                });
+            d1.resolve(0);
+            d2.resolve(1);
+            d3.reject(2);
+        });
+    });
+});
+
+describe("injector", function() {
+    var injector, valuePlugin;
+    beforeEach(function() {
+        valuePlugin = new Scorpion.ValuePlugin();
+        injector = new Scorpion([valuePlugin]);
+    });
+    this.timeout(100);
+
+    describe("value registration and retrieval", function() {
+        it("works", function(done) {
+            injector.register("a", function(){return "the value of a";});
+            injector.get("a").then(function(value){
+                if (value == "the value of a")
+                    done();
+                else
+                    done(new Error("incorrect value for a (" + value + ")"));
+            }, done);
+        });
+
+        it("with dependencies works", function(done) {
+            injector
+                .register("a", function(){return "the value of a";})
+                .register("b", ["a", function(a){return a.replace(/a/g, "b");}]);
+            injector.get("b").then(function(value){
+                if (value == "the vblue of b")
+                    done();
+                else
+                    done(new Error("incorrect value for b (" + value + ")"));
+            }, done);
+        });
+    });
+
+    describe("destroy handlers", function() {
+        it("work with single gets", function(done) {
+            injector.register("a", function(){return "a";});
+            expect("a" in valuePlugin.values).to.equal(false);
+            var result = injector.get("a");
+            result.then(function() {
+                if ("a" in valuePlugin.values) {
+                    result.destroy().then(function() {
+                        if ("a" in valuePlugin.values) {
+                            done(new Error("Value found for a after destruction"));
+                        } else {
+                            done();
+                        }
+                    });
+                } else {
+                    done(new Error("No value found for a"));
+                }
+            }, done);
+        });
+
+        it("works with multiple gets", function(done) {
+            injector.register("a", function(){return "a";});
+            expect("a" in valuePlugin.values).to.equal(false);
+            var result = [injector.get("a"), injector.get("a"), injector.get("a")];
+            result[1].destroy();
+            result[2].destroy();
+            result[0].then(function() {
+                if ("a" in valuePlugin.values) {
+                    result[0].destroy().then(function() {
+                        if ("a" in valuePlugin.values) {
+                            done(new Error("Value found for a after destruction"));
+                        } else {
+                            done();
+                        }
+                    });
+                } else {
+                    done(new Error("No value found for a"));
+                }
+            }, done);
+        });
+
+        it("works through dependencies", function(done) {
+            injector
+                .register("a", function(){return "a";})
+                .register("b", ["a", function(a){return a + "b";}]);
+            expect("a" in valuePlugin.values).to.equal(false);
+            var result = injector.get("b");
+            result.then(function() {
+                if ("a" in valuePlugin.values) {
+                    result.destroy().then(function() {
+                        if ("a" in valuePlugin.values) {
+                            done(new Error("Value found for a after destruction"));
+                        } else {
+                            done();
+                        }
+                    });
+                } else {
+                    done(new Error("No value found for a"));
+                }
+            }, done);
+        });
+    });
+
+    describe("cyclic dependency detection", function() {
+        it("detects simple cycles", function(done) {
+            injector.register("a", ["b", function(b) {return b;}]);
+            injector.register("b", ["a", function(a) {return a;}]);
+            injector.get("a").then(function() {
+                done(new Error("Promise should have been rejected"));
+            }, function(e) {
+                done();
+            });
+        });
+
+        it("detects cycles with intermediate nodes", function(done) {
+            injector.register("a", ["c", function(c) {return c;}]);
+            injector.register("b", ["a", function(a) {return a;}]);
+            injector.register("c", ["b", function(b) {return b;}]);
+            injector.get("a").then(function() {
+                done(new Error("Promise should have been rejected"));
+            }, function(e) {
+                done();
+            });
+        });
+    });
+});
diff --git a/scorpion.js b/scorpion.js
new file mode 100644
index 0000000..6d7051c
--- /dev/null
+++ b/scorpion.js
@@ -0,0 +1,426 @@
+/*global module, setTimeout*/
+
+var Scorpion = (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 Scorpion = (function(Deferred, DepsParser) {
+
+        var Scorpion = function(plugins) {
+            // plugins are a list to be executed in order
+            this.plugins = plugins;
+        };
+
+        Scorpion.rejected = function(message, cause) {
+            var deferred = this.defer();
+            deferred.reject(this.error(message, cause));
+            return deferred.promise;
+        };
+        Scorpion.prototype.rejected = Scorpion.rejected;
+
+        Scorpion.resolved = function(value) {
+            var deferred = this.defer();
+            deferred.resolve(value);
+            return deferred.promise;
+        };
+        Scorpion.prototype.resolved = Scorpion.resolved;
+
+        Scorpion.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 Scorpion.resolved(true);};
+                    return value;
+                }
+            }
+            return this.rejected("Unknown dependency: " + stack.join(" <- "));
+        };
+
+        Scorpion.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;
+        };
+
+        Scorpion.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 Scorpion.waitForAll(depPromises.map(function(promise) {
+                    return promise.destroy();
+                })).then(function() {
+                    return true;
+                });
+            };
+            return result;
+        };
+
+
+
+        /* static utility functions */
+
+        Scorpion.error = function(message, cause) {return new ScorpionError(message, cause);};
+        Scorpion.prototype.error = Scorpion.error;
+
+        Scorpion.defer = function() {return new Deferred();};
+        Scorpion.prototype.defer = Scorpion.defer;
+
+        Scorpion.parseSpec = DepsParser.spec;
+        Scorpion.prototype.parseSpec = DepsParser.spec;
+
+        Scorpion.waitForAll = Deferred.waitForAll;
+        Scorpion.prototype.waitForAll = Deferred.waitForAll;
+
+        /* the injector error type */
+
+        var ScorpionError = function(message, cause) {
+            this.name = "ScorpionError";
+            this.message = message;
+            this.cause = cause;
+        };
+        ScorpionError.prototype = new Error();
+        ScorpionError.prototype.constructor = ScorpionError;
+        ScorpionError.prototype.toString = function() {
+            return "ScorpionError: " + this.message + (this.cause ? " [caused by " + this.cause + "]" : "");
+        };
+
+        return Scorpion;
+
+    })(Deferred, DepsParser);
+
+    Scorpion.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;
+                }
+            }
+        };
+    };
+
+    Scorpion.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;
+    })();
+
+    Scorpion.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 Scorpion.resolved(true);
+            };
+            return deferred.promise;
+        };
+
+        return HTTPPlugin;
+    })();
+
+    Scorpion.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 wrappedScorpion = Object.create(injector);
+                    wrappedScorpion.requestor = function() {
+                        readRequestor = true;
+                        return stack[1];
+                    };
+                    try {
+                        deferred.resolve(constructor.apply(wrappedScorpion, results));
+                    } catch (e) {
+                        deferred.reject(e);
+                    } finally {
+                        onDestroy = wrappedScorpion.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 Scorpion;
+})();
+
+if (typeof(module) !== "undefined")
+    module.exports = Scorpion;
diff --git a/scorpion.min.js b/scorpion.min.js
new file mode 100644
index 0000000..5524044
--- /dev/null
+++ b/scorpion.min.js
@@ -0,0 +1 @@
+/*! scorpion 0.1.0 */var Scorpion=function(){var a=function(){var a=function(a,b){setTimeout(function(){a(b)})},b=function(a,b){return function(c){try{var d=a(c);d&&d.then?d.then(function(a){b.resolve(a)},function(a){b.reject(a)}):b.resolve(d)}catch(e){b.reject(e)}}},c=function(){this.resolved=!1,this.rejected=!1,this.onSuccess=[],this.onError=[]};c.prototype.then=function(c,e){c=c||function(a){return a},e=e||function(a){throw a};var f=new d,g=b(c,f),h=b(e,f);return this.resolved?a(g,this.value):this.rejected?a(h,this.value):(null!=this.onSuccess&&this.onSuccess.push(g),null!=this.onError&&this.onError.push(h)),f.promise};var d=function(){this.promise=new c};return d.prototype.resolve=function(a){if(this.promise.resolved)throw new Error("Cannot re-resolve already resolved promise");if(this.promise.rejected)throw new Error("Cannot resolve a rejected promise");this.promise.resolved=!0,this.promise.value=a;var b=this.promise.onSuccess;this.promise.onSuccess=null,this.promise.onError=null,setTimeout(function(){b.forEach(function(b){b(a)})})},d.prototype.reject=function(a){if(this.promise.resolved)throw new Error("Cannot reject an already resolved promise");if(this.promise.rejected)throw new Error("Cannot re-reject a rejected promise");this.promise.rejected=!0,this.promise.value=a;var b=this.promise.onError;this.promise.onSuccess=null,this.promise.onError=null,setTimeout(function(){b.forEach(function(b){b(a)})})},d.waitForAll=function(a){var b=new d,c=0,e=0,f={},g={},h=a.length;a.forEach(function(a,b){a.then(function(a){g[b]=a,c++,i()},function(a){f[b]=a,e++,i()})});var i=function(){if(c==h){for(var a=[],d=0,i=h;i>d;++d)a.push(g[d]);b.resolve(a)}else c+e==h&&b.reject({errors:f,values:g})};return i(),b.promise},d}(),b=function(){var a=/^function[^(]*\(([^)]*)\)/,b=function(b){var c=a.exec(b.toString().replace(/\s+/g,""));if(null==c)throw new Error("Unable to parse fn definition");return c[1]?c[1].split(/,/):[]},c=function(a){var c,d;return"function"==typeof a||a instanceof Function?(d=b(a),c=a):(c=a[a.length-1],d=a.slice(0,a.length-1)),[c,d]},d=function(a){var b=a.split("!");if(0==b.length)throw new Error("Invalid dependency: "+a);return 1==b.length?{prefix:"",name:b[0]}:{prefix:b[0],name:b.slice(1).join("!")}};return{spec:c,dep:d}}(),c=function(a,b){var c=function(a){this.plugins=a};c.rejected=function(a,b){var c=this.defer();return c.reject(this.error(a,b)),c.promise},c.prototype.rejected=c.rejected,c.resolved=function(a){var b=this.defer();return b.resolve(a),b.promise},c.prototype.resolved=c.resolved,c.prototype.get=function(a,b){if(b=[a].concat(b||[]),b&&0!=b.lastIndexOf(a))return this.rejected("Cyclic dependency: "+b.join(" <- "));for(var d=0,e=this.plugins.length;e>d;++d){var f=this.plugins[d].get(this,a,b);if(f)return f.destroy||(f.destroy=function(){return c.resolved(!0)}),f}return this.rejected("Unknown dependency: "+b.join(" <- "))},c.prototype.register=function(a,b){for(var c=0,d=this.plugins.length;d>c;++c)if(this.plugins[c].register(this,a,b))return this;throw this.error("No plugin handled registration of: "+a)},c.prototype.invoke=function(a){var b=this,d=this.parseSpec(a),e=d[0],f=d[1],g=f.map(function(a){return b.get(a,[])}),h=this.waitForAll(g),i=h.then(function(a){return e.apply(b,a)});return i.destroy=function(){return c.waitForAll(g.map(function(a){return a.destroy()})).then(function(){return!0})},i},c.error=function(a,b){return new d(a,b)},c.prototype.error=c.error,c.defer=function(){return new a},c.prototype.defer=c.defer,c.parseSpec=b.spec,c.prototype.parseSpec=b.spec,c.waitForAll=a.waitForAll,c.prototype.waitForAll=a.waitForAll;var d=function(a,b){this.name="InjectorError",this.message=a,this.cause=b};return d.prototype=new Error,d.prototype.constructor=d,d.prototype.toString=function(){return"InjectorError: "+this.message+(this.cause?" [caused by "+this.cause+"]":"")},c}(a,b);return c.prefixPlugin=function(a,b){return{register:function(c,d,e){return 0==d.indexOf(a)?b.register(c,d.substr(a.length),e):!1},get:function(c,d,e){return 0==d.indexOf(a)?b.get(c,d.substr(a.length),e):null}}},c.DOMPlugin=function(){var a=function(){this.aliases={}};return a.prototype.register=function(a,b,c){return this.aliases[b]=c,!0},a.prototype.get=function(a,b){var c=a.defer(),d=setInterval(function(){var a=$(this.aliases[b]||b);a.length&&(clearInterval(d),c.resolve(a))}.bind(this),100);return c.promise},a}(),c.HTTPPlugin=function(){var a=function(){this.aliases={}};return a.prototype.register=function(a,b,c){return this.aliases[b]=c,!0},a.prototype.get=function(a,b){var d=a.defer();return $.ajax(this.aliases[b]||b).then(function(a){d.resolve(a)},d.reject.bind(d)),d.promise.destroy=function(){return c.resolved(!0)},d.promise},a}(),c.ValuePlugin=function(){var a=function(){this.specs={},this.values={}};return a.prototype.register=function(a,b,c){return this.specs[b]=c,!0},a.prototype.get=function(a,b,c){if(b in this.values)return this.values[b].references++,this.values[b];if(b in this.specs){var d=this.specs[b],e=a.parseSpec(d),f=e[0],g=e[1],h=g.map(function(d){return a.get(d,[b].concat(c))}),i=a.waitForAll(h),j=null,k=!1,l=a.defer(),m=l.promise;i.then(function(b){var d=Object.create(a);d.requestor=function(){return k=!0,c[1]};try{l.resolve(f.apply(d,b))}catch(e){l.reject(e)}finally{j=d.onDestroy,m.references<=0&&j&&j()}},function(){l.reject(a.error("Error constructing "+b))}),m.references=1;var n=this.values;return m.destroy=function(){return this.references--,this.references<=0?(j&&j(),delete n[b],m.resolved||m.rejected||l.reject(a.error("Promise destroyed before value completed construction")),a.waitForAll(h.map(function(a){return a.destroy()})).then(function(){return!0})):a.resolved(!0)},k?m:this.values[b]=m}return null},a}(),c}();"undefined"!=typeof module&&(module.exports=Scorpion);
\ No newline at end of file
-- 
cgit v1.2.3