UNPKG

mimosa-ember-test

Version:
1,684 lines (1,332 loc) 1.56 MB
/*! * @overview Ember - JavaScript Application Framework * @copyright Copyright 2011-2014 Tilde Inc. and contributors * Portions Copyright 2006-2011 Strobe Inc. * Portions Copyright 2008-2011 Apple Inc. All rights reserved. * @license Licensed under MIT license * See https://raw.github.com/emberjs/ember.js/master/LICENSE * @version 1.7.0-beta.4 */ (function() { var define, requireModule, require, requirejs, Ember; (function() { Ember = this.Ember = this.Ember || {}; if (typeof Ember === 'undefined') { Ember = {} }; if (typeof Ember.__loader === 'undefined') { var registry = {}, seen = {}; define = function(name, deps, callback) { registry[name] = { deps: deps, callback: callback }; }; requirejs = require = requireModule = function(name) { if (seen.hasOwnProperty(name)) { return seen[name]; } seen[name] = {}; if (!registry[name]) { throw new Error("Could not find module " + name); } var mod = registry[name], deps = mod.deps, callback = mod.callback, reified = [], exports; for (var i=0, l=deps.length; i<l; i++) { if (deps[i] === 'exports') { reified.push(exports = {}); } else { reified.push(requireModule(resolve(deps[i]))); } } var value = callback.apply(this, reified); return seen[name] = exports || value; function resolve(child) { if (child.charAt(0) !== '.') { return child; } var parts = child.split("/"); var parentBase = name.split("/").slice(0, -1); for (var i=0, l=parts.length; i<l; i++) { var part = parts[i]; if (part === '..') { parentBase.pop(); } else if (part === '.') { continue; } else { parentBase.push(part); } } return parentBase.join("/"); } }; requirejs._eak_seen = registry; Ember.__loader = {define: define, require: require, registry: registry}; } else { define = Ember.__loader.define; requirejs = require = requireModule = Ember.__loader.require; } })(); define("backburner", ["backburner/utils","backburner/deferred_action_queues","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var Utils = __dependency1__["default"]; var DeferredActionQueues = __dependency2__.DeferredActionQueues; var slice = [].slice, pop = [].pop, each = Utils.each, isString = Utils.isString, isFunction = Utils.isFunction, isNumber = Utils.isNumber, timers = [], global = this, NUMBER = /\d+/; // In IE 6-8, try/finally doesn't work without a catch. // Unfortunately, this is impossible to test for since wrapping it in a parent try/catch doesn't trigger the bug. // This tests for another broken try/catch behavior that only exhibits in the same versions of IE. var needsIETryCatchFix = (function(e,x){ try{ x(); } catch(e) { } // jshint ignore:line return !!e; })(); function isCoercableNumber(number) { return isNumber(number) || NUMBER.test(number); } function Backburner(queueNames, options) { this.queueNames = queueNames; this.options = options || {}; if (!this.options.defaultQueue) { this.options.defaultQueue = queueNames[0]; } this.instanceStack = []; this._debouncees = []; this._throttlers = []; } Backburner.prototype = { queueNames: null, options: null, currentInstance: null, instanceStack: null, begin: function() { var options = this.options, onBegin = options && options.onBegin, previousInstance = this.currentInstance; if (previousInstance) { this.instanceStack.push(previousInstance); } this.currentInstance = new DeferredActionQueues(this.queueNames, options); if (onBegin) { onBegin(this.currentInstance, previousInstance); } }, end: function() { var options = this.options, onEnd = options && options.onEnd, currentInstance = this.currentInstance, nextInstance = null; // Prevent double-finally bug in Safari 6.0.2 and iOS 6 // This bug appears to be resolved in Safari 6.0.5 and iOS 7 var finallyAlreadyCalled = false; try { currentInstance.flush(); } finally { if (!finallyAlreadyCalled) { finallyAlreadyCalled = true; this.currentInstance = null; if (this.instanceStack.length) { nextInstance = this.instanceStack.pop(); this.currentInstance = nextInstance; } if (onEnd) { onEnd(currentInstance, nextInstance); } } } }, run: function(target, method /*, args */) { var onError = getOnError(this.options); this.begin(); if (!method) { method = target; target = null; } if (isString(method)) { method = target[method]; } var args = slice.call(arguments, 2); // guard against Safari 6's double-finally bug var didFinally = false; if (onError) { try { return method.apply(target, args); } catch(error) { onError(error); } finally { if (!didFinally) { didFinally = true; this.end(); } } } else { try { return method.apply(target, args); } finally { if (!didFinally) { didFinally = true; this.end(); } } } }, defer: function(queueName, target, method /* , args */) { if (!method) { method = target; target = null; } if (isString(method)) { method = target[method]; } var stack = this.DEBUG ? new Error() : undefined, args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, false, stack); }, deferOnce: function(queueName, target, method /* , args */) { if (!method) { method = target; target = null; } if (isString(method)) { method = target[method]; } var stack = this.DEBUG ? new Error() : undefined, args = arguments.length > 3 ? slice.call(arguments, 3) : undefined; if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, true, stack); }, setTimeout: function() { var args = slice.call(arguments), length = args.length, method, wait, target, methodOrTarget, methodOrWait, methodOrArgs; if (length === 0) { return; } else if (length === 1) { method = args.shift(); wait = 0; } else if (length === 2) { methodOrTarget = args[0]; methodOrWait = args[1]; if (isFunction(methodOrWait) || isFunction(methodOrTarget[methodOrWait])) { target = args.shift(); method = args.shift(); wait = 0; } else if (isCoercableNumber(methodOrWait)) { method = args.shift(); wait = args.shift(); } else { method = args.shift(); wait = 0; } } else { var last = args[args.length - 1]; if (isCoercableNumber(last)) { wait = args.pop(); } else { wait = 0; } methodOrTarget = args[0]; methodOrArgs = args[1]; if (isFunction(methodOrArgs) || (isString(methodOrArgs) && methodOrTarget !== null && methodOrArgs in methodOrTarget)) { target = args.shift(); method = args.shift(); } else { method = args.shift(); } } var executeAt = (+new Date()) + parseInt(wait, 10); if (isString(method)) { method = target[method]; } var onError = getOnError(this.options); function fn() { if (onError) { try { method.apply(target, args); } catch (e) { onError(e); } } else { method.apply(target, args); } } // find position to insert var i = searchTimer(executeAt, timers); timers.splice(i, 0, executeAt, fn); updateLaterTimer(this, executeAt, wait); return fn; }, throttle: function(target, method /* , args, wait, [immediate] */) { var self = this, args = arguments, immediate = pop.call(args), wait, throttler, index, timer; if (isNumber(immediate) || isString(immediate)) { wait = immediate; immediate = true; } else { wait = pop.call(args); } wait = parseInt(wait, 10); index = findThrottler(target, method, this._throttlers); if (index > -1) { return this._throttlers[index]; } // throttled timer = global.setTimeout(function() { if (!immediate) { self.run.apply(self, args); } var index = findThrottler(target, method, self._throttlers); if (index > -1) { self._throttlers.splice(index, 1); } }, wait); if (immediate) { self.run.apply(self, args); } throttler = [target, method, timer]; this._throttlers.push(throttler); return throttler; }, debounce: function(target, method /* , args, wait, [immediate] */) { var self = this, args = arguments, immediate = pop.call(args), wait, index, debouncee, timer; if (isNumber(immediate) || isString(immediate)) { wait = immediate; immediate = false; } else { wait = pop.call(args); } wait = parseInt(wait, 10); // Remove debouncee index = findDebouncee(target, method, this._debouncees); if (index > -1) { debouncee = this._debouncees[index]; this._debouncees.splice(index, 1); clearTimeout(debouncee[2]); } timer = global.setTimeout(function() { if (!immediate) { self.run.apply(self, args); } var index = findDebouncee(target, method, self._debouncees); if (index > -1) { self._debouncees.splice(index, 1); } }, wait); if (immediate && index === -1) { self.run.apply(self, args); } debouncee = [target, method, timer]; self._debouncees.push(debouncee); return debouncee; }, cancelTimers: function() { var clearItems = function(item) { clearTimeout(item[2]); }; each(this._throttlers, clearItems); this._throttlers = []; each(this._debouncees, clearItems); this._debouncees = []; if (this._laterTimer) { clearTimeout(this._laterTimer); this._laterTimer = null; } timers = []; if (this._autorun) { clearTimeout(this._autorun); this._autorun = null; } }, hasTimers: function() { return !!timers.length || !!this._debouncees.length || !!this._throttlers.length || this._autorun; }, cancel: function(timer) { var timerType = typeof timer; if (timer && timerType === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce return timer.queue.cancel(timer); } else if (timerType === 'function') { // we're cancelling a setTimeout for (var i = 0, l = timers.length; i < l; i += 2) { if (timers[i + 1] === timer) { timers.splice(i, 2); // remove the two elements return true; } } } else if (Object.prototype.toString.call(timer) === "[object Array]"){ // we're cancelling a throttle or debounce return this._cancelItem(findThrottler, this._throttlers, timer) || this._cancelItem(findDebouncee, this._debouncees, timer); } else { return; // timer was null or not a timer } }, _cancelItem: function(findMethod, array, timer){ var item, index; if (timer.length < 3) { return false; } index = findMethod(timer[0], timer[1], array); if(index > -1) { item = array[index]; if(item[2] === timer[2]){ array.splice(index, 1); clearTimeout(timer[2]); return true; } } return false; } }; Backburner.prototype.schedule = Backburner.prototype.defer; Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; Backburner.prototype.later = Backburner.prototype.setTimeout; if (needsIETryCatchFix) { var originalRun = Backburner.prototype.run; Backburner.prototype.run = wrapInTryCatch(originalRun); var originalEnd = Backburner.prototype.end; Backburner.prototype.end = wrapInTryCatch(originalEnd); } function wrapInTryCatch(func) { return function () { try { return func.apply(this, arguments); } catch (e) { throw e; } }; } function getOnError(options) { return options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]); } function createAutorun(backburner) { backburner.begin(); backburner._autorun = global.setTimeout(function() { backburner._autorun = null; backburner.end(); }); } function updateLaterTimer(self, executeAt, wait) { if (!self._laterTimer || executeAt < self._laterTimerExpiresAt) { self._laterTimer = global.setTimeout(function() { self._laterTimer = null; self._laterTimerExpiresAt = null; executeTimers(self); }, wait); self._laterTimerExpiresAt = executeAt; } } function executeTimers(self) { var now = +new Date(), time, fns, i, l; self.run(function() { i = searchTimer(now, timers); fns = timers.splice(0, i); for (i = 1, l = fns.length; i < l; i += 2) { self.schedule(self.options.defaultQueue, null, fns[i]); } }); if (timers.length) { updateLaterTimer(self, timers[0], timers[0] - now); } } function findDebouncee(target, method, debouncees) { return findItem(target, method, debouncees); } function findThrottler(target, method, throttlers) { return findItem(target, method, throttlers); } function findItem(target, method, collection) { var item, index = -1; for (var i = 0, l = collection.length; i < l; i++) { item = collection[i]; if (item[0] === target && item[1] === method) { index = i; break; } } return index; } function searchTimer(time, timers) { var start = 0, end = timers.length - 2, middle, l; while (start < end) { // since timers is an array of pairs 'l' will always // be an integer l = (end - start) / 2; // compensate for the index in case even number // of pairs inside timers middle = start + l - (l % 2); if (time >= timers[middle]) { start = middle + 2; } else { end = middle; } } return (time >= timers[start]) ? start + 2 : start; } __exports__.Backburner = Backburner; }); define("backburner/deferred_action_queues", ["backburner/utils","backburner/queue","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var Utils = __dependency1__["default"]; var Queue = __dependency2__.Queue; var each = Utils.each, isString = Utils.isString; function DeferredActionQueues(queueNames, options) { var queues = this.queues = {}; this.queueNames = queueNames = queueNames || []; this.options = options; each(queueNames, function(queueName) { queues[queueName] = new Queue(this, queueName, options); }); } DeferredActionQueues.prototype = { queueNames: null, queues: null, options: null, schedule: function(queueName, target, method, args, onceFlag, stack) { var queues = this.queues, queue = queues[queueName]; if (!queue) { throw new Error("You attempted to schedule an action in a queue (" + queueName + ") that doesn't exist"); } if (onceFlag) { return queue.pushUnique(target, method, args, stack); } else { return queue.push(target, method, args, stack); } }, invoke: function(target, method, args, _) { if (args && args.length > 0) { method.apply(target, args); } else { method.call(target); } }, invokeWithOnError: function(target, method, args, onError) { try { if (args && args.length > 0) { method.apply(target, args); } else { method.call(target); } } catch(error) { onError(error); } }, flush: function() { var queues = this.queues, queueNames = this.queueNames, queueName, queue, queueItems, priorQueueNameIndex, queueNameIndex = 0, numberOfQueues = queueNames.length, options = this.options, onError = options.onError || (options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]), invoke = onError ? this.invokeWithOnError : this.invoke; outerloop: while (queueNameIndex < numberOfQueues) { queueName = queueNames[queueNameIndex]; queue = queues[queueName]; queueItems = queue._queueBeingFlushed = queue._queue.slice(); queue._queue = []; var queueOptions = queue.options, // TODO: write a test for this before = queueOptions && queueOptions.before, after = queueOptions && queueOptions.after, target, method, args, stack, queueIndex = 0, numberOfQueueItems = queueItems.length; if (numberOfQueueItems && before) { before(); } while (queueIndex < numberOfQueueItems) { target = queueItems[queueIndex]; method = queueItems[queueIndex+1]; args = queueItems[queueIndex+2]; stack = queueItems[queueIndex+3]; // Debugging assistance if (isString(method)) { method = target[method]; } // method could have been nullified / canceled during flush if (method) { invoke(target, method, args, onError); } queueIndex += 4; } queue._queueBeingFlushed = null; if (numberOfQueueItems && after) { after(); } if ((priorQueueNameIndex = indexOfPriorQueueWithActions(this, queueNameIndex)) !== -1) { queueNameIndex = priorQueueNameIndex; continue outerloop; } queueNameIndex++; } } }; function indexOfPriorQueueWithActions(daq, currentQueueIndex) { var queueName, queue; for (var i = 0, l = currentQueueIndex; i <= l; i++) { queueName = daq.queueNames[i]; queue = daq.queues[queueName]; if (queue._queue.length) { return i; } } return -1; } __exports__.DeferredActionQueues = DeferredActionQueues; }); define("backburner/queue", ["exports"], function(__exports__) { "use strict"; function Queue(daq, name, options) { this.daq = daq; this.name = name; this.globalOptions = options; this.options = options[name]; this._queue = []; } Queue.prototype = { daq: null, name: null, options: null, onError: null, _queue: null, push: function(target, method, args, stack) { var queue = this._queue; queue.push(target, method, args, stack); return {queue: this, target: target, method: method}; }, pushUnique: function(target, method, args, stack) { var queue = this._queue, currentTarget, currentMethod, i, l; for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; if (currentTarget === target && currentMethod === method) { queue[i+2] = args; // replace args queue[i+3] = stack; // replace stack return {queue: this, target: target, method: method}; } } queue.push(target, method, args, stack); return {queue: this, target: target, method: method}; }, // TODO: remove me, only being used for Ember.run.sync flush: function() { var queue = this._queue, globalOptions = this.globalOptions, options = this.options, before = options && options.before, after = options && options.after, onError = globalOptions.onError || (globalOptions.onErrorTarget && globalOptions.onErrorTarget[globalOptions.onErrorMethod]), target, method, args, stack, i, l = queue.length; if (l && before) { before(); } for (i = 0; i < l; i += 4) { target = queue[i]; method = queue[i+1]; args = queue[i+2]; stack = queue[i+3]; // Debugging assistance // TODO: error handling if (args && args.length > 0) { if (onError) { try { method.apply(target, args); } catch (e) { onError(e); } } else { method.apply(target, args); } } else { if (onError) { try { method.call(target); } catch(e) { onError(e); } } else { method.call(target); } } } if (l && after) { after(); } // check if new items have been added if (queue.length > l) { this._queue = queue.slice(l); this.flush(); } else { this._queue.length = 0; } }, cancel: function(actionToCancel) { var queue = this._queue, currentTarget, currentMethod, i, l; for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { queue.splice(i, 4); return true; } } // if not found in current queue // could be in the queue that is being flushed queue = this._queueBeingFlushed; if (!queue) { return; } for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i+1]; if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) { // don't mess with array during flush // just nullify the method queue[i+1] = null; return true; } } } }; __exports__.Queue = Queue; }); define("backburner/utils", ["exports"], function(__exports__) { "use strict"; __exports__["default"] = { each: function(collection, callback) { for (var i = 0; i < collection.length; i++) { callback(collection[i]); } }, isString: function(suspect) { return typeof suspect === 'string'; }, isFunction: function(suspect) { return typeof suspect === 'function'; }, isNumber: function(suspect) { return typeof suspect === 'number'; } }; }); define("container", ["container/container","exports"], function(__dependency1__, __exports__) { "use strict"; /* Public api for the container is still in flux. The public api, specified on the application namespace should be considered the stable api. // @module container @private */ /* Flag to enable/disable model factory injections (disabled by default) If model factory injections are enabled, models should not be accessed globally (only through `container.lookupFactory('model:modelName'))`); */ Ember.MODEL_FACTORY_INJECTIONS = false; if (Ember.ENV && typeof Ember.ENV.MODEL_FACTORY_INJECTIONS !== 'undefined') { Ember.MODEL_FACTORY_INJECTIONS = !!Ember.ENV.MODEL_FACTORY_INJECTIONS; } var Container = __dependency1__["default"]; __exports__["default"] = Container; }); define("container/container", ["container/inheriting_dict","ember-metal/core","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var InheritingDict = __dependency1__["default"]; var Ember = __dependency2__["default"]; // Ember.assert // A lightweight container that helps to assemble and decouple components. // Public api for the container is still in flux. // The public api, specified on the application namespace should be considered the stable api. function Container(parent) { this.parent = parent; this.children = []; this.resolver = parent && parent.resolver || function() {}; this.registry = new InheritingDict(parent && parent.registry); this.cache = new InheritingDict(parent && parent.cache); this.factoryCache = new InheritingDict(parent && parent.factoryCache); this.resolveCache = new InheritingDict(parent && parent.resolveCache); this.typeInjections = new InheritingDict(parent && parent.typeInjections); this.injections = {}; this.factoryTypeInjections = new InheritingDict(parent && parent.factoryTypeInjections); this.factoryInjections = {}; this._options = new InheritingDict(parent && parent._options); this._typeOptions = new InheritingDict(parent && parent._typeOptions); } Container.prototype = { /** @property parent @type Container @default null */ parent: null, /** @property children @type Array @default [] */ children: null, /** @property resolver @type function */ resolver: null, /** @property registry @type InheritingDict */ registry: null, /** @property cache @type InheritingDict */ cache: null, /** @property typeInjections @type InheritingDict */ typeInjections: null, /** @property injections @type Object @default {} */ injections: null, /** @private @property _options @type InheritingDict @default null */ _options: null, /** @private @property _typeOptions @type InheritingDict */ _typeOptions: null, /** Returns a new child of the current container. These children are configured to correctly inherit from the current container. @method child @return {Container} */ child: function() { var container = new Container(this); this.children.push(container); return container; }, /** Sets a key-value pair on the current container. If a parent container, has the same key, once set on a child, the parent and child will diverge as expected. @method set @param {Object} object @param {String} key @param {any} value */ set: function(object, key, value) { object[key] = value; }, /** Registers a factory for later injection. Example: ```javascript var container = new Container(); container.register('model:user', Person, {singleton: false }); container.register('fruit:favorite', Orange); container.register('communication:main', Email, {singleton: false}); ``` @method register @param {String} fullName @param {Function} factory @param {Object} options */ register: function(fullName, factory, options) { Ember.assert('fullName must be a proper full name', validateFullName(fullName)); if (factory === undefined) { throw new TypeError('Attempting to register an unknown factory: `' + fullName + '`'); } var normalizedName = this.normalize(fullName); if (this.cache.has(normalizedName)) { throw new Error('Cannot re-register: `' + fullName +'`, as it has already been looked up.'); } this.registry.set(normalizedName, factory); this._options.set(normalizedName, options || {}); }, /** Unregister a fullName ```javascript var container = new Container(); container.register('model:user', User); container.lookup('model:user') instanceof User //=> true container.unregister('model:user') container.lookup('model:user') === undefined //=> true ``` @method unregister @param {String} fullName */ unregister: function(fullName) { Ember.assert('fullName must be a proper full name', validateFullName(fullName)); var normalizedName = this.normalize(fullName); this.registry.remove(normalizedName); this.cache.remove(normalizedName); this.factoryCache.remove(normalizedName); this.resolveCache.remove(normalizedName); this._options.remove(normalizedName); }, /** Given a fullName return the corresponding factory. By default `resolve` will retrieve the factory from its container's registry. ```javascript var container = new Container(); container.register('api:twitter', Twitter); container.resolve('api:twitter') // => Twitter ``` Optionally the container can be provided with a custom resolver. If provided, `resolve` will first provide the custom resolver the opportunity to resolve the fullName, otherwise it will fallback to the registry. ```javascript var container = new Container(); container.resolver = function(fullName) { // lookup via the module system of choice }; // the twitter factory is added to the module system container.resolve('api:twitter') // => Twitter ``` @method resolve @param {String} fullName @return {Function} fullName's factory */ resolve: function(fullName) { Ember.assert('fullName must be a proper full name', validateFullName(fullName)); return resolve(this, this.normalize(fullName)); }, /** A hook that can be used to describe how the resolver will attempt to find the factory. For example, the default Ember `.describe` returns the full class name (including namespace) where Ember's resolver expects to find the `fullName`. @method describe @param {String} fullName @return {string} described fullName */ describe: function(fullName) { return fullName; }, /** A hook to enable custom fullName normalization behaviour @method normalize @param {String} fullName @return {string} normalized fullName */ normalize: function(fullName) { return fullName; }, /** @method makeToString @param {any} factory @param {string} fullName @return {function} toString function */ makeToString: function(factory, fullName) { return factory.toString(); }, /** Given a fullName return a corresponding instance. The default behaviour is for lookup to return a singleton instance. The singleton is scoped to the container, allowing multiple containers to all have their own locally scoped singletons. ```javascript var container = new Container(); container.register('api:twitter', Twitter); var twitter = container.lookup('api:twitter'); twitter instanceof Twitter; // => true // by default the container will return singletons var twitter2 = container.lookup('api:twitter'); twitter2 instanceof Twitter; // => true twitter === twitter2; //=> true ``` If singletons are not wanted an optional flag can be provided at lookup. ```javascript var container = new Container(); container.register('api:twitter', Twitter); var twitter = container.lookup('api:twitter', { singleton: false }); var twitter2 = container.lookup('api:twitter', { singleton: false }); twitter === twitter2; //=> false ``` @method lookup @param {String} fullName @param {Object} options @return {any} */ lookup: function(fullName, options) { Ember.assert('fullName must be a proper full name', validateFullName(fullName)); return lookup(this, this.normalize(fullName), options); }, /** Given a fullName return the corresponding factory. @method lookupFactory @param {String} fullName @return {any} */ lookupFactory: function(fullName) { Ember.assert('fullName must be a proper full name', validateFullName(fullName)); return factoryFor(this, this.normalize(fullName)); }, /** Given a fullName check if the container is aware of its factory or singleton instance. @method has @param {String} fullName @return {Boolean} */ has: function(fullName) { Ember.assert('fullName must be a proper full name', validateFullName(fullName)); return has(this, this.normalize(fullName)); }, /** Allow registering options for all factories of a type. ```javascript var container = new Container(); // if all of type `connection` must not be singletons container.optionsForType('connection', { singleton: false }); container.register('connection:twitter', TwitterConnection); container.register('connection:facebook', FacebookConnection); var twitter = container.lookup('connection:twitter'); var twitter2 = container.lookup('connection:twitter'); twitter === twitter2; // => false var facebook = container.lookup('connection:facebook'); var facebook2 = container.lookup('connection:facebook'); facebook === facebook2; // => false ``` @method optionsForType @param {String} type @param {Object} options */ optionsForType: function(type, options) { if (this.parent) { illegalChildOperation('optionsForType'); } this._typeOptions.set(type, options); }, /** @method options @param {String} type @param {Object} options */ options: function(type, options) { this.optionsForType(type, options); }, /** Used only via `injection`. Provides a specialized form of injection, specifically enabling all objects of one type to be injected with a reference to another object. For example, provided each object of type `controller` needed a `router`. one would do the following: ```javascript var container = new Container(); container.register('router:main', Router); container.register('controller:user', UserController); container.register('controller:post', PostController); container.typeInjection('controller', 'router', 'router:main'); var user = container.lookup('controller:user'); var post = container.lookup('controller:post'); user.router instanceof Router; //=> true post.router instanceof Router; //=> true // both controllers share the same router user.router === post.router; //=> true ``` @private @method typeInjection @param {String} type @param {String} property @param {String} fullName */ typeInjection: function(type, property, fullName) { Ember.assert('fullName must be a proper full name', validateFullName(fullName)); if (this.parent) { illegalChildOperation('typeInjection'); } var fullNameType = fullName.split(':')[0]; if(fullNameType === type) { throw new Error('Cannot inject a `' + fullName + '` on other ' + type + '(s). Register the `' + fullName + '` as a different type and perform the typeInjection.'); } addTypeInjection(this.typeInjections, type, property, fullName); }, /** Defines injection rules. These rules are used to inject dependencies onto objects when they are instantiated. Two forms of injections are possible: * Injecting one fullName on another fullName * Injecting one fullName on a type Example: ```javascript var container = new Container(); container.register('source:main', Source); container.register('model:user', User); container.register('model:post', Post); // injecting one fullName on another fullName // eg. each user model gets a post model container.injection('model:user', 'post', 'model:post'); // injecting one fullName on another type container.injection('model', 'source', 'source:main'); var user = container.lookup('model:user'); var post = container.lookup('model:post'); user.source instanceof Source; //=> true post.source instanceof Source; //=> true user.post instanceof Post; //=> true // and both models share the same source user.source === post.source; //=> true ``` @method injection @param {String} factoryName @param {String} property @param {String} injectionName */ injection: function(fullName, property, injectionName) { if (this.parent) { illegalChildOperation('injection'); } validateFullName(injectionName); var normalizedInjectionName = this.normalize(injectionName); if (fullName.indexOf(':') === -1) { return this.typeInjection(fullName, property, normalizedInjectionName); } Ember.assert('fullName must be a proper full name', validateFullName(fullName)); var normalizedName = this.normalize(fullName); if (this.cache.has(normalizedName)) { throw new Error("Attempted to register an injection for a type that has already been looked up. ('" + normalizedName + "', '" + property + "', '" + injectionName + "')"); } addInjection(this.injections, normalizedName, property, normalizedInjectionName); }, /** Used only via `factoryInjection`. Provides a specialized form of injection, specifically enabling all factory of one type to be injected with a reference to another object. For example, provided each factory of type `model` needed a `store`. one would do the following: ```javascript var container = new Container(); container.register('store:main', SomeStore); container.factoryTypeInjection('model', 'store', 'store:main'); var store = container.lookup('store:main'); var UserFactory = container.lookupFactory('model:user'); UserFactory.store instanceof SomeStore; //=> true ``` @private @method factoryTypeInjection @param {String} type @param {String} property @param {String} fullName */ factoryTypeInjection: function(type, property, fullName) { if (this.parent) { illegalChildOperation('factoryTypeInjection'); } addTypeInjection(this.factoryTypeInjections, type, property, this.normalize(fullName)); }, /** Defines factory injection rules. Similar to regular injection rules, but are run against factories, via `Container#lookupFactory`. These rules are used to inject objects onto factories when they are looked up. Two forms of injections are possible: * Injecting one fullName on another fullName * Injecting one fullName on a type Example: ```javascript var container = new Container(); container.register('store:main', Store); container.register('store:secondary', OtherStore); container.register('model:user', User); container.register('model:post', Post); // injecting one fullName on another type container.factoryInjection('model', 'store', 'store:main'); // injecting one fullName on another fullName container.factoryInjection('model:post', 'secondaryStore', 'store:secondary'); var UserFactory = container.lookupFactory('model:user'); var PostFactory = container.lookupFactory('model:post'); var store = container.lookup('store:main'); UserFactory.store instanceof Store; //=> true UserFactory.secondaryStore instanceof OtherStore; //=> false PostFactory.store instanceof Store; //=> true PostFactory.secondaryStore instanceof OtherStore; //=> true // and both models share the same source instance UserFactory.store === PostFactory.store; //=> true ``` @method factoryInjection @param {String} factoryName @param {String} property @param {String} injectionName */ factoryInjection: function(fullName, property, injectionName) { if (this.parent) { illegalChildOperation('injection'); } var normalizedName = this.normalize(fullName); var normalizedInjectionName = this.normalize(injectionName); validateFullName(injectionName); if (fullName.indexOf(':') === -1) { return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName); } Ember.assert('fullName must be a proper full name', validateFullName(fullName)); if (this.factoryCache.has(normalizedName)) { throw new Error('Attempted to register a factoryInjection for a type that has already ' + 'been looked up. (\'' + normalizedName + '\', \'' + property + '\', \'' + injectionName + '\')'); } addInjection(this.factoryInjections, normalizedName, property, normalizedInjectionName); }, /** A depth first traversal, destroying the container, its descendant containers and all their managed objects. @method destroy */ destroy: function() { for (var i = 0, length = this.children.length; i < length; i++) { this.children[i].destroy(); } this.children = []; eachDestroyable(this, function(item) { item.destroy(); }); this.parent = undefined; this.isDestroyed = true; }, /** @method reset */ reset: function() { for (var i = 0, length = this.children.length; i < length; i++) { resetCache(this.children[i]); } resetCache(this); } }; function resolve(container, normalizedName) { var cached = container.resolveCache.get(normalizedName); if (cached) { return cached; } var resolved = container.resolver(normalizedName) || container.registry.get(normalizedName); container.resolveCache.set(normalizedName, resolved); return resolved; } function has(container, fullName){ if (container.cache.has(fullName)) { return true; } return !!container.resolve(fullName); } function lookup(container, fullName, options) { options = options || {}; if (container.cache.has(fullName) && options.singleton !== false) { return container.cache.get(fullName); } var value = instantiate(container, fullName); if (value === undefined) { return; } if (isSingleton(container, fullName) && options.singleton !== false) { container.cache.set(fullName, value); } return value; } function illegalChildOperation(operation) { throw new Error(operation + ' is not currently supported on child containers'); } function isSingleton(container, fullName) { var singleton = option(container, fullName, 'singleton'); return singleton !== false; } function buildInjections(container, injections) { var hash = {}; if (!injections) { return hash; } var injection, injectable; for (var i = 0, length = injections.length; i < length; i++) { injection = injections[i]; injectable = lookup(container, injection.fullName); if (injectable !== undefined) { hash[injection.property] = injectable; } else { throw new Error('Attempting to inject an unknown injection: `' + injection.fullName + '`'); } } return hash; } function option(container, fullName, optionName) { var options = container._options.get(fullName); if (options && options[optionName] !== undefined) { return options[optionName]; } var type = fullName.split(':')[0]; options = container._typeOptions.get(type); if (options) { return options[optionName]; } } function factoryFor(container, fullName) { var cache = container.factoryCache; if (cache.has(fullName)) { return cache.get(fullName); } var factory = container.resolve(fullName); if (factory === undefined) { return; } var type = fullName.split(':')[0]; if (!factory || typeof factory.extend !== 'function' || (!Ember.MODEL_FACTORY_INJECTIONS && type === 'model')) { // TODO: think about a 'safe' merge style extension // for now just fallback to create time injection return factory; } else { var injections = injectionsFor(container, fullName); var factoryInjections = factoryInjectionsFor(container, fullName); factoryInjections._toString = container.makeToString(factory, fullName); var injectedFactory = factory.extend(injections); injectedFactory.reopenClass(factoryInjections); cache.set(fullName, injectedFactory); return injectedFactory; } } function injectionsFor(container, fullName) { var splitName = fullName.split(':'), type = splitName[0], injections = []; injections = injections.concat(container.typeInjections.get(type) || []); injections = injections.concat(container.injections[fullName] || []); injections = buildInjections(container, injections); injections._debugContainerKey = fullName; injections.container = container; return injections; } function factoryInjectionsFor(container, fullName) { var splitName = fullName.split(':'), type = splitName[0], factoryInjections = []; factoryInjections = factoryInjections.concat(container.factoryTypeInjections.get(type) || []); factoryInjections = factoryInjections.concat(container.factoryInjections[fullName] || []); factoryInjections = buildInjections(container, factoryInjections); factoryInjections._debugContainerKey = fullName; return factoryInjections; } function instantiate(container, fullName) { var factory = factoryFor(container, fullName); if (option(container, fullName, 'instantiate') === false) { return factory; } if (factory) { if (typeof factory.create !== 'function') { throw new Error('Failed to create an instance of \'' + fullName + '\'. ' + 'Most likely an improperly defined class or an invalid module export.'); } if (typeof factory.extend === 'function') { // assume the factory was extendable and is already injected return factory.create(); } else { // assume the factory was extendable // to create time injections // TODO: support new'ing for instantiation and merge injections for pure JS Functions return factory.create(injectionsFor(container, fullName)); } } } function eachDestroyable(container, callback) { container.cache.eachLocal(function(key, value) { if (option(container, key, 'instanti