;(function() { /*! * @overview Ember - JavaScript Application Framework * @copyright Copyright 2011-2016 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 2.8.3 */ var enifed, requireModule, require, Ember; var mainContext = this; (function() { var isNode = typeof window === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]'; if (!isNode) { Ember = this.Ember = this.Ember || {}; } if (typeof Ember === 'undefined') { Ember = {}; } if (typeof Ember.__loader === 'undefined') { var registry = {}; var seen = {}; enifed = function(name, deps, callback) { var value = { }; if (!callback) { value.deps = []; value.callback = deps; } else { value.deps = deps; value.callback = callback; } registry[name] = value; }; require = requireModule = function(name) { return internalRequire(name, null); }; // setup `require` module require['default'] = require; require.has = function registryHas(moduleName) { return !!registry[moduleName] || !!registry[moduleName + '/index']; }; function missingModule(name, referrerName) { if (referrerName) { throw new Error('Could not find module ' + name + ' required by: ' + referrerName); } else { throw new Error('Could not find module ' + name); } } function internalRequire(_name, referrerName) { var name = _name; var mod = registry[name]; if (!mod) { name = name + '/index'; mod = registry[name]; } var exports = seen[name]; if (exports !== undefined) { return exports; } exports = seen[name] = {}; if (!mod) { missingModule(_name, referrerName); } var deps = mod.deps; var callback = mod.callback; var reified = new Array(deps.length); for (var i = 0; i < deps.length; i++) { if (deps[i] === 'exports') { reified[i] = exports; } else if (deps[i] === 'require') { reified[i] = require; } else { reified[i] = internalRequire(deps[i], name); } } callback.apply(this, reified); return exports; } requireModule._eak_seen = registry; Ember.__loader = { define: enifed, require: require, registry: registry }; } else { enifed = Ember.__loader.define; require = requireModule = Ember.__loader.require; } })(); enifed('backburner', ['exports', 'backburner/utils', 'backburner/platform', 'backburner/binary-search', 'backburner/deferred-action-queues'], function (exports, _backburnerUtils, _backburnerPlatform, _backburnerBinarySearch, _backburnerDeferredActionQueues) { 'use strict'; exports.default = Backburner; 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 = []; this._eventCallbacks = { end: [], begin: [] }; var _this = this; this._boundClearItems = function () { clearItems(); }; this._timerTimeoutId = undefined; this._timers = []; this._platform = this.options._platform || _backburnerPlatform.default; this._boundRunExpiredTimers = function () { _this._runExpiredTimers(); }; } Backburner.prototype = { begin: function () { var options = this.options; var onBegin = options && options.onBegin; var previousInstance = this.currentInstance; if (previousInstance) { this.instanceStack.push(previousInstance); } this.currentInstance = new _backburnerDeferredActionQueues.default(this.queueNames, options); this._trigger('begin', this.currentInstance, previousInstance); if (onBegin) { onBegin(this.currentInstance, previousInstance); } }, end: function () { var options = this.options; var onEnd = options && options.onEnd; var currentInstance = this.currentInstance; var 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; } this._trigger('end', currentInstance, nextInstance); if (onEnd) { onEnd(currentInstance, nextInstance); } } } }, /** Trigger an event. Supports up to two arguments. Designed around triggering transition events from one run loop instance to the next, which requires an argument for the first instance and then an argument for the next instance. @private @method _trigger @param {String} eventName @param {any} arg1 @param {any} arg2 */ _trigger: function (eventName, arg1, arg2) { var callbacks = this._eventCallbacks[eventName]; if (callbacks) { for (var i = 0; i < callbacks.length; i++) { callbacks[i](arg1, arg2); } } }, on: function (eventName, callback) { if (typeof callback !== 'function') { throw new TypeError('Callback must be a function'); } var callbacks = this._eventCallbacks[eventName]; if (callbacks) { callbacks.push(callback); } else { throw new TypeError('Cannot on() event "' + eventName + '" because it does not exist'); } }, off: function (eventName, callback) { if (eventName) { var callbacks = this._eventCallbacks[eventName]; var callbackFound = false; if (!callbacks) return; if (callback) { for (var i = 0; i < callbacks.length; i++) { if (callbacks[i] === callback) { callbackFound = true; callbacks.splice(i, 1); i--; } } } if (!callbackFound) { throw new TypeError('Cannot off() callback that does not exist'); } } else { throw new TypeError('Cannot off() event "' + eventName + '" because it does not exist'); } }, run: function () /* target, method, args */{ var length = arguments.length; var method, target, args; if (length === 1) { method = arguments[0]; target = null; } else { target = arguments[0]; method = arguments[1]; } if (_backburnerUtils.isString(method)) { method = target[method]; } if (length > 2) { args = new Array(length - 2); for (var i = 0, l = length - 2; i < l; i++) { args[i] = arguments[i + 2]; } } else { args = []; } var onError = getOnError(this.options); this.begin(); // 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(); } } } }, /* Join the passed method with an existing queue and execute immediately, if there isn't one use `Backburner#run`. The join method is like the run method except that it will schedule into an existing queue if one already exists. In either case, the join method will immediately execute the passed in function and return its result. @method join @param {Object} target @param {Function} method The method to be executed @param {any} args The method arguments @return method result */ join: function () /* target, method, args */{ if (!this.currentInstance) { return this.run.apply(this, arguments); } var length = arguments.length; var method, target; if (length === 1) { method = arguments[0]; target = null; } else { target = arguments[0]; method = arguments[1]; } if (_backburnerUtils.isString(method)) { method = target[method]; } if (length === 1) { return method(); } else if (length === 2) { return method.call(target); } else { var args = new Array(length - 2); for (var i = 0, l = length - 2; i < l; i++) { args[i] = arguments[i + 2]; } return method.apply(target, args); } }, /* Defer the passed function to run inside the specified queue. @method defer @param {String} queueName @param {Object} target @param {Function|String} method The method or method name to be executed @param {any} args The method arguments @return method result */ defer: function (queueName /* , target, method, args */) { var length = arguments.length; var method, target, args; if (length === 2) { method = arguments[1]; target = null; } else { target = arguments[1]; method = arguments[2]; } if (_backburnerUtils.isString(method)) { method = target[method]; } var stack = this.DEBUG ? new Error() : undefined; if (length > 3) { args = new Array(length - 3); for (var i = 3; i < length; i++) { args[i - 3] = arguments[i]; } } else { args = undefined; } if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, false, stack); }, deferOnce: function (queueName /* , target, method, args */) { var length = arguments.length; var method, target, args; if (length === 2) { method = arguments[1]; target = null; } else { target = arguments[1]; method = arguments[2]; } if (_backburnerUtils.isString(method)) { method = target[method]; } var stack = this.DEBUG ? new Error() : undefined; if (length > 3) { args = new Array(length - 3); for (var i = 3; i < length; i++) { args[i - 3] = arguments[i]; } } else { args = undefined; } if (!this.currentInstance) { createAutorun(this); } return this.currentInstance.schedule(queueName, target, method, args, true, stack); }, setTimeout: function () { var l = arguments.length; var args = new Array(l); for (var x = 0; x < l; x++) { args[x] = arguments[x]; } var 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 (_backburnerUtils.isFunction(methodOrWait) || _backburnerUtils.isFunction(methodOrTarget[methodOrWait])) { target = args.shift(); method = args.shift(); wait = 0; } else if (_backburnerUtils.isCoercableNumber(methodOrWait)) { method = args.shift(); wait = args.shift(); } else { method = args.shift(); wait = 0; } } else { var last = args[args.length - 1]; if (_backburnerUtils.isCoercableNumber(last)) { wait = args.pop(); } else { wait = 0; } methodOrTarget = args[0]; methodOrArgs = args[1]; if (_backburnerUtils.isFunction(methodOrArgs) || _backburnerUtils.isString(methodOrArgs) && methodOrTarget !== null && methodOrArgs in methodOrTarget) { target = args.shift(); method = args.shift(); } else { method = args.shift(); } } var executeAt = Date.now() + parseInt(wait !== wait ? 0 : wait, 10); if (_backburnerUtils.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); } } return this._setTimeout(fn, executeAt); }, _setTimeout: function (fn, executeAt) { if (this._timers.length === 0) { this._timers.push(executeAt, fn); this._installTimerTimeout(); return fn; } // find position to insert var i = _backburnerBinarySearch.default(executeAt, this._timers); this._timers.splice(i, 0, executeAt, fn); // we should be the new earliest timer if i == 0 if (i === 0) { this._reinstallTimerTimeout(); } return fn; }, throttle: function (target, method /* , args, wait, [immediate] */) { var backburner = this; var args = new Array(arguments.length); for (var i = 0; i < arguments.length; i++) { args[i] = arguments[i]; } var immediate = args.pop(); var wait, throttler, index, timer; if (_backburnerUtils.isNumber(immediate) || _backburnerUtils.isString(immediate)) { wait = immediate; immediate = true; } else { wait = args.pop(); } wait = parseInt(wait, 10); index = findThrottler(target, method, this._throttlers); if (index > -1) { return this._throttlers[index]; } // throttled timer = this._platform.setTimeout(function () { if (!immediate) { backburner.run.apply(backburner, args); } var index = findThrottler(target, method, backburner._throttlers); if (index > -1) { backburner._throttlers.splice(index, 1); } }, wait); if (immediate) { this.run.apply(this, args); } throttler = [target, method, timer]; this._throttlers.push(throttler); return throttler; }, debounce: function (target, method /* , args, wait, [immediate] */) { var backburner = this; var args = new Array(arguments.length); for (var i = 0; i < arguments.length; i++) { args[i] = arguments[i]; } var immediate = args.pop(); var wait, index, debouncee, timer; if (_backburnerUtils.isNumber(immediate) || _backburnerUtils.isString(immediate)) { wait = immediate; immediate = false; } else { wait = args.pop(); } wait = parseInt(wait, 10); // Remove debouncee index = findDebouncee(target, method, this._debouncees); if (index > -1) { debouncee = this._debouncees[index]; this._debouncees.splice(index, 1); this._platform.clearTimeout(debouncee[2]); } timer = this._platform.setTimeout(function () { if (!immediate) { backburner.run.apply(backburner, args); } var index = findDebouncee(target, method, backburner._debouncees); if (index > -1) { backburner._debouncees.splice(index, 1); } }, wait); if (immediate && index === -1) { backburner.run.apply(backburner, args); } debouncee = [target, method, timer]; backburner._debouncees.push(debouncee); return debouncee; }, cancelTimers: function () { _backburnerUtils.each(this._throttlers, this._boundClearItems); this._throttlers = []; _backburnerUtils.each(this._debouncees, this._boundClearItems); this._debouncees = []; this._clearTimerTimeout(); this._timers = []; if (this._autorun) { this._platform.clearTimeout(this._autorun); this._autorun = null; } }, hasTimers: function () { return !!this._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 = this._timers.length; i < l; i += 2) { if (this._timers[i + 1] === timer) { this._timers.splice(i, 2); // remove the two elements if (i === 0) { this._reinstallTimerTimeout(); } 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); this._platform.clearTimeout(timer[2]); return true; } } return false; }, _runExpiredTimers: function () { this._timerTimeoutId = undefined; this.run(this, this._scheduleExpiredTimers); }, _scheduleExpiredTimers: function () { var n = Date.now(); var timers = this._timers; var i = 0; var l = timers.length; for (; i < l; i += 2) { var executeAt = timers[i]; var fn = timers[i + 1]; if (executeAt <= n) { this.schedule(this.options.defaultQueue, null, fn); } else { break; } } timers.splice(0, i); this._installTimerTimeout(); }, _reinstallTimerTimeout: function () { this._clearTimerTimeout(); this._installTimerTimeout(); }, _clearTimerTimeout: function () { if (!this._timerTimeoutId) { return; } this._platform.clearTimeout(this._timerTimeoutId); this._timerTimeoutId = undefined; }, _installTimerTimeout: function () { if (!this._timers.length) { return; } var minExpiresAt = this._timers[0]; var n = Date.now(); var wait = Math.max(0, minExpiresAt - n); this._timerTimeoutId = this._platform.setTimeout(this._boundRunExpiredTimers, wait); } }; Backburner.prototype.schedule = Backburner.prototype.defer; Backburner.prototype.scheduleOnce = Backburner.prototype.deferOnce; Backburner.prototype.later = Backburner.prototype.setTimeout; function getOnError(options) { return options.onError || options.onErrorTarget && options.onErrorTarget[options.onErrorMethod]; } function createAutorun(backburner) { backburner.begin(); backburner._autorun = backburner._platform.setTimeout(function () { backburner._autorun = null; backburner.end(); }); } 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; var 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 clearItems(item) { this._platform.clearTimeout(item[2]); } }); enifed("backburner/binary-search", ["exports"], function (exports) { "use strict"; exports.default = binarySearch; function binarySearch(time, timers) { var start = 0; var end = timers.length - 2; var 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; } }); enifed('backburner/deferred-action-queues', ['exports', 'backburner/utils', 'backburner/queue'], function (exports, _backburnerUtils, _backburnerQueue) { 'use strict'; exports.default = DeferredActionQueues; function DeferredActionQueues(queueNames, options) { var queues = this.queues = {}; this.queueNames = queueNames = queueNames || []; this.options = options; _backburnerUtils.each(queueNames, function (queueName) { queues[queueName] = new _backburnerQueue.default(queueName, options[queueName], options); }); } function noSuchQueue(name) { throw new Error('You attempted to schedule an action in a queue (' + name + ') that doesn\'t exist'); } function noSuchMethod(name) { throw new Error('You attempted to schedule an action in a queue (' + name + ') for a method that doesn\'t exist'); } DeferredActionQueues.prototype = { schedule: function (name, target, method, args, onceFlag, stack) { var queues = this.queues; var queue = queues[name]; if (!queue) { noSuchQueue(name); } if (!method) { noSuchMethod(name); } if (onceFlag) { return queue.pushUnique(target, method, args, stack); } else { return queue.push(target, method, args, stack); } }, flush: function () { var queues = this.queues; var queueNames = this.queueNames; var queueName, queue; var queueNameIndex = 0; var numberOfQueues = queueNames.length; while (queueNameIndex < numberOfQueues) { queueName = queueNames[queueNameIndex]; queue = queues[queueName]; var numberOfQueueItems = queue._queue.length; if (numberOfQueueItems === 0) { queueNameIndex++; } else { queue.flush(false /* async */); queueNameIndex = 0; } } } }; }); enifed('backburner/platform', ['exports'], function (exports) { 'use strict'; var GlobalContext; /* global self */ if (typeof self === 'object') { GlobalContext = self; /* global global */ } else if (typeof global === 'object') { GlobalContext = global; /* global window */ } else if (typeof window === 'object') { GlobalContext = window; } else { throw new Error('no global: `self`, `global` nor `window` was found'); } exports.default = GlobalContext; }); enifed('backburner/queue', ['exports', 'backburner/utils'], function (exports, _backburnerUtils) { 'use strict'; exports.default = Queue; function Queue(name, options, globalOptions) { this.name = name; this.globalOptions = globalOptions || {}; this.options = options; this._queue = []; this.targetQueues = {}; this._queueBeingFlushed = undefined; } Queue.prototype = { push: function (target, method, args, stack) { var queue = this._queue; queue.push(target, method, args, stack); return { queue: this, target: target, method: method }; }, pushUniqueWithoutGuid: function (target, method, args, stack) { var queue = this._queue; for (var i = 0, l = queue.length; i < l; i += 4) { var currentTarget = queue[i]; var currentMethod = queue[i + 1]; if (currentTarget === target && currentMethod === method) { queue[i + 2] = args; // replace args queue[i + 3] = stack; // replace stack return; } } queue.push(target, method, args, stack); }, targetQueue: function (targetQueue, target, method, args, stack) { var queue = this._queue; for (var i = 0, l = targetQueue.length; i < l; i += 2) { var currentMethod = targetQueue[i]; var currentIndex = targetQueue[i + 1]; if (currentMethod === method) { queue[currentIndex + 2] = args; // replace args queue[currentIndex + 3] = stack; // replace stack return; } } targetQueue.push(method, queue.push(target, method, args, stack) - 4); }, pushUniqueWithGuid: function (guid, target, method, args, stack) { var hasLocalQueue = this.targetQueues[guid]; if (hasLocalQueue) { this.targetQueue(hasLocalQueue, target, method, args, stack); } else { this.targetQueues[guid] = [method, this._queue.push(target, method, args, stack) - 4]; } return { queue: this, target: target, method: method }; }, pushUnique: function (target, method, args, stack) { var KEY = this.globalOptions.GUID_KEY; if (target && KEY) { var guid = target[KEY]; if (guid) { return this.pushUniqueWithGuid(guid, target, method, args, stack); } } this.pushUniqueWithoutGuid(target, method, args, stack); return { queue: this, target: target, method: method }; }, invoke: function (target, method, args, _, _errorRecordedForStack) { if (args && args.length > 0) { method.apply(target, args); } else { method.call(target); } }, invokeWithOnError: function (target, method, args, onError, errorRecordedForStack) { try { if (args && args.length > 0) { method.apply(target, args); } else { method.call(target); } } catch (error) { onError(error, errorRecordedForStack); } }, flush: function (sync) { var queue = this._queue; var length = queue.length; if (length === 0) { return; } var globalOptions = this.globalOptions; var options = this.options; var before = options && options.before; var after = options && options.after; var onError = globalOptions.onError || globalOptions.onErrorTarget && globalOptions.onErrorTarget[globalOptions.onErrorMethod]; var target, method, args, errorRecordedForStack; var invoke = onError ? this.invokeWithOnError : this.invoke; this.targetQueues = Object.create(null); var queueItems = this._queueBeingFlushed = this._queue.slice(); this._queue = []; if (before) { before(); } for (var i = 0; i < length; i += 4) { target = queueItems[i]; method = queueItems[i + 1]; args = queueItems[i + 2]; errorRecordedForStack = queueItems[i + 3]; // Debugging assistance if (_backburnerUtils.isString(method)) { method = target[method]; } // method could have been nullified / canceled during flush if (method) { // // ** Attention intrepid developer ** // // To find out the stack of this task when it was scheduled onto // the run loop, add the following to your app.js: // // Ember.run.backburner.DEBUG = true; // NOTE: This slows your app, don't leave it on in production. // // Once that is in place, when you are at a breakpoint and navigate // here in the stack explorer, you can look at `errorRecordedForStack.stack`, // which will be the captured stack when this job was scheduled. // invoke(target, method, args, onError, errorRecordedForStack); } } if (after) { after(); } this._queueBeingFlushed = undefined; if (sync !== false && this._queue.length > 0) { // check if new items have been added this.flush(true); } }, cancel: function (actionToCancel) { var queue = this._queue, currentTarget, currentMethod, i, l; var target = actionToCancel.target; var method = actionToCancel.method; var GUID_KEY = this.globalOptions.GUID_KEY; if (GUID_KEY && this.targetQueues && target) { var targetQueue = this.targetQueues[target[GUID_KEY]]; if (targetQueue) { for (i = 0, l = targetQueue.length; i < l; i++) { if (targetQueue[i] === method) { targetQueue.splice(i, 1); } } } } for (i = 0, l = queue.length; i < l; i += 4) { currentTarget = queue[i]; currentMethod = queue[i + 1]; if (currentTarget === target && currentMethod === 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 === target && currentMethod === method) { // don't mess with array during flush // just nullify the method queue[i + 1] = null; return true; } } } }; }); enifed('backburner/utils', ['exports'], function (exports) { 'use strict'; exports.each = each; exports.isString = isString; exports.isFunction = isFunction; exports.isNumber = isNumber; exports.isCoercableNumber = isCoercableNumber; var NUMBER = /\d+/; function each(collection, callback) { for (var i = 0; i < collection.length; i++) { callback(collection[i]); } } function isString(suspect) { return typeof suspect === 'string'; } function isFunction(suspect) { return typeof suspect === 'function'; } function isNumber(suspect) { return typeof suspect === 'number'; } function isCoercableNumber(number) { return isNumber(number) || NUMBER.test(number); } }); enifed('container/container', ['exports', 'ember-environment', 'ember-metal/debug', 'ember-metal/dictionary', 'container/owner', 'ember-runtime/mixins/container_proxy', 'ember-metal/symbol'], function (exports, _emberEnvironment, _emberMetalDebug, _emberMetalDictionary, _containerOwner, _emberRuntimeMixinsContainer_proxy, _emberMetalSymbol) { 'use strict'; exports.default = Container; var CONTAINER_OVERRIDE = _emberMetalSymbol.default('CONTAINER_OVERRIDE'); /** A container used to instantiate and cache objects. Every `Container` must be associated with a `Registry`, which is referenced to determine the factory and options that should be used to instantiate objects. The public API for `Container` is still in flux and should not be considered stable. @private @class Container */ function Container(registry, options) { this.registry = registry; this.owner = options && options.owner ? options.owner : null; this.cache = _emberMetalDictionary.default(options && options.cache ? options.cache : null); this.factoryCache = _emberMetalDictionary.default(options && options.factoryCache ? options.factoryCache : null); this.validationCache = _emberMetalDictionary.default(options && options.validationCache ? options.validationCache : null); this._fakeContainerToInject = _emberRuntimeMixinsContainer_proxy.buildFakeContainerWithDeprecations(this); this[CONTAINER_OVERRIDE] = undefined; this.isDestroyed = false; } Container.prototype = { /** @private @property owner @type Object */ owner: null, /** @private @property registry @type Registry @since 1.11.0 */ registry: null, /** @private @property cache @type InheritingDict */ cache: null, /** @private @property factoryCache @type InheritingDict */ factoryCache: null, /** @private @property validationCache @type InheritingDict */ validationCache: null, /** 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 let registry = new Registry(); let container = registry.container(); registry.register('api:twitter', Twitter); let twitter = container.lookup('api:twitter'); twitter instanceof Twitter; // => true // by default the container will return singletons let 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 let registry = new Registry(); let container = registry.container(); registry.register('api:twitter', Twitter); let twitter = container.lookup('api:twitter', { singleton: false }); let twitter2 = container.lookup('api:twitter', { singleton: false }); twitter === twitter2; //=> false ``` @private @method lookup @param {String} fullName @param {Object} [options] @param {String} [options.source] The fullname of the request source (used for local lookup) @return {any} */ lookup: function (fullName, options) { _emberMetalDebug.assert('fullName must be a proper full name', this.registry.validateFullName(fullName)); return lookup(this, this.registry.normalize(fullName), options); }, /** Given a fullName, return the corresponding factory. @private @method lookupFactory @param {String} fullName @param {Object} [options] @param {String} [options.source] The fullname of the request source (used for local lookup) @return {any} */ lookupFactory: function (fullName, options) { _emberMetalDebug.assert('fullName must be a proper full name', this.registry.validateFullName(fullName)); return factoryFor(this, this.registry.normalize(fullName), options); }, /** A depth first traversal, destroying the container, its descendant containers and all their managed objects. @private @method destroy */ destroy: function () { eachDestroyable(this, function (item) { if (item.destroy) { item.destroy(); } }); this.isDestroyed = true; }, /** Clear either the entire cache or just the cache for a particular key. @private @method reset @param {String} fullName optional key to reset; if missing, resets everything */ reset: function (fullName) { if (arguments.length > 0) { resetMember(this, this.registry.normalize(fullName)); } else { resetCache(this); } }, /** Returns an object that can be used to provide an owner to a manually created instance. @private @method ownerInjection @returns { Object } */ ownerInjection: function () { var _ref; return _ref = {}, _ref[_containerOwner.OWNER] = this.owner, _ref; } }; function isSingleton(container, fullName) { return container.registry.getOption(fullName, 'singleton') !== false; } function lookup(container, fullName) { var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; if (options.source) { fullName = container.registry.expandLocalLookup(fullName, options); // if expandLocalLookup returns falsey, we do not support local lookup if (!fullName) { return; } } if (container.cache[fullName] !== undefined && options.singleton !== false) { return container.cache[fullName]; } var value = instantiate(container, fullName); if (value === undefined) { return; } if (isSingleton(container, fullName) && options.singleton !== false) { container.cache[fullName] = value; } return value; } function markInjectionsAsDynamic(injections) { injections._dynamic = true; } function areInjectionsDynamic(injections) { return !!injections._dynamic; } function buildInjections() /* container, ...injections */{ var hash = {}; if (arguments.length > 1) { var container = arguments[0]; var injections = []; var injection = undefined; for (var i = 1; i < arguments.length; i++) { if (arguments[i]) { injections = injections.concat(arguments[i]); } } container.registry.validateInjections(injections); for (var i = 0; i < injections.length; i++) { injection = injections[i]; hash[injection.property] = lookup(container, injection.fullName); if (!isSingleton(container, injection.fullName)) { markInjectionsAsDynamic(hash); } } } return hash; } function factoryFor(container, fullName) { var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; var registry = container.registry; if (options.source) { fullName = registry.expandLocalLookup(fullName, options); // if expandLocalLookup returns falsey, we do not support local lookup if (!fullName) { return; } } var cache = container.factoryCache; if (cache[fullName]) { return cache[fullName]; } var factory = registry.resolve(fullName); if (factory === undefined) { return; } var type = fullName.split(':')[0]; if (!factory || typeof factory.extend !== 'function' || !_emberEnvironment.ENV.MODEL_FACTORY_INJECTIONS && type === 'model') { if (factory && typeof factory._onLookup === 'function') { factory._onLookup(fullName); } // TODO: think about a 'safe' merge style extension // for now just fallback to create time injection cache[fullName] = factory; return factory; } else { var injections = injectionsFor(container, fullName); var factoryInjections = factoryInjectionsFor(container, fullName); var cacheable = !areInjectionsDynamic(injections) && !areInjectionsDynamic(factoryInjections); factoryInjections._toString = registry.makeToString(factory, fullName); var injectedFactory = factory.extend(injections); // TODO - remove all `container` injections when Ember reaches v3.0.0 injectDeprecatedContainer(injectedFactory.prototype, container); injectedFactory.reopenClass(factoryInjections); if (factory && typeof factory._onLookup === 'function') { factory._onLookup(fullName); } if (cacheable) { cache[fullName] = injectedFactory; } return injectedFactory; } } function injectionsFor(container, fullName) { var registry = container.registry; var splitName = fullName.split(':'); var type = splitName[0]; var injections = buildInjections(container, registry.getTypeInjections(type), registry.getInjections(fullName)); injections._debugContainerKey = fullName; _containerOwner.setOwner(injections, container.owner); return injections; } function factoryInjectionsFor(container, fullName) { var registry = container.registry; var splitName = fullName.split(':'); var type = splitName[0]; var factoryInjections = buildInjections(container, registry.getFactoryTypeInjections(type), registry.getFactoryInjections(fullName)); factoryInjections._debugContainerKey = fullName; return factoryInjections; } function instantiate(container, fullName) { var factory = factoryFor(container, fullName); var lazyInjections = undefined, validationCache = undefined; if (container.registry.getOption(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.'); } validationCache = container.validationCache; _emberMetalDebug.runInDebug(function () { // Ensure that all lazy injections are valid at instantiation time if (!validationCache[fullName] && typeof factory._lazyInjections === 'function') { lazyInjections = factory._lazyInjections(); lazyInjections = container.registry.normalizeInjectionsHash(lazyInjections); container.registry.validateInjections(lazyInjections); } }); validationCache[fullName] = true; var obj = undefined; if (typeof factory.extend === 'function') { // assume the factory was extendable and is already injected obj = 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 var injections = injectionsFor(container, fullName); // Ensure that a container is available to an object during instantiation. // TODO - remove when Ember reaches v3.0.0 // This "fake" container will be replaced after instantiation with a // property that raises deprecations every time it is accessed. injections.container = container._fakeContainerToInject; obj = factory.create(injections); // TODO - remove when Ember reaches v3.0.0 if (!Object.isFrozen(obj) && 'container' in obj) { injectDeprecatedContainer(obj, container); } } return obj; } } // TODO - remove when Ember reaches v3.0.0 function injectDeprecatedContainer(object, container) { Object.defineProperty(object, 'container', { configurable: true, enumerable: false, get: function () { _emberMetalDebug.deprecate('Using the injected `container` is deprecated. Please use the `getOwner` helper instead to access the owner of this object.', false, { id: 'ember-application.injected-container', until: '3.0.0', url: 'http://emberjs.com/deprecations/v2.x#toc_injected-container-access' }); return this[CONTAINER_OVERRIDE] || container; }, set: function (value) { _emberMetalDebug.deprecate('Providing the `container` property to ' + this + ' is deprecated. Please use `Ember.setOwner` or `owner.ownerInjection()` instead to provide an owner to the instance being created.', false, { id: 'ember-application.injected-container', until: '3.0.0', url: 'http://emberjs.com/deprecations/v2.x#toc_injected-container-access' }); this[CONTAINER_OVERRIDE] = value; return value; } }); } function eachDestroyable(container, callback) { var cache = container.cache; var keys = Object.keys(cache); for (var i = 0; i < keys.length; i++) { var key = keys[i]; var value = cache[key]; if (container.registry.getOption(key, 'instantiate') !== false) { callback(value); } } } function resetCache(container) { eachDestroyable(container, function (value) { if (value.destroy) { value.destroy(); } }); container.cache.dict = _emberMetalDictionary.default(null); } function resetMember(container, fullName) { var member = container.cache[fullName]; delete container.factoryCache[fullName]; if (member) { delete container.cache[fullName]; if (member.destroy) { member.destroy(); } } } }); enifed('container/index', ['exports', 'container/registry', 'container/container', 'container/owner'], function (exports, _containerRegistry, _containerContainer, _containerOwner) { /* 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 */ 'use strict'; exports.Registry = _containerRegistry.default; exports.Container = _containerContainer.default; exports.getOwner = _containerOwner.getOwner; exports.setOwner = _containerOwner.setOwner; }); enifed('container/owner', ['exports', 'ember-metal/symbol'], function (exports, _emberMetalSymbol) { /** @module ember @submodule ember-runtime */ 'use strict'; exports.getOwner = getOwner; exports.setOwner = setOwner; var OWNER = _emberMetalSymbol.default('OWNER'); exports.OWNER = OWNER; /** Framework objects in an Ember application (components, services, routes, etc.) are created via a factory and dependency injection system. Each of these objects is the responsibility of an "owner", which handled its instantiation and manages its lifetime. `getOwner` fetches the owner object responsible for an instance. This can be used to lookup or resolve other class instances, or register new factories into the owner. For example, this component dynamically looks up a service based on the `audioType` passed as an attribute: ``` // app/components/play-audio.js import Ember from 'ember'; // Usage: // // {{play-audio audioType=model.audioType audioFile=model.file}} // export default Ember.Component.extend({ audioService: Ember.computed('audioType', function() { let owner = Ember.getOwner(this); return owner.lookup(`service:${this.get('audioType')}`); }), click() { let player = this.get('audioService'); player.play(this.get('audioFile')); } }); ``` @method getOwner @for Ember @param {Object} object An object with an owner. @return {Object} An owner object. @since 2.3.0 @public */ function getOwner(object) { return object[OWNER]; } /** `setOwner` forces a new owner on a given object instance. This is primarily useful in some testing cases. @method setOwner @for Ember @param {Object} object An object with an owner. @return {Object} An owner object. @since 2.3.0 @public */ function setOwner(object, owner) { object[OWNER] = owner; } }); enifed('container/registry', ['exports', 'ember-metal/debug', 'ember-metal/dictionary', 'ember-metal/empty_object', 'ember-metal/assign', 'container/container', 'ember-metal/utils'], function (exports, _emberMetalDebug, _emberMetalDictionary, _emberMetalEmpty_object, _emberMetalAssign, _containerContainer, _emberMetalUtils) { 'use strict'; exports.default = Registry; exports.privatize = privatize; var VALID_FULL_NAME_REGEXP = /^[^:]+:[^:]+$/; /** A registry used to store factory and option information keyed by type. A `Registry` stores the factory and option information needed by a `Container` to instantiate and cache objects. The API for `Registry` is still in flux and should not be considered stable. @private @class Registry @since 1.11.0 */ function Registry(options) { this.fallback = options && options.fallback ? options.fallback : null; if (options && options.resolver) { this.resolver = options.resolver; if (typeof this.resolver === 'function') { deprecateResolverFunction(this); } } this.registrations = _emberMetalDictionary.default(options && options.registrations ? options.registrations : null); this._typeInjections = _emberMetalDictionary.default(null); this._injections = _emberMetalDictionary.default(null); this._factoryTypeInjections = _emberMetalDictionary.default(null); this._factoryInjections = _emberMetalDictionary.default(null); this._localLookupCache = new _emberMetalEmpty_object.default(); this._normalizeCache = _emberMetalDictionary.default(null); this._resolveCache = _emberMetalDictionary.default(null); this._failCache = _emberMetalDictionary.default(null); this._options = _emberMetalDictionary.default(null); this._typeOptions = _emberMetalDictionary.default(null); } Registry.prototype = { /** A backup registry for resolving registrations when no matches can be found. @private @property fallback @type Registry */ fallback: null, /** An object that has a `resolve` method that resolves a name. @private @property resolver @type Resolver */ resolver: null, /** @private @property registrations @type InheritingDict */ registrations: null, /** @private @property _typeInjections @type InheritingDict */ _typeInjections: null, /** @private @property _injections @type InheritingDict */ _injections: null, /** @private @property _factoryTypeInjections @type InheritingDict */ _factoryTypeInjections: null, /** @private @property _factoryInjections @type InheritingDict */ _factoryInjections: null, /** @private @property _normalizeCache @type InheritingDict */ _normalizeCache: null, /** @private @property _resolveCache @type InheritingDict */ _resolveCache: null, /** @private @property _options @type InheritingDict */ _options: null, /** @private @property _typeOptions @type InheritingDict */ _typeOptions: null, /** Creates a container based on this registry. @private @method container @param {Object} options @return {Container} created container */ container: function (options) { return new _containerContainer.default(this, options); }, /** Registers a factory for later injection. Example: ```javascript let registry = new Registry(); registry.register('model:user', Person, {singleton: false }); registry.register('fruit:favorite', Orange); registry.register('communication:main', Email, {singleton: false}); ``` @private @method register @param {String} fullName @param {Function} factory @param {Object} options */ register: function (fullName, factory) { var options = arguments.length <= 2 || arguments[2] === undefined ? {} : arguments[2]; _emberMetalDebug.assert('fullName must be a proper full name', this.validateFullName(fullName)); if (factory === undefined) { throw new TypeError('Attempting to register an unknown factory: \'' + fullName + '\''); } var normalizedName = this.normalize(fullName); if (this._resolveCache[normalizedName]) { throw new Error('Cannot re-register: \'' + fullName + '\', as it has already been resolved.'); } delete this._failCache[normalizedName]; this.registrations[normalizedName] = factory; this._options[normalizedName] = options; }, /** Unregister a fullName ```javascript let registry = new Registry(); registry.register('model:user', User); registry.resolve('model:user').create() instanceof User //=> true registry.unregister('model:user') registry.resolve('model:user') === undefined //=> true ``` @private @method unregister @param {String} fullName */ unregister: function (fullName) { _emberMetalDebug.assert('fullName must be a proper full name', this.validateFullName(fullName)); var normalizedName = this.normalize(fullName); this._localLookupCache = new _emberMetalEmpty_object.default(); delete this.registrations[normalizedName]; delete this._resolveCache[normalizedName]; delete this._failCache[normalizedName]; delete this._options[normalizedName]; }, /** Given a fullName return the corresponding factory. By default `resolve` will retrieve the factory from the registry. ```javascript let registry = new Registry(); registry.register('api:twitter', Twitter); registry.resolve('api:twitter') // => Twitter ``` Optionally the registry 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 let registry = new Registry(); registry.resolver = function(fullName) { // lookup via the module system of choice }; // the twitter factory is added to the module system registry.resolve('api:twitter') // => Twitter ``` @private @method resolve @param {String} fullName @param {Object} [options] @param {String} [options.source] the fullname of the request source (used for local lookups) @return {Function} fullName's factory */ resolve: function (fullName, options) { _emberMetalDebug.assert('fullName must be a proper full name', this.validateFullName(fullName)); var factory = resolve(this, this.normalize(fullName), options); if (factory === undefined && this.fallback) { var _fallback; factory = (_fallback = this.fallback).resolve.apply(_fallback, arguments); } return factory; }, /** 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`. @private @method describe @param {String} fullName @return {string} described fullName */ describe: function (fullName) { if (this.resolver && this.resolver.lookupDescription) { return this.resolver.lookupDescription(fullName); } else if (this.fallback) { return this.fallback.describe(fullName); } else { return fullName; } }, /** A hook to enable custom fullName normalization behaviour @private @method normalizeFullName @param {String} fullName @return {string} normalized fullName */ normalizeFullName: function (fullName) { if (this.resolver && this.resolver.normalize) { return this.resolver.normalize(fullName); } else if (this.fallback) { return this.fallback.normalizeFullName(fullName); } else { return fullName; } }, /** Normalize a fullName based on the application's conventions @private @method normalize @param {String} fullName @return {string} normalized fullName */ normalize: function (fullName) { return this._normalizeCache[fullName] || (this._normalizeCache[fullName] = this.normalizeFullName(fullName)); }, /** @method makeToString @private @param {any} factory @param {string} fullName @return {function} toString function */ makeToString: function (factory, fullName) { if (this.resolver && this.resolver.makeToString) { return this.resolver.makeToString(factory, fullName); } else if (this.fallback) { return this.fallback.makeToString(factory, fullName); } else { return factory.toString(); } }, /** Given a fullName check if the container is aware of its factory or singleton instance. @private @method has @param {String} fullName @param {Object} [options] @param {String} [options.source] the fullname of the request source (used for local lookups) @return {Boolean} */ has: function (fullName, options) { if (!this.isValidFullName(fullName)) { return false; } var source = options && options.source && this.normalize(options.source); return has(this, this.normalize(fullName), source); }, /** Allow registering options for all factories of a type. ```javascript let registry = new Registry(); let container = registry.container(); // if all of type `connection` must not be singletons registry.optionsForType('connection', { singleton: false }); registry.register('connection:twitter', TwitterConnection); registry.register('connection:facebook', FacebookConnection); let twitter = container.lookup('connection:twitter'); let twitter2 = container.lookup('connection:twitter'); twitter === twitter2; // => false let facebook = container.lookup('connection:facebook'); let facebook2 = container.lookup('connection:facebook'); facebook === facebook2; // => false ``` @private @method optionsForType @param {String} type @param {Object} options */ optionsForType: function (type, options) { this._typeOptions[type] = options; }, getOptionsForType: function (type) { var optionsForType = this._typeOptions[type]; if (optionsForType === undefined && this.fallback) { optionsForType = this.fallback.getOptionsForType(type); } return optionsForType; }, /** @private @method options @param {String} fullName @param {Object} options */ options: function (fullName) { var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; var normalizedName = this.normalize(fullName); this._options[normalizedName] = options; }, getOptions: function (fullName) { var normalizedName = this.normalize(fullName); var options = this._options[normalizedName]; if (options === undefined && this.fallback) { options = this.fallback.getOptions(fullName); } return options; }, getOption: function (fullName, optionName) { var options = this._options[fullName]; if (options && options[optionName] !== undefined) { return options[optionName]; } var type = fullName.split(':')[0]; options = this._typeOptions[type]; if (options && options[optionName] !== undefined) { return options[optionName]; } else if (this.fallback) { return this.fallback.getOption(fullName, optionName); } }, /** 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 let registry = new Registry(); let container = registry.container(); registry.register('router:main', Router); registry.register('controller:user', UserController); registry.register('controller:post', PostController); registry.typeInjection('controller', 'router', 'router:main'); let user = container.lookup('controller:user'); let 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) { _emberMetalDebug.assert('fullName must be a proper full name', this.validateFullName(fullName)); var fullNameType = fullName.split(':')[0]; if (fullNameType === type) { throw new Error('Cannot inject a \'' + fullName + '\' on other ' + type + '(s).'); } var injections = this._typeInjections[type] || (this._typeInjections[type] = []); injections.push({ property: property, fullName: 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 let registry = new Registry(); let container = registry.container(); registry.register('source:main', Source); registry.register('model:user', User); registry.register('model:post', Post); // injecting one fullName on another fullName // eg. each user model gets a post model registry.injection('model:user', 'post', 'model:post'); // injecting one fullName on another type registry.injection('model', 'source', 'source:main'); let user = container.lookup('model:user'); let 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 ``` @private @method injection @param {String} factoryName @param {String} property @param {String} injectionName */ injection: function (fullName, property, injectionName) { this.validateFullName(injectionName); var normalizedInjectionName = this.normalize(injectionName); if (fullName.indexOf(':') === -1) { return this.typeInjection(fullName, property, normalizedInjectionName); } _emberMetalDebug.assert('fullName must be a proper full name', this.validateFullName(fullName)); var normalizedName = this.normalize(fullName); var injections = this._injections[normalizedName] || (this._injections[normalizedName] = []); injections.push({ property: property, fullName: 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 let registry = new Registry(); registry.register('store:main', SomeStore); registry.factoryTypeInjection('model', 'store', 'store:main'); let store = registry.lookup('store:main'); let UserFactory = registry.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) { var injections = this._factoryTypeInjections[type] || (this._factoryTypeInjections[type] = []); injections.push({ property: property, fullName: this.normalize(fullName) }); }, /** Defines factory injection rules. Similar to regular injection rules, but are run against factories, via `Registry#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 let registry = new Registry(); let container = registry.container(); registry.register('store:main', Store); registry.register('store:secondary', OtherStore); registry.register('model:user', User); registry.register('model:post', Post); // injecting one fullName on another type registry.factoryInjection('model', 'store', 'store:main'); // injecting one fullName on another fullName registry.factoryInjection('model:post', 'secondaryStore', 'store:secondary'); let UserFactory = container.lookupFactory('model:user'); let PostFactory = container.lookupFactory('model:post'); let 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 ``` @private @method factoryInjection @param {String} factoryName @param {String} property @param {String} injectionName */ factoryInjection: function (fullName, property, injectionName) { var normalizedName = this.normalize(fullName); var normalizedInjectionName = this.normalize(injectionName); this.validateFullName(injectionName); if (fullName.indexOf(':') === -1) { return this.factoryTypeInjection(normalizedName, property, normalizedInjectionName); } var injections = this._factoryInjections[normalizedName] || (this._factoryInjections[normalizedName] = []); injections.push({ property: property, fullName: normalizedInjectionName }); }, /** @private @method knownForType @param {String} type the type to iterate over */ knownForType: function (type) { var fallbackKnown = undefined, resolverKnown = undefined; var localKnown = _emberMetalDictionary.default(null); var registeredNames = Object.keys(this.registrations); for (var index = 0; index < registeredNames.length; index++) { var fullName = registeredNames[index]; var itemType = fullName.split(':')[0]; if (itemType === type) { localKnown[fullName] = true; } } if (this.fallback) { fallbackKnown = this.fallback.knownForType(type); } if (this.resolver && this.resolver.knownForType) { resolverKnown = this.resolver.knownForType(type); } return _emberMetalAssign.default({}, fallbackKnown, localKnown, resolverKnown); }, validateFullName: function (fullName) { if (!this.isValidFullName(fullName)) { throw new TypeError('Invalid Fullname, expected: \'type:name\' got: ' + fullName); } return true; }, isValidFullName: function (fullName) { return !!VALID_FULL_NAME_REGEXP.test(fullName); }, validateInjections: function (injections) { if (!injections) { return; } var fullName = undefined; for (var i = 0; i < injections.length; i++) { fullName = injections[i].fullName; if (!this.has(fullName)) { throw new Error('Attempting to inject an unknown injection: \'' + fullName + '\''); } } }, normalizeInjectionsHash: function (hash) { var injections = []; for (var key in hash) { if (hash.hasOwnProperty(key)) { _emberMetalDebug.assert('Expected a proper full name, given \'' + hash[key] + '\'', this.validateFullName(hash[key])); injections.push({ property: key, fullName: hash[key] }); } } return injections; }, getInjections: function (fullName) { var injections = this._injections[fullName] || []; if (this.fallback) { injections = injections.concat(this.fallback.getInjections(fullName)); } return injections; }, getTypeInjections: function (type) { var injections = this._typeInjections[type] || []; if (this.fallback) { injections = injections.concat(this.fallback.getTypeInjections(type)); } return injections; }, getFactoryInjections: function (fullName) { var injections = this._factoryInjections[fullName] || []; if (this.fallback) { injections = injections.concat(this.fallback.getFactoryInjections(fullName)); } return injections; }, getFactoryTypeInjections: function (type) { var injections = this._factoryTypeInjections[type] || []; if (this.fallback) { injections = injections.concat(this.fallback.getFactoryTypeInjections(type)); } return injections; } }; function deprecateResolverFunction(registry) { _emberMetalDebug.deprecate('Passing a `resolver` function into a Registry is deprecated. Please pass in a Resolver object with a `resolve` method.', false, { id: 'ember-application.registry-resolver-as-function', until: '3.0.0', url: 'http://emberjs.com/deprecations/v2.x#toc_registry-resolver-as-function' }); registry.resolver = { resolve: registry.resolver }; } /** Given a fullName and a source fullName returns the fully resolved fullName. Used to allow for local lookup. ```javascript let registry = new Registry(); // the twitter factory is added to the module system registry.expandLocalLookup('component:post-title', { source: 'template:post' }) // => component:post/post-title ``` @private @method expandLocalLookup @param {String} fullName @param {Object} [options] @param {String} [options.source] the fullname of the request source (used for local lookups) @return {String} fullName */ Registry.prototype.expandLocalLookup = function Registry_expandLocalLookup(fullName, options) { if (this.resolver && this.resolver.expandLocalLookup) { _emberMetalDebug.assert('fullName must be a proper full name', this.validateFullName(fullName)); _emberMetalDebug.assert('options.source must be provided to expandLocalLookup', options && options.source); _emberMetalDebug.assert('options.source must be a proper full name', this.validateFullName(options.source)); var normalizedFullName = this.normalize(fullName); var normalizedSource = this.normalize(options.source); return expandLocalLookup(this, normalizedFullName, normalizedSource); } else if (this.fallback) { return this.fallback.expandLocalLookup(fullName, options); } else { return null; } }; function expandLocalLookup(registry, normalizedName, normalizedSource) { var cache = registry._localLookupCache; var normalizedNameCache = cache[normalizedName]; if (!normalizedNameCache) { normalizedNameCache = cache[normalizedName] = new _emberMetalEmpty_object.default(); } var cached = normalizedNameCache[normalizedSource]; if (cached !== undefined) { return cached; } var expanded = registry.resolver.expandLocalLookup(normalizedName, normalizedSource); return normalizedNameCache[normalizedSource] = expanded; } function resolve(registry, normalizedName, options) { if (options && options.source) { // when `source` is provided expand normalizedName // and source into the full normalizedName normalizedName = registry.expandLocalLookup(normalizedName, options); // if expandLocalLookup returns falsey, we do not support local lookup if (!normalizedName) { return; } } var cached = registry._resolveCache[normalizedName]; if (cached !== undefined) { return cached; } if (registry._failCache[normalizedName]) { return; } var resolved = undefined; if (registry.resolver) { resolved = registry.resolver.resolve(normalizedName); } if (resolved === undefined) { resolved = registry.registrations[normalizedName]; } if (resolved === undefined) { registry._failCache[normalizedName] = true; } else { registry._resolveCache[normalizedName] = resolved; } return resolved; } function has(registry, fullName, source) { return registry.resolve(fullName, { source: source }) !== undefined; } var privateNames = _emberMetalDictionary.default(null); var privateSuffix = '' + Math.random() + Date.now(); function privatize(_ref) { var fullName = _ref[0]; var name = privateNames[fullName]; if (name) { return name; } var _fullName$split = fullName.split(':'); var type = _fullName$split[0]; var rawName = _fullName$split[1]; return privateNames[fullName] = _emberMetalUtils.intern(type + ':' + rawName + '-' + privateSuffix); } }); enifed('dag-map', ['exports', 'vertex', 'visit'], function (exports, _vertex, _visit) { 'use strict'; exports.default = DAG; /** * DAG stands for Directed acyclic graph. * * It is used to build a graph of dependencies checking that there isn't circular * dependencies. p.e Registering initializers with a certain precedence order. * * @class DAG * @constructor */ function DAG() { this.names = []; this.vertices = Object.create(null); } /** * Adds a vertex entry to the graph unless it is already added. * * @private * @method add * @param {String} name The name of the vertex to add */ DAG.prototype.add = function (name) { if (!name) { throw new Error("Can't add Vertex without name"); } if (this.vertices[name] !== undefined) { return this.vertices[name]; } var vertex = new _vertex.default(name); this.vertices[name] = vertex; this.names.push(name); return vertex; }; /** * Adds a vertex to the graph and sets its value. * * @private * @method map * @param {String} name The name of the vertex. * @param value The value to put in the vertex. */ DAG.prototype.map = function (name, value) { this.add(name).value = value; }; /** * Connects the vertices with the given names, adding them to the graph if * necessary, only if this does not produce is any circular dependency. * * @private * @method addEdge * @param {String} fromName The name the vertex where the edge starts. * @param {String} toName The name the vertex where the edge ends. */ DAG.prototype.addEdge = function (fromName, toName) { if (!fromName || !toName || fromName === toName) { return; } var from = this.add(fromName); var to = this.add(toName); if (to.incoming.hasOwnProperty(fromName)) { return; } function checkCycle(vertex, path) { if (vertex.name === toName) { throw new Error("cycle detected: " + toName + " <- " + path.join(" <- ")); } } _visit.default(from, checkCycle); from.hasOutgoing = true; to.incoming[fromName] = from; to.incomingNames.push(fromName); }; /** * Visits all the vertex of the graph calling the given function with each one, * ensuring that the vertices are visited respecting their precedence. * * @method topsort * @param {Function} fn The function to be invoked on each vertex. */ DAG.prototype.topsort = function (fn) { var visited = {}; var vertices = this.vertices; var names = this.names; var len = names.length; var i, vertex; for (i = 0; i < len; i++) { vertex = vertices[names[i]]; if (!vertex.hasOutgoing) { _visit.default(vertex, fn, visited); } } }; /** * Adds a vertex with the given name and value to the graph and joins it with the * vertices referenced in _before_ and _after_. If there isn't vertices with those * names, they are added too. * * If either _before_ or _after_ are falsy/empty, the added vertex will not have * an incoming/outgoing edge. * * @method addEdges * @param {String} name The name of the vertex to be added. * @param value The value of that vertex. * @param before An string or array of strings with the names of the vertices before * which this vertex must be visited. * @param after An string or array of strings with the names of the vertex after * which this vertex must be visited. * */ DAG.prototype.addEdges = function (name, value, before, after) { var i; this.map(name, value); if (before) { if (typeof before === 'string') { this.addEdge(name, before); } else { for (i = 0; i < before.length; i++) { this.addEdge(name, before[i]); } } } if (after) { if (typeof after === 'string') { this.addEdge(after, name); } else { for (i = 0; i < after.length; i++) { this.addEdge(after[i], name); } } } }; }); enifed('dag-map.umd', ['exports', 'dag-map/platform', 'dag-map'], function (exports, _dagMapPlatform, _dagMap) { 'use strict'; /* global define:true module:true window: true */ if (typeof define === 'function' && define.amd) { define(function () { return _dagMap.default; }); } else if (typeof module !== 'undefined' && module.exports) { module.exports = _dagMap.default; } else if (typeof _dagMapPlatform.default !== 'undefined') { _dagMapPlatform.default['DAG'] = _dagMap.default; } }); enifed('dag-map/platform', ['exports'], function (exports) { 'use strict'; var platform; /* global self */ if (typeof self === 'object') { platform = self; /* global global */ } else if (typeof global === 'object') { platform = global; } else { throw new Error('no global: `self` or `global` found'); } exports.default = platform; }); enifed("dom-helper", ["exports", "htmlbars-runtime/morph", "morph-attr", "dom-helper/build-html-dom", "dom-helper/classes", "dom-helper/prop"], function (exports, _htmlbarsRuntimeMorph, _morphAttr, _domHelperBuildHtmlDom, _domHelperClasses, _domHelperProp) { /*globals module, URL*/ "use strict"; var doc = typeof document === 'undefined' ? false : document; var deletesBlankTextNodes = doc && (function (document) { var element = document.createElement('div'); element.appendChild(document.createTextNode('')); var clonedElement = element.cloneNode(true); return clonedElement.childNodes.length === 0; })(doc); var ignoresCheckedAttribute = doc && (function (document) { var element = document.createElement('input'); element.setAttribute('checked', 'checked'); var clonedElement = element.cloneNode(false); return !clonedElement.checked; })(doc); var canRemoveSvgViewBoxAttribute = doc && (doc.createElementNS ? (function (document) { var element = document.createElementNS(_domHelperBuildHtmlDom.svgNamespace, 'svg'); element.setAttribute('viewBox', '0 0 100 100'); element.removeAttribute('viewBox'); return !element.getAttribute('viewBox'); })(doc) : true); var canClone = doc && (function (document) { var element = document.createElement('div'); element.appendChild(document.createTextNode(' ')); element.appendChild(document.createTextNode(' ')); var clonedElement = element.cloneNode(true); return clonedElement.childNodes[0].nodeValue === ' '; })(doc); // This is not the namespace of the element, but of // the elements inside that elements. function interiorNamespace(element) { if (element && element.namespaceURI === _domHelperBuildHtmlDom.svgNamespace && !_domHelperBuildHtmlDom.svgHTMLIntegrationPoints[element.tagName]) { return _domHelperBuildHtmlDom.svgNamespace; } else { return null; } } // The HTML spec allows for "omitted start tags". These tags are optional // when their intended child is the first thing in the parent tag. For // example, this is a tbody start tag: // // // // // // The tbody may be omitted, and the browser will accept and render: // //
// // // However, the omitted start tag will still be added to the DOM. Here // we test the string and context to see if the browser is about to // perform this cleanup. // // http://www.whatwg.org/specs/web-apps/current-work/multipage/syntax.html#optional-tags // describes which tags are omittable. The spec for tbody and colgroup // explains this behavior: // // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-tbody-element // http://www.whatwg.org/specs/web-apps/current-work/multipage/tables.html#the-colgroup-element // var omittedStartTagChildTest = /<([\w:]+)/; function detectOmittedStartTag(string, contextualElement) { // Omitted start tags are only inside table tags. if (contextualElement.tagName === 'TABLE') { var omittedStartTagChildMatch = omittedStartTagChildTest.exec(string); if (omittedStartTagChildMatch) { var omittedStartTagChild = omittedStartTagChildMatch[1]; // It is already asserted that the contextual element is a table // and not the proper start tag. Just see if a tag was omitted. return omittedStartTagChild === 'tr' || omittedStartTagChild === 'col'; } } } function buildSVGDOM(html, dom) { var div = dom.document.createElement('div'); div.innerHTML = '' + html + ''; return div.firstChild.childNodes; } var guid = 1; function ElementMorph(element, dom, namespace) { this.element = element; this.dom = dom; this.namespace = namespace; this.guid = "element" + guid++; this._state = undefined; this.isDirty = true; } ElementMorph.prototype.getState = function () { if (!this._state) { this._state = {}; } return this._state; }; ElementMorph.prototype.setState = function (newState) { /*jshint -W093 */ return this._state = newState; }; // renderAndCleanup calls `clear` on all items in the morph map // just before calling `destroy` on the morph. // // As a future refactor this could be changed to set the property // back to its original/default value. ElementMorph.prototype.clear = function () {}; ElementMorph.prototype.destroy = function () { this.element = null; this.dom = null; }; /* * A class wrapping DOM functions to address environment compatibility, * namespaces, contextual elements for morph un-escaped content * insertion. * * When entering a template, a DOMHelper should be passed: * * template(context, { hooks: hooks, dom: new DOMHelper() }); * * TODO: support foreignObject as a passed contextual element. It has * a namespace (svg) that does not match its internal namespace * (xhtml). * * @class DOMHelper * @constructor * @param {HTMLDocument} _document The document DOM methods are proxied to */ function DOMHelper(_document) { this.document = _document || document; if (!this.document) { throw new Error("A document object must be passed to the DOMHelper, or available on the global scope"); } this.canClone = canClone; this.namespace = null; installEnvironmentSpecificMethods(this); } var prototype = DOMHelper.prototype; prototype.constructor = DOMHelper; prototype.getElementById = function (id, rootNode) { rootNode = rootNode || this.document; return rootNode.getElementById(id); }; prototype.insertBefore = function (element, childElement, referenceChild) { return element.insertBefore(childElement, referenceChild); }; prototype.appendChild = function (element, childElement) { return element.appendChild(childElement); }; var itemAt; // It appears that sometimes, in yet to be itentified scenarios PhantomJS 2.0 // crashes on childNodes.item(index), but works as expected with childNodes[index]; // // Although it would be nice to move to childNodes[index] in all scenarios, // this would require SimpleDOM to maintain the childNodes array. This would be // quite costly, in both dev time and runtime. // // So instead, we choose the best possible method and call it a day. // /*global navigator */ if (typeof navigator !== 'undefined' && navigator.userAgent.indexOf('PhantomJS')) { itemAt = function (nodes, index) { return nodes[index]; }; } else { itemAt = function (nodes, index) { return nodes.item(index); }; } prototype.childAt = function (element, indices) { var child = element; for (var i = 0; i < indices.length; i++) { child = itemAt(child.childNodes, indices[i]); } return child; }; // Note to a Fellow Implementor: // Ahh, accessing a child node at an index. Seems like it should be so simple, // doesn't it? Unfortunately, this particular method has caused us a surprising // amount of pain. As you'll note below, this method has been modified to walk // the linked list of child nodes rather than access the child by index // directly, even though there are two (2) APIs in the DOM that do this for us. // If you're thinking to yourself, "What an oversight! What an opportunity to // optimize this code!" then to you I say: stop! For I have a tale to tell. // // First, this code must be compatible with simple-dom for rendering on the // server where there is no real DOM. Previously, we accessed a child node // directly via `element.childNodes[index]`. While we *could* in theory do a // full-fidelity simulation of a live `childNodes` array, this is slow, // complicated and error-prone. // // "No problem," we thought, "we'll just use the similar // `childNodes.item(index)` API." Then, we could just implement our own `item` // method in simple-dom and walk the child node linked list there, allowing // us to retain the performance advantages of the (surely optimized) `item()` // API in the browser. // // Unfortunately, an enterprising soul named Samy Alzahrani discovered that in // IE8, accessing an item out-of-bounds via `item()` causes an exception where // other browsers return null. This necessitated a... check of // `childNodes.length`, bringing us back around to having to support a // full-fidelity `childNodes` array! // // Worst of all, Kris Selden investigated how browsers are actualy implemented // and discovered that they're all linked lists under the hood anyway. Accessing // `childNodes` requires them to allocate a new live collection backed by that // linked list, which is itself a rather expensive operation. Our assumed // optimization had backfired! That is the danger of magical thinking about // the performance of native implementations. // // And this, my friends, is why the following implementation just walks the // linked list, as surprised as that may make you. Please ensure you understand // the above before changing this and submitting a PR. // // Tom Dale, January 18th, 2015, Portland OR prototype.childAtIndex = function (element, index) { var node = element.firstChild; for (var idx = 0; node && idx < index; idx++) { node = node.nextSibling; } return node; }; prototype.appendText = function (element, text) { return element.appendChild(this.document.createTextNode(text)); }; prototype.setAttribute = function (element, name, value) { element.setAttribute(name, String(value)); }; prototype.getAttribute = function (element, name) { return element.getAttribute(name); }; prototype.setAttributeNS = function (element, namespace, name, value) { element.setAttributeNS(namespace, name, String(value)); }; prototype.getAttributeNS = function (element, namespace, name) { return element.getAttributeNS(namespace, name); }; if (canRemoveSvgViewBoxAttribute) { prototype.removeAttribute = function (element, name) { element.removeAttribute(name); }; } else { prototype.removeAttribute = function (element, name) { if (element.tagName === 'svg' && name === 'viewBox') { element.setAttribute(name, null); } else { element.removeAttribute(name); } }; } prototype.setPropertyStrict = function (element, name, value) { if (value === undefined) { value = null; } if (value === null && (name === 'value' || name === 'type' || name === 'src')) { value = ''; } element[name] = value; }; prototype.getPropertyStrict = function (element, name) { return element[name]; }; prototype.setProperty = function (element, name, value, namespace) { if (element.namespaceURI === _domHelperBuildHtmlDom.svgNamespace) { if (_domHelperProp.isAttrRemovalValue(value)) { element.removeAttribute(name); } else { if (namespace) { element.setAttributeNS(namespace, name, value); } else { element.setAttribute(name, value); } } } else { var _normalizeProperty = _domHelperProp.normalizeProperty(element, name); var normalized = _normalizeProperty.normalized; var type = _normalizeProperty.type; if (type === 'prop') { element[normalized] = value; } else { if (_domHelperProp.isAttrRemovalValue(value)) { element.removeAttribute(name); } else { if (namespace && element.setAttributeNS) { element.setAttributeNS(namespace, name, value); } else { element.setAttribute(name, value); } } } } }; if (doc && doc.createElementNS) { // Only opt into namespace detection if a contextualElement // is passed. prototype.createElement = function (tagName, contextualElement) { var namespace = this.namespace; if (contextualElement) { if (tagName === 'svg') { namespace = _domHelperBuildHtmlDom.svgNamespace; } else { namespace = interiorNamespace(contextualElement); } } if (namespace) { return this.document.createElementNS(namespace, tagName); } else { return this.document.createElement(tagName); } }; prototype.setAttributeNS = function (element, namespace, name, value) { element.setAttributeNS(namespace, name, String(value)); }; } else { prototype.createElement = function (tagName) { return this.document.createElement(tagName); }; prototype.setAttributeNS = function (element, namespace, name, value) { element.setAttribute(name, String(value)); }; } prototype.addClasses = _domHelperClasses.addClasses; prototype.removeClasses = _domHelperClasses.removeClasses; prototype.setNamespace = function (ns) { this.namespace = ns; }; prototype.detectNamespace = function (element) { this.namespace = interiorNamespace(element); }; prototype.createDocumentFragment = function () { return this.document.createDocumentFragment(); }; prototype.createTextNode = function (text) { return this.document.createTextNode(text); }; prototype.createComment = function (text) { return this.document.createComment(text); }; prototype.repairClonedNode = function (element, blankChildTextNodes, isChecked) { if (deletesBlankTextNodes && blankChildTextNodes.length > 0) { for (var i = 0, len = blankChildTextNodes.length; i < len; i++) { var textNode = this.document.createTextNode(''), offset = blankChildTextNodes[i], before = this.childAtIndex(element, offset); if (before) { element.insertBefore(textNode, before); } else { element.appendChild(textNode); } } } if (ignoresCheckedAttribute && isChecked) { element.setAttribute('checked', 'checked'); } }; prototype.cloneNode = function (element, deep) { var clone = element.cloneNode(!!deep); return clone; }; prototype.AttrMorphClass = _morphAttr.default; prototype.createAttrMorph = function (element, attrName, namespace) { return this.AttrMorphClass.create(element, attrName, this, namespace); }; prototype.ElementMorphClass = ElementMorph; prototype.createElementMorph = function (element, namespace) { return new this.ElementMorphClass(element, this, namespace); }; prototype.createUnsafeAttrMorph = function (element, attrName, namespace) { var morph = this.createAttrMorph(element, attrName, namespace); morph.escaped = false; return morph; }; prototype.MorphClass = _htmlbarsRuntimeMorph.default; prototype.createMorph = function (parent, start, end, contextualElement) { if (contextualElement && contextualElement.nodeType === 11) { throw new Error("Cannot pass a fragment as the contextual element to createMorph"); } if (!contextualElement && parent && parent.nodeType === 1) { contextualElement = parent; } var morph = new this.MorphClass(this, contextualElement); morph.firstNode = start; morph.lastNode = end; return morph; }; prototype.createFragmentMorph = function (contextualElement) { if (contextualElement && contextualElement.nodeType === 11) { throw new Error("Cannot pass a fragment as the contextual element to createMorph"); } var fragment = this.createDocumentFragment(); return _htmlbarsRuntimeMorph.default.create(this, contextualElement, fragment); }; prototype.replaceContentWithMorph = function (element) { var firstChild = element.firstChild; if (!firstChild) { var comment = this.createComment(''); this.appendChild(element, comment); return _htmlbarsRuntimeMorph.default.create(this, element, comment); } else { var morph = _htmlbarsRuntimeMorph.default.attach(this, element, firstChild, element.lastChild); morph.clear(); return morph; } }; prototype.createUnsafeMorph = function (parent, start, end, contextualElement) { var morph = this.createMorph(parent, start, end, contextualElement); morph.parseTextAsHTML = true; return morph; }; // This helper is just to keep the templates good looking, // passing integers instead of element references. prototype.createMorphAt = function (parent, startIndex, endIndex, contextualElement) { var single = startIndex === endIndex; var start = this.childAtIndex(parent, startIndex); var end = single ? start : this.childAtIndex(parent, endIndex); return this.createMorph(parent, start, end, contextualElement); }; prototype.createUnsafeMorphAt = function (parent, startIndex, endIndex, contextualElement) { var morph = this.createMorphAt(parent, startIndex, endIndex, contextualElement); morph.parseTextAsHTML = true; return morph; }; prototype.insertMorphBefore = function (element, referenceChild, contextualElement) { var insertion = this.document.createComment(''); element.insertBefore(insertion, referenceChild); return this.createMorph(element, insertion, insertion, contextualElement); }; prototype.appendMorph = function (element, contextualElement) { var insertion = this.document.createComment(''); element.appendChild(insertion); return this.createMorph(element, insertion, insertion, contextualElement); }; prototype.insertBoundary = function (fragment, index) { // this will always be null or firstChild var child = index === null ? null : this.childAtIndex(fragment, index); this.insertBefore(fragment, this.createTextNode(''), child); }; prototype.setMorphHTML = function (morph, html) { morph.setHTML(html); }; prototype.parseHTML = function (html, contextualElement) { var childNodes; if (interiorNamespace(contextualElement) === _domHelperBuildHtmlDom.svgNamespace) { childNodes = buildSVGDOM(html, this); } else { var nodes = _domHelperBuildHtmlDom.buildHTMLDOM(html, contextualElement, this); if (detectOmittedStartTag(html, contextualElement)) { var node = nodes[0]; while (node && node.nodeType !== 1) { node = node.nextSibling; } childNodes = node.childNodes; } else { childNodes = nodes; } } // Copy node list to a fragment. var fragment = this.document.createDocumentFragment(); if (childNodes && childNodes.length > 0) { var currentNode = childNodes[0]; // We prepend an