diff --git a/ui/javascripts/libs/ember-1.5.1.js b/ui/javascripts/libs/ember-1.5.1.js deleted file mode 100755 index 4ec7312559..0000000000 --- a/ui/javascripts/libs/ember-1.5.1.js +++ /dev/null @@ -1,44267 +0,0 @@ -/*! - * @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.5.1 - */ - - -(function() { -/*global __fail__*/ - -/** -Ember Debug - -@module ember -@submodule ember-debug -*/ - -/** -@class Ember -*/ - -if ('undefined' === typeof Ember) { - Ember = {}; - - if ('undefined' !== typeof window) { - window.Em = window.Ember = Em = Ember; - } -} - -// This needs to be kept in sync with the logic in -// `packages/ember-metal/lib/core.js`. -// -// This is duplicated here to ensure that `Ember.ENV` -// is setup even if `Ember` is not loaded yet. -if (Ember.ENV) { - // do nothing if Ember.ENV is already setup -} else if ('undefined' !== typeof EmberENV) { - Ember.ENV = EmberENV; -} else if('undefined' !== typeof ENV) { - Ember.ENV = ENV; -} else { - Ember.ENV = {}; -} - -if (!('MANDATORY_SETTER' in Ember.ENV)) { - Ember.ENV.MANDATORY_SETTER = true; // default to true for debug dist -} - -/** - Define an assertion that will throw an exception if the condition is not - met. Ember build tools will remove any calls to `Ember.assert()` when - doing a production build. Example: - - ```javascript - // Test for truthiness - Ember.assert('Must pass a valid object', obj); - // Fail unconditionally - Ember.assert('This code path should never be run') - ``` - - @method assert - @param {String} desc A description of the assertion. This will become - the text of the Error thrown if the assertion fails. - @param {Boolean} test Must be truthy for the assertion to pass. If - falsy, an exception will be thrown. -*/ -Ember.assert = function(desc, test) { - if (!test) { - throw new Ember.Error("Assertion Failed: " + desc); - } -}; - - -/** - Display a warning with the provided message. Ember build tools will - remove any calls to `Ember.warn()` when doing a production build. - - @method warn - @param {String} message A warning to display. - @param {Boolean} test An optional boolean. If falsy, the warning - will be displayed. -*/ -Ember.warn = function(message, test) { - if (!test) { - Ember.Logger.warn("WARNING: "+message); - if ('trace' in Ember.Logger) Ember.Logger.trace(); - } -}; - -/** - Display a debug notice. Ember build tools will remove any calls to - `Ember.debug()` when doing a production build. - - ```javascript - Ember.debug("I'm a debug notice!"); - ``` - - @method debug - @param {String} message A debug message to display. -*/ -Ember.debug = function(message) { - Ember.Logger.debug("DEBUG: "+message); -}; - -/** - Display a deprecation warning with the provided message and a stack trace - (Chrome and Firefox only). Ember build tools will remove any calls to - `Ember.deprecate()` when doing a production build. - - @method deprecate - @param {String} message A description of the deprecation. - @param {Boolean} test An optional boolean. If falsy, the deprecation - will be displayed. -*/ -Ember.deprecate = function(message, test) { - if (test) { return; } - - if (Ember.ENV.RAISE_ON_DEPRECATION) { throw new Ember.Error(message); } - - var error; - - // When using new Error, we can't do the arguments check for Chrome. Alternatives are welcome - try { __fail__.fail(); } catch (e) { error = e; } - - if (Ember.LOG_STACKTRACE_ON_DEPRECATION && error.stack) { - var stack, stackStr = ''; - if (error['arguments']) { - // Chrome - stack = error.stack.replace(/^\s+at\s+/gm, ''). - replace(/^([^\(]+?)([\n$])/gm, '{anonymous}($1)$2'). - replace(/^Object.\s*\(([^\)]+)\)/gm, '{anonymous}($1)').split('\n'); - stack.shift(); - } else { - // Firefox - stack = error.stack.replace(/(?:\n@:0)?\s+$/m, ''). - replace(/^\(/gm, '{anonymous}(').split('\n'); - } - - stackStr = "\n " + stack.slice(2).join("\n "); - message = message + stackStr; - } - - Ember.Logger.warn("DEPRECATION: "+message); -}; - - - -/** - Alias an old, deprecated method with its new counterpart. - - Display a deprecation warning with the provided message and a stack trace - (Chrome and Firefox only) when the assigned method is called. - - Ember build tools will not remove calls to `Ember.deprecateFunc()`, though - no warnings will be shown in production. - - ```javascript - Ember.oldMethod = Ember.deprecateFunc("Please use the new, updated method", Ember.newMethod); - ``` - - @method deprecateFunc - @param {String} message A description of the deprecation. - @param {Function} func The new function called to replace its deprecated counterpart. - @return {Function} a new function that wrapped the original function with a deprecation warning -*/ -Ember.deprecateFunc = function(message, func) { - return function() { - Ember.deprecate(message); - return func.apply(this, arguments); - }; -}; - - -/** - Run a function meant for debugging. Ember build tools will remove any calls to - `Ember.runInDebug()` when doing a production build. - - ```javascript - Ember.runInDebug( function() { - Ember.Handlebars.EachView.reopen({ - didInsertElement: function() { - console.log("I'm happy"); - } - }); - }); - ``` - - @method runInDebug - @param {Function} func The function to be executed. -*/ -Ember.runInDebug = function(func) { - func() -}; - -// Inform the developer about the Ember Inspector if not installed. -if (!Ember.testing) { - var isFirefox = typeof InstallTrigger !== 'undefined'; - var isChrome = !!window.chrome && !window.opera; - - if (typeof window !== 'undefined' && (isFirefox || isChrome) && window.addEventListener) { - window.addEventListener("load", function() { - if (document.documentElement && document.documentElement.dataset && !document.documentElement.dataset.emberExtension) { - var downloadURL; - - if(isChrome) { - downloadURL = 'https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi'; - } else if(isFirefox) { - downloadURL = 'https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/'; - } - - Ember.debug('For more advanced debugging, install the Ember Inspector from ' + downloadURL); - } - }, false); - } -} - -})(); - -/*! - * @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.5.1 - */ - - -(function() { -var define, requireModule, require, requirejs; - -(function() { - var registry = {}, seen = {}; - - define = function(name, deps, callback) { - registry[name] = { deps: deps, callback: callback }; - }; - - requirejs = require = requireModule = function(name) { - requirejs._eak_seen = registry; - - if (seen[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 -1; -}; - -// From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map -var arrayMap = isNativeFunc(Array.prototype.map) ? Array.prototype.map : function(fun /*, thisp */) { - //"use strict"; - - if (this === void 0 || this === null) { - throw new TypeError(); - } - - var t = Object(this); - var len = t.length >>> 0; - if (typeof fun !== "function") { - throw new TypeError(); - } - - var res = new Array(len); - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - if (i in t) { - res[i] = fun.call(thisp, t[i], i, t); - } - } - - return res; -}; - -// From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach -var arrayForEach = isNativeFunc(Array.prototype.forEach) ? Array.prototype.forEach : function(fun /*, thisp */) { - //"use strict"; - - if (this === void 0 || this === null) { - throw new TypeError(); - } - - var t = Object(this); - var len = t.length >>> 0; - if (typeof fun !== "function") { - throw new TypeError(); - } - - var thisp = arguments[1]; - for (var i = 0; i < len; i++) { - if (i in t) { - fun.call(thisp, t[i], i, t); - } - } -}; - -var arrayIndexOf = isNativeFunc(Array.prototype.indexOf) ? Array.prototype.indexOf : function (obj, fromIndex) { - if (fromIndex === null || fromIndex === undefined) { fromIndex = 0; } - else if (fromIndex < 0) { fromIndex = Math.max(0, this.length + fromIndex); } - for (var i = fromIndex, j = this.length; i < j; i++) { - if (this[i] === obj) { return i; } - } - return -1; -}; - -var arrayFilter = isNativeFunc(Array.prototype.filter) ? Array.prototype.filter : function (fn, context) { - var i, - value, - result = [], - length = this.length; - - for (i = 0; i < length; i++) { - if (this.hasOwnProperty(i)) { - value = this[i]; - if (fn.call(context, value, i, this)) { - result.push(value); - } - } - } - return result; -}; - -/** - Array polyfills to support ES5 features in older browsers. - - @namespace Ember - @property ArrayPolyfills -*/ -Ember.ArrayPolyfills = { - map: arrayMap, - forEach: arrayForEach, - filter: arrayFilter, - indexOf: arrayIndexOf -}; - -if (Ember.SHIM_ES5) { - if (!Array.prototype.map) { - Array.prototype.map = arrayMap; - } - - if (!Array.prototype.forEach) { - Array.prototype.forEach = arrayForEach; - } - - if (!Array.prototype.filter) { - Array.prototype.filter = arrayFilter; - } - - if (!Array.prototype.indexOf) { - Array.prototype.indexOf = arrayIndexOf; - } -} - -})(); - - - -(function() { -var errorProps = ['description', 'fileName', 'lineNumber', 'message', 'name', 'number', 'stack']; - -/** - A subclass of the JavaScript Error object for use in Ember. - - @class Error - @namespace Ember - @extends Error - @constructor -*/ -Ember.Error = function() { - var tmp = Error.apply(this, arguments); - - // Adds a `stack` property to the given error object that will yield the - // stack trace at the time captureStackTrace was called. - // When collecting the stack trace all frames above the topmost call - // to this function, including that call, will be left out of the - // stack trace. - // This is useful because we can hide Ember implementation details - // that are not very helpful for the user. - if (Error.captureStackTrace) { - Error.captureStackTrace(this, Ember.Error); - } - // Unfortunately errors are not enumerable in Chrome (at least), so `for prop in tmp` doesn't work. - for (var idx = 0; idx < errorProps.length; idx++) { - this[errorProps[idx]] = tmp[errorProps[idx]]; - } -}; - -Ember.Error.prototype = Ember.create(Error.prototype); - -// .......................................................... -// ERROR HANDLING -// - -/** - A function may be assigned to `Ember.onerror` to be called when Ember - internals encounter an error. This is useful for specialized error handling - and reporting code. - - ```javascript - Ember.onerror = function(error) { - Em.$.ajax('/report-error', 'POST', { - stack: error.stack, - otherInformation: 'whatever app state you want to provide' - }); - }; - ``` - - @event onerror - @for Ember - @param {Exception} error the error object -*/ -Ember.onerror = null; - -})(); - - - -(function() { -/** -@module ember-metal -*/ - -/** - Prefix used for guids through out Ember. - @private -*/ -Ember.GUID_PREFIX = 'ember'; - - -var o_defineProperty = Ember.platform.defineProperty, - o_create = Ember.create, - // Used for guid generation... - GUID_KEY = '__ember'+ (+ new Date()), - numberCache = [], - stringCache = {}, - uuid = 0; - -var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; - -/** - A unique key used to assign guids and other private metadata to objects. - If you inspect an object in your browser debugger you will often see these. - They can be safely ignored. - - On browsers that support it, these properties are added with enumeration - disabled so they won't show up when you iterate over your properties. - - @private - @property GUID_KEY - @for Ember - @type String - @final -*/ -Ember.GUID_KEY = GUID_KEY; - -var GUID_DESC = { - writable: false, - configurable: false, - enumerable: false, - value: null -}; - -/** - Generates a new guid, optionally saving the guid to the object that you - pass in. You will rarely need to use this method. Instead you should - call `Ember.guidFor(obj)`, which return an existing guid if available. - - @private - @method generateGuid - @for Ember - @param {Object} [obj] Object the guid will be used for. If passed in, the guid will - be saved on the object and reused whenever you pass the same object - again. - - If no object is passed, just generate a new guid. - @param {String} [prefix] Prefix to place in front of the guid. Useful when you want to - separate the guid into separate namespaces. - @return {String} the guid -*/ -Ember.generateGuid = function generateGuid(obj, prefix) { - if (!prefix) prefix = Ember.GUID_PREFIX; - var ret = (prefix + (uuid++)); - if (obj) { - if (obj[GUID_KEY] === null) { - obj[GUID_KEY] = ret; - } else { - GUID_DESC.value = ret; - o_defineProperty(obj, GUID_KEY, GUID_DESC); - } - } - return ret; -}; - -/** - Returns a unique id for the object. If the object does not yet have a guid, - one will be assigned to it. You can call this on any object, - `Ember.Object`-based or not, but be aware that it will add a `_guid` - property. - - You can also use this method on DOM Element objects. - - @private - @method guidFor - @for Ember - @param {Object} obj any object, string, number, Element, or primitive - @return {String} the unique guid for this instance. -*/ -Ember.guidFor = function guidFor(obj) { - - // special cases where we don't want to add a key to object - if (obj === undefined) return "(undefined)"; - if (obj === null) return "(null)"; - - var ret; - var type = typeof obj; - - // Don't allow prototype changes to String etc. to change the guidFor - switch(type) { - case 'number': - ret = numberCache[obj]; - if (!ret) ret = numberCache[obj] = 'nu'+obj; - return ret; - - case 'string': - ret = stringCache[obj]; - if (!ret) ret = stringCache[obj] = 'st'+(uuid++); - return ret; - - case 'boolean': - return obj ? '(true)' : '(false)'; - - default: - if (obj[GUID_KEY]) return obj[GUID_KEY]; - if (obj === Object) return '(Object)'; - if (obj === Array) return '(Array)'; - ret = 'ember' + (uuid++); - - if (obj[GUID_KEY] === null) { - obj[GUID_KEY] = ret; - } else { - GUID_DESC.value = ret; - o_defineProperty(obj, GUID_KEY, GUID_DESC); - } - return ret; - } -}; - -// .......................................................... -// META -// - -var META_DESC = Ember.META_DESC = { - writable: true, - configurable: false, - enumerable: false, - value: null -}; - -var META_KEY = Ember.GUID_KEY+'_meta'; - -/** - The key used to store meta information on object for property observing. - - @property META_KEY - @for Ember - @private - @final - @type String -*/ -Ember.META_KEY = META_KEY; - -var isDefinePropertySimulated = Ember.platform.defineProperty.isSimulated; - -function Meta(obj) { - this.descs = {}; - this.watching = {}; - this.cache = {}; - this.cacheMeta = {}; - this.source = obj; -} - -Meta.prototype = { - descs: null, - deps: null, - watching: null, - listeners: null, - cache: null, - cacheMeta: null, - source: null, - mixins: null, - bindings: null, - chains: null, - chainWatchers: null, - values: null, - proto: null -}; - -if (isDefinePropertySimulated) { - // on platforms that don't support enumerable false - // make meta fail jQuery.isPlainObject() to hide from - // jQuery.extend() by having a property that fails - // hasOwnProperty check. - Meta.prototype.__preventPlainObject__ = true; - - // Without non-enumerable properties, meta objects will be output in JSON - // unless explicitly suppressed - Meta.prototype.toJSON = function () { }; -} - -// Placeholder for non-writable metas. -var EMPTY_META = new Meta(null); - -if (MANDATORY_SETTER) { EMPTY_META.values = {}; } - -Ember.EMPTY_META = EMPTY_META; - -/** - Retrieves the meta hash for an object. If `writable` is true ensures the - hash is writable for this object as well. - - The meta object contains information about computed property descriptors as - well as any watched properties and other information. You generally will - not access this information directly but instead work with higher level - methods that manipulate this hash indirectly. - - @method meta - @for Ember - @private - - @param {Object} obj The object to retrieve meta for - @param {Boolean} [writable=true] Pass `false` if you do not intend to modify - the meta hash, allowing the method to avoid making an unnecessary copy. - @return {Object} the meta hash for an object -*/ -Ember.meta = function meta(obj, writable) { - - var ret = obj[META_KEY]; - if (writable===false) return ret || EMPTY_META; - - if (!ret) { - if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC); - - ret = new Meta(obj); - - if (MANDATORY_SETTER) { ret.values = {}; } - - obj[META_KEY] = ret; - - // make sure we don't accidentally try to create constructor like desc - ret.descs.constructor = null; - - } else if (ret.source !== obj) { - if (!isDefinePropertySimulated) o_defineProperty(obj, META_KEY, META_DESC); - - ret = o_create(ret); - ret.descs = o_create(ret.descs); - ret.watching = o_create(ret.watching); - ret.cache = {}; - ret.cacheMeta = {}; - ret.source = obj; - - if (MANDATORY_SETTER) { ret.values = o_create(ret.values); } - - obj[META_KEY] = ret; - } - return ret; -}; - -Ember.getMeta = function getMeta(obj, property) { - var meta = Ember.meta(obj, false); - return meta[property]; -}; - -Ember.setMeta = function setMeta(obj, property, value) { - var meta = Ember.meta(obj, true); - meta[property] = value; - return value; -}; - -/** - @deprecated - @private - - In order to store defaults for a class, a prototype may need to create - a default meta object, which will be inherited by any objects instantiated - from the class's constructor. - - However, the properties of that meta object are only shallow-cloned, - so if a property is a hash (like the event system's `listeners` hash), - it will by default be shared across all instances of that class. - - This method allows extensions to deeply clone a series of nested hashes or - other complex objects. For instance, the event system might pass - `['listeners', 'foo:change', 'ember157']` to `prepareMetaPath`, which will - walk down the keys provided. - - For each key, if the key does not exist, it is created. If it already - exists and it was inherited from its constructor, the constructor's - key is cloned. - - You can also pass false for `writable`, which will simply return - undefined if `prepareMetaPath` discovers any part of the path that - shared or undefined. - - @method metaPath - @for Ember - @param {Object} obj The object whose meta we are examining - @param {Array} path An array of keys to walk down - @param {Boolean} writable whether or not to create a new meta - (or meta property) if one does not already exist or if it's - shared with its constructor -*/ -Ember.metaPath = function metaPath(obj, path, writable) { - Ember.deprecate("Ember.metaPath is deprecated and will be removed from future releases."); - var meta = Ember.meta(obj, writable), keyName, value; - - for (var i=0, l=path.length; i size ? size : ends; - if (count <= 0) { count = 0; } - - chunk = args.splice(0, size); - chunk = [start, count].concat(chunk); - - start += size; - ends -= count; - - ret = ret.concat(splice.apply(array, chunk)); - } - return ret; - }, - - /** - * Replaces objects in an array with the passed objects. - * - * ```javascript - * var array = [1,2,3]; - * Ember.EnumerableUtils.replace(array, 1, 2, [4, 5]); // [1, 4, 5] - * - * var array = [1,2,3]; - * Ember.EnumerableUtils.replace(array, 1, 1, [4, 5]); // [1, 4, 5, 3] - * - * var array = [1,2,3]; - * Ember.EnumerableUtils.replace(array, 10, 1, [4, 5]); // [1, 2, 3, 4, 5] - * ``` - * - * @method replace - * @param {Array} array The array the objects should be inserted into. - * @param {Number} idx Starting index in the array to replace. If *idx* >= - * length, then append to the end of the array. - * @param {Number} amt Number of elements that should be removed from the array, - * starting at *idx* - * @param {Array} objects An array of zero or more objects that should be - * inserted into the array at *idx* - * - * @return {Array} The modified array. - */ - replace: function(array, idx, amt, objects) { - if (array.replace) { - return array.replace(idx, amt, objects); - } else { - return utils._replace(array, idx, amt, objects); - } - }, - - /** - * Calculates the intersection of two arrays. This method returns a new array - * filled with the records that the two passed arrays share with each other. - * If there is no intersection, an empty array will be returned. - * - * ```javascript - * var array1 = [1, 2, 3, 4, 5]; - * var array2 = [1, 3, 5, 6, 7]; - * - * Ember.EnumerableUtils.intersection(array1, array2); // [1, 3, 5] - * - * var array1 = [1, 2, 3]; - * var array2 = [4, 5, 6]; - * - * Ember.EnumerableUtils.intersection(array1, array2); // [] - * ``` - * - * @method intersection - * @param {Array} array1 The first array - * @param {Array} array2 The second array - * - * @return {Array} The intersection of the two passed arrays. - */ - intersection: function(array1, array2) { - var intersection = []; - - utils.forEach(array1, function(element) { - if (utils.indexOf(array2, element) >= 0) { - intersection.push(element); - } - }); - - return intersection; - } -}; - -})(); - - - -(function() { -/** -@module ember-metal -*/ - -var META_KEY = Ember.META_KEY, get; - -var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; - -var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.\*]/; -var HAS_THIS = /^this[\.\*]/; -var FIRST_KEY = /^([^\.\*]+)/; - -// .......................................................... -// GET AND SET -// -// If we are on a platform that supports accessors we can use those. -// Otherwise simulate accessors by looking up the property directly on the -// object. - -/** - Gets the value of a property on an object. If the property is computed, - the function will be invoked. If the property is not defined but the - object implements the `unknownProperty` method then that will be invoked. - - If you plan to run on IE8 and older browsers then you should use this - method anytime you want to retrieve a property on an object that you don't - know for sure is private. (Properties beginning with an underscore '_' - are considered private.) - - On all newer browsers, you only need to use this method to retrieve - properties if the property might not be defined on the object and you want - to respect the `unknownProperty` handler. Otherwise you can ignore this - method. - - Note that if the object itself is `undefined`, this method will throw - an error. - - @method get - @for Ember - @param {Object} obj The object to retrieve from. - @param {String} keyName The property key to retrieve - @return {Object} the property value or `null`. -*/ -get = function get(obj, keyName) { - // Helpers that operate with 'this' within an #each - if (keyName === '') { - return obj; - } - - if (!keyName && 'string'===typeof obj) { - keyName = obj; - obj = null; - } - - Ember.assert("Cannot call get with "+ keyName +" key.", !!keyName); - Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined); - - if (obj === null || keyName.indexOf('.') !== -1) { - return getPath(obj, keyName); - } - - var meta = obj[META_KEY], desc = meta && meta.descs[keyName], ret; - if (desc) { - return desc.get(obj, keyName); - } else { - if (MANDATORY_SETTER && meta && meta.watching[keyName] > 0) { - ret = meta.values[keyName]; - } else { - ret = obj[keyName]; - } - - if (ret === undefined && - 'object' === typeof obj && !(keyName in obj) && 'function' === typeof obj.unknownProperty) { - return obj.unknownProperty(keyName); - } - - return ret; - } -}; - -// Currently used only by Ember Data tests -if (Ember.config.overrideAccessors) { - Ember.get = get; - Ember.config.overrideAccessors(); - get = Ember.get; -} - -/** - Normalizes a target/path pair to reflect that actual target/path that should - be observed, etc. This takes into account passing in global property - paths (i.e. a path beginning with a captial letter not defined on the - target) and * separators. - - @private - @method normalizeTuple - @for Ember - @param {Object} target The current target. May be `null`. - @param {String} path A path on the target or a global property path. - @return {Array} a temporary array with the normalized target/path pair. -*/ -var normalizeTuple = Ember.normalizeTuple = function(target, path) { - var hasThis = HAS_THIS.test(path), - isGlobal = !hasThis && IS_GLOBAL_PATH.test(path), - key; - - if (!target || isGlobal) target = Ember.lookup; - if (hasThis) path = path.slice(5); - - if (target === Ember.lookup) { - key = path.match(FIRST_KEY)[0]; - target = get(target, key); - path = path.slice(key.length+1); - } - - // must return some kind of path to be valid else other things will break. - if (!path || path.length===0) throw new Ember.Error('Path cannot be empty'); - - return [ target, path ]; -}; - -var getPath = Ember._getPath = function(root, path) { - var hasThis, parts, tuple, idx, len; - - // If there is no root and path is a key name, return that - // property from the global object. - // E.g. get('Ember') -> Ember - if (root === null && path.indexOf('.') === -1) { return get(Ember.lookup, path); } - - // detect complicated paths and normalize them - hasThis = HAS_THIS.test(path); - - if (!root || hasThis) { - tuple = normalizeTuple(root, path); - root = tuple[0]; - path = tuple[1]; - tuple.length = 0; - } - - parts = path.split("."); - len = parts.length; - for (idx = 0; root != null && idx < len; idx++) { - root = get(root, parts[idx], true); - if (root && root.isDestroyed) { return undefined; } - } - return root; -}; - -Ember.getWithDefault = function(root, key, defaultValue) { - var value = get(root, key); - - if (value === undefined) { return defaultValue; } - return value; -}; - - -Ember.get = get; - -})(); - - - -(function() { -/** -@module ember-metal -*/ - -var o_create = Ember.create, - metaFor = Ember.meta, - META_KEY = Ember.META_KEY, - a_slice = [].slice, - /* listener flags */ - ONCE = 1, SUSPENDED = 2; - -/* - The event system uses a series of nested hashes to store listeners on an - object. When a listener is registered, or when an event arrives, these - hashes are consulted to determine which target and action pair to invoke. - - The hashes are stored in the object's meta hash, and look like this: - - // Object's meta hash - { - listeners: { // variable name: `listenerSet` - "foo:changed": [ // variable name: `actions` - target, method, flags - ] - } - } - -*/ - -function indexOf(array, target, method) { - var index = -1; - // hashes are added to the end of the event array - // so it makes sense to start searching at the end - // of the array and search in reverse - for (var i = array.length - 3 ; i >=0; i -= 3) { - if (target === array[i] && method === array[i + 1]) { - index = i; break; - } - } - return index; -} - -function actionsFor(obj, eventName) { - var meta = metaFor(obj, true), - actions; - - if (!meta.listeners) { meta.listeners = {}; } - - if (!meta.hasOwnProperty('listeners')) { - // setup inherited copy of the listeners object - meta.listeners = o_create(meta.listeners); - } - - actions = meta.listeners[eventName]; - - // if there are actions, but the eventName doesn't exist in our listeners, then copy them from the prototype - if (actions && !meta.listeners.hasOwnProperty(eventName)) { - actions = meta.listeners[eventName] = meta.listeners[eventName].slice(); - } else if (!actions) { - actions = meta.listeners[eventName] = []; - } - - return actions; -} - -function actionsUnion(obj, eventName, otherActions) { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; - - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - var target = actions[i], - method = actions[i+1], - flags = actions[i+2], - actionIndex = indexOf(otherActions, target, method); - - if (actionIndex === -1) { - otherActions.push(target, method, flags); - } - } -} - -function actionsDiff(obj, eventName, otherActions) { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName], - diffActions = []; - - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - var target = actions[i], - method = actions[i+1], - flags = actions[i+2], - actionIndex = indexOf(otherActions, target, method); - - if (actionIndex !== -1) { continue; } - - otherActions.push(target, method, flags); - diffActions.push(target, method, flags); - } - - return diffActions; -} - -/** - Add an event listener - - @method addListener - @for Ember - @param obj - @param {String} eventName - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` - @param {Boolean} once A flag whether a function should only be called once -*/ -function addListener(obj, eventName, target, method, once) { - Ember.assert("You must pass at least an object and event name to Ember.addListener", !!obj && !!eventName); - - if (!method && 'function' === typeof target) { - method = target; - target = null; - } - - var actions = actionsFor(obj, eventName), - actionIndex = indexOf(actions, target, method), - flags = 0; - - if (once) flags |= ONCE; - - if (actionIndex !== -1) { return; } - - actions.push(target, method, flags); - - if ('function' === typeof obj.didAddListener) { - obj.didAddListener(eventName, target, method); - } -} - -/** - Remove an event listener - - Arguments should match those passed to `Ember.addListener`. - - @method removeListener - @for Ember - @param obj - @param {String} eventName - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` -*/ -function removeListener(obj, eventName, target, method) { - Ember.assert("You must pass at least an object and event name to Ember.removeListener", !!obj && !!eventName); - - if (!method && 'function' === typeof target) { - method = target; - target = null; - } - - function _removeListener(target, method) { - var actions = actionsFor(obj, eventName), - actionIndex = indexOf(actions, target, method); - - // action doesn't exist, give up silently - if (actionIndex === -1) { return; } - - actions.splice(actionIndex, 3); - - if ('function' === typeof obj.didRemoveListener) { - obj.didRemoveListener(eventName, target, method); - } - } - - if (method) { - _removeListener(target, method); - } else { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; - - if (!actions) { return; } - for (var i = actions.length - 3; i >= 0; i -= 3) { - _removeListener(actions[i], actions[i+1]); - } - } -} - -/** - Suspend listener during callback. - - This should only be used by the target of the event listener - when it is taking an action that would cause the event, e.g. - an object might suspend its property change listener while it is - setting that property. - - @method suspendListener - @for Ember - - @private - @param obj - @param {String} eventName - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` - @param {Function} callback -*/ -function suspendListener(obj, eventName, target, method, callback) { - if (!method && 'function' === typeof target) { - method = target; - target = null; - } - - var actions = actionsFor(obj, eventName), - actionIndex = indexOf(actions, target, method); - - if (actionIndex !== -1) { - actions[actionIndex+2] |= SUSPENDED; // mark the action as suspended - } - - function tryable() { return callback.call(target); } - function finalizer() { if (actionIndex !== -1) { actions[actionIndex+2] &= ~SUSPENDED; } } - - return Ember.tryFinally(tryable, finalizer); -} - -/** - Suspends multiple listeners during a callback. - - @method suspendListeners - @for Ember - - @private - @param obj - @param {Array} eventName Array of event names - @param {Object|Function} targetOrMethod A target object or a function - @param {Function|String} method A function or the name of a function to be called on `target` - @param {Function} callback -*/ -function suspendListeners(obj, eventNames, target, method, callback) { - if (!method && 'function' === typeof target) { - method = target; - target = null; - } - - var suspendedActions = [], - actionsList = [], - eventName, actions, i, l; - - for (i=0, l=eventNames.length; i= 0; i -= 3) { // looping in reverse for once listeners - var target = actions[i], method = actions[i+1], flags = actions[i+2]; - if (!method) { continue; } - if (flags & SUSPENDED) { continue; } - if (flags & ONCE) { removeListener(obj, eventName, target, method); } - if (!target) { target = obj; } - if ('string' === typeof method) { method = target[method]; } - if (params) { - method.apply(target, params); - } else { - method.call(target); - } - } - return true; -} - -/** - @private - @method hasListeners - @for Ember - @param obj - @param {String} eventName -*/ -function hasListeners(obj, eventName) { - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; - - return !!(actions && actions.length); -} - -/** - @private - @method listenersFor - @for Ember - @param obj - @param {String} eventName -*/ -function listenersFor(obj, eventName) { - var ret = []; - var meta = obj[META_KEY], - actions = meta && meta.listeners && meta.listeners[eventName]; - - if (!actions) { return ret; } - - for (var i = 0, l = actions.length; i < l; i += 3) { - var target = actions[i], - method = actions[i+1]; - ret.push([target, method]); - } - - return ret; -} - -/** - Define a property as a function that should be executed when - a specified event or events are triggered. - - - ``` javascript - var Job = Ember.Object.extend({ - logCompleted: Ember.on('completed', function(){ - console.log('Job completed!'); - }) - }); - var job = Job.create(); - Ember.sendEvent(job, 'completed'); // Logs "Job completed!" - ``` - - @method on - @for Ember - @param {String} eventNames* - @param {Function} func - @return func -*/ -Ember.on = function(){ - var func = a_slice.call(arguments, -1)[0], - events = a_slice.call(arguments, 0, -1); - func.__ember_listens__ = events; - return func; -}; - -Ember.addListener = addListener; -Ember.removeListener = removeListener; -Ember._suspendListener = suspendListener; -Ember._suspendListeners = suspendListeners; -Ember.sendEvent = sendEvent; -Ember.hasListeners = hasListeners; -Ember.watchedEvents = watchedEvents; -Ember.listenersFor = listenersFor; -Ember.listenersDiff = actionsDiff; -Ember.listenersUnion = actionsUnion; - -})(); - - - -(function() { -var guidFor = Ember.guidFor, - sendEvent = Ember.sendEvent; - -/* - this.observerSet = { - [senderGuid]: { // variable name: `keySet` - [keyName]: listIndex - } - }, - this.observers = [ - { - sender: obj, - keyName: keyName, - eventName: eventName, - listeners: [ - [target, method, flags] - ] - }, - ... - ] -*/ -var ObserverSet = Ember._ObserverSet = function() { - this.clear(); -}; - -ObserverSet.prototype.add = function(sender, keyName, eventName) { - var observerSet = this.observerSet, - observers = this.observers, - senderGuid = guidFor(sender), - keySet = observerSet[senderGuid], - index; - - if (!keySet) { - observerSet[senderGuid] = keySet = {}; - } - index = keySet[keyName]; - if (index === undefined) { - index = observers.push({ - sender: sender, - keyName: keyName, - eventName: eventName, - listeners: [] - }) - 1; - keySet[keyName] = index; - } - return observers[index].listeners; -}; - -ObserverSet.prototype.flush = function() { - var observers = this.observers, i, len, observer, sender; - this.clear(); - for (i=0, len=observers.length; i < len; ++i) { - observer = observers[i]; - sender = observer.sender; - if (sender.isDestroying || sender.isDestroyed) { continue; } - sendEvent(sender, observer.eventName, [sender, observer.keyName], observer.listeners); - } -}; - -ObserverSet.prototype.clear = function() { - this.observerSet = {}; - this.observers = []; -}; -})(); - - - -(function() { -var META_KEY = Ember.META_KEY, - guidFor = Ember.guidFor, - tryFinally = Ember.tryFinally, - sendEvent = Ember.sendEvent, - listenersUnion = Ember.listenersUnion, - listenersDiff = Ember.listenersDiff, - ObserverSet = Ember._ObserverSet, - beforeObserverSet = new ObserverSet(), - observerSet = new ObserverSet(), - deferred = 0; - -// .......................................................... -// PROPERTY CHANGES -// - -/** - This function is called just before an object property is about to change. - It will notify any before observers and prepare caches among other things. - - Normally you will not need to call this method directly but if for some - reason you can't directly watch a property you can invoke this method - manually along with `Ember.propertyDidChange()` which you should call just - after the property value changes. - - @method propertyWillChange - @for Ember - @param {Object} obj The object with the property that will change - @param {String} keyName The property key (or path) that will change. - @return {void} -*/ -function propertyWillChange(obj, keyName) { - var m = obj[META_KEY], - watching = (m && m.watching[keyName] > 0) || keyName === 'length', - proto = m && m.proto, - desc = m && m.descs[keyName]; - - if (!watching) { return; } - if (proto === obj) { return; } - if (desc && desc.willChange) { desc.willChange(obj, keyName); } - dependentKeysWillChange(obj, keyName, m); - chainsWillChange(obj, keyName, m); - notifyBeforeObservers(obj, keyName); -} -Ember.propertyWillChange = propertyWillChange; - -/** - This function is called just after an object property has changed. - It will notify any observers and clear caches among other things. - - Normally you will not need to call this method directly but if for some - reason you can't directly watch a property you can invoke this method - manually along with `Ember.propertyWillChange()` which you should call just - before the property value changes. - - @method propertyDidChange - @for Ember - @param {Object} obj The object with the property that will change - @param {String} keyName The property key (or path) that will change. - @return {void} -*/ -function propertyDidChange(obj, keyName) { - var m = obj[META_KEY], - watching = (m && m.watching[keyName] > 0) || keyName === 'length', - proto = m && m.proto, - desc = m && m.descs[keyName]; - - if (proto === obj) { return; } - - // shouldn't this mean that we're watching this key? - if (desc && desc.didChange) { desc.didChange(obj, keyName); } - if (!watching && keyName !== 'length') { return; } - - dependentKeysDidChange(obj, keyName, m); - chainsDidChange(obj, keyName, m, false); - notifyObservers(obj, keyName); -} -Ember.propertyDidChange = propertyDidChange; - -var WILL_SEEN, DID_SEEN; - -// called whenever a property is about to change to clear the cache of any dependent keys (and notify those properties of changes, etc...) -function dependentKeysWillChange(obj, depKey, meta) { - if (obj.isDestroying) { return; } - - var seen = WILL_SEEN, top = !seen; - if (top) { seen = WILL_SEEN = {}; } - iterDeps(propertyWillChange, obj, depKey, seen, meta); - if (top) { WILL_SEEN = null; } -} - -// called whenever a property has just changed to update dependent keys -function dependentKeysDidChange(obj, depKey, meta) { - if (obj.isDestroying) { return; } - - var seen = DID_SEEN, top = !seen; - if (top) { seen = DID_SEEN = {}; } - iterDeps(propertyDidChange, obj, depKey, seen, meta); - if (top) { DID_SEEN = null; } -} - -function iterDeps(method, obj, depKey, seen, meta) { - var guid = guidFor(obj); - if (!seen[guid]) seen[guid] = {}; - if (seen[guid][depKey]) return; - seen[guid][depKey] = true; - - var deps = meta.deps; - deps = deps && deps[depKey]; - if (deps) { - for(var key in deps) { - var desc = meta.descs[key]; - if (desc && desc._suspended === obj) continue; - method(obj, key); - } - } -} - -function chainsWillChange(obj, keyName, m) { - if (!(m.hasOwnProperty('chainWatchers') && - m.chainWatchers[keyName])) { - return; - } - - var nodes = m.chainWatchers[keyName], - events = [], - i, l; - - for(i = 0, l = nodes.length; i < l; i++) { - nodes[i].willChange(events); - } - - for (i = 0, l = events.length; i < l; i += 2) { - propertyWillChange(events[i], events[i+1]); - } -} - -function chainsDidChange(obj, keyName, m, suppressEvents) { - if (!(m && m.hasOwnProperty('chainWatchers') && - m.chainWatchers[keyName])) { - return; - } - - var nodes = m.chainWatchers[keyName], - events = suppressEvents ? null : [], - i, l; - - for(i = 0, l = nodes.length; i < l; i++) { - nodes[i].didChange(events); - } - - if (suppressEvents) { - return; - } - - for (i = 0, l = events.length; i < l; i += 2) { - propertyDidChange(events[i], events[i+1]); - } -} - -Ember.overrideChains = function(obj, keyName, m) { - chainsDidChange(obj, keyName, m, true); -}; - -/** - @method beginPropertyChanges - @chainable - @private -*/ -function beginPropertyChanges() { - deferred++; -} - -Ember.beginPropertyChanges = beginPropertyChanges; - -/** - @method endPropertyChanges - @private -*/ -function endPropertyChanges() { - deferred--; - if (deferred<=0) { - beforeObserverSet.clear(); - observerSet.flush(); - } -} - -Ember.endPropertyChanges = endPropertyChanges; - -/** - Make a series of property changes together in an - exception-safe way. - - ```javascript - Ember.changeProperties(function() { - obj1.set('foo', mayBlowUpWhenSet); - obj2.set('bar', baz); - }); - ``` - - @method changeProperties - @param {Function} callback - @param [binding] -*/ -Ember.changeProperties = function(cb, binding) { - beginPropertyChanges(); - tryFinally(cb, endPropertyChanges, binding); -}; - -function notifyBeforeObservers(obj, keyName) { - if (obj.isDestroying) { return; } - - var eventName = keyName + ':before', listeners, diff; - if (deferred) { - listeners = beforeObserverSet.add(obj, keyName, eventName); - diff = listenersDiff(obj, eventName, listeners); - sendEvent(obj, eventName, [obj, keyName], diff); - } else { - sendEvent(obj, eventName, [obj, keyName]); - } -} - -function notifyObservers(obj, keyName) { - if (obj.isDestroying) { return; } - - var eventName = keyName + ':change', listeners; - if (deferred) { - listeners = observerSet.add(obj, keyName, eventName); - listenersUnion(obj, eventName, listeners); - } else { - sendEvent(obj, eventName, [obj, keyName]); - } -} - -})(); - - - -(function() { -// META_KEY -// _getPath -// propertyWillChange, propertyDidChange - -var META_KEY = Ember.META_KEY, - MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER, - IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/, - getPath = Ember._getPath; - -/** - Sets the value of a property on an object, respecting computed properties - and notifying observers and other listeners of the change. If the - property is not defined but the object implements the `setUnknownProperty` - method then that will be invoked as well. - - @method set - @for Ember - @param {Object} obj The object to modify. - @param {String} keyName The property key to set - @param {Object} value The value to set - @return {Object} the passed value. -*/ -var set = function set(obj, keyName, value, tolerant) { - if (typeof obj === 'string') { - Ember.assert("Path '" + obj + "' must be global if no obj is given.", IS_GLOBAL.test(obj)); - value = keyName; - keyName = obj; - obj = null; - } - - Ember.assert("Cannot call set with "+ keyName +" key.", !!keyName); - - if (!obj || keyName.indexOf('.') !== -1) { - return setPath(obj, keyName, value, tolerant); - } - - Ember.assert("You need to provide an object and key to `set`.", !!obj && keyName !== undefined); - Ember.assert('calling set on destroyed object', !obj.isDestroyed); - - var meta = obj[META_KEY], desc = meta && meta.descs[keyName], - isUnknown, currentValue; - if (desc) { - desc.set(obj, keyName, value); - } else { - isUnknown = 'object' === typeof obj && !(keyName in obj); - - // setUnknownProperty is called if `obj` is an object, - // the property does not already exist, and the - // `setUnknownProperty` method exists on the object - if (isUnknown && 'function' === typeof obj.setUnknownProperty) { - obj.setUnknownProperty(keyName, value); - } else if (meta && meta.watching[keyName] > 0) { - if (MANDATORY_SETTER) { - currentValue = meta.values[keyName]; - } else { - currentValue = obj[keyName]; - } - // only trigger a change if the value has changed - if (value !== currentValue) { - Ember.propertyWillChange(obj, keyName); - if (MANDATORY_SETTER) { - if ((currentValue === undefined && !(keyName in obj)) || !obj.propertyIsEnumerable(keyName)) { - Ember.defineProperty(obj, keyName, null, value); // setup mandatory setter - } else { - meta.values[keyName] = value; - } - } else { - obj[keyName] = value; - } - Ember.propertyDidChange(obj, keyName); - } - } else { - obj[keyName] = value; - } - } - return value; -}; - -// Currently used only by Ember Data tests -if (Ember.config.overrideAccessors) { - Ember.set = set; - Ember.config.overrideAccessors(); - set = Ember.set; -} - -function setPath(root, path, value, tolerant) { - var keyName; - - // get the last part of the path - keyName = path.slice(path.lastIndexOf('.') + 1); - - // get the first part of the part - path = (path === keyName) ? keyName : path.slice(0, path.length-(keyName.length+1)); - - // unless the path is this, look up the first part to - // get the root - if (path !== 'this') { - root = getPath(root, path); - } - - if (!keyName || keyName.length === 0) { - throw new Ember.Error('Property set failed: You passed an empty path'); - } - - if (!root) { - if (tolerant) { return; } - else { throw new Ember.Error('Property set failed: object in path "'+path+'" could not be found or was destroyed.'); } - } - - return set(root, keyName, value); -} - -Ember.set = set; - -/** - Error-tolerant form of `Ember.set`. Will not blow up if any part of the - chain is `undefined`, `null`, or destroyed. - - This is primarily used when syncing bindings, which may try to update after - an object has been destroyed. - - @method trySet - @for Ember - @param {Object} obj The object to modify. - @param {String} path The property path to set - @param {Object} value The value to set -*/ -Ember.trySet = function(root, path, value) { - return set(root, path, value, true); -}; - -})(); - - - -(function() { -/** -@module ember-metal -*/ - -/* - JavaScript (before ES6) does not have a Map implementation. Objects, - which are often used as dictionaries, may only have Strings as keys. - - Because Ember has a way to get a unique identifier for every object - via `Ember.guidFor`, we can implement a performant Map with arbitrary - keys. Because it is commonly used in low-level bookkeeping, Map is - implemented as a pure JavaScript object for performance. - - This implementation follows the current iteration of the ES6 proposal for - maps (http://wiki.ecmascript.org/doku.php?id=harmony:simple_maps_and_sets), - with two exceptions. First, because we need our implementation to be pleasant - on older browsers, we do not use the `delete` name (using `remove` instead). - Second, as we do not have the luxury of in-VM iteration, we implement a - forEach method for iteration. - - Map is mocked out to look like an Ember object, so you can do - `Ember.Map.create()` for symmetry with other Ember classes. -*/ -var set = Ember.set, - guidFor = Ember.guidFor, - indexOf = Ember.ArrayPolyfills.indexOf; - -var copy = function(obj) { - var output = {}; - - for (var prop in obj) { - if (obj.hasOwnProperty(prop)) { output[prop] = obj[prop]; } - } - - return output; -}; - -var copyMap = function(original, newObject) { - var keys = original.keys.copy(), - values = copy(original.values); - - newObject.keys = keys; - newObject.values = values; - newObject.length = original.length; - - return newObject; -}; - -/** - This class is used internally by Ember and Ember Data. - Please do not use it at this time. We plan to clean it up - and add many tests soon. - - @class OrderedSet - @namespace Ember - @constructor - @private -*/ -var OrderedSet = Ember.OrderedSet = function() { - this.clear(); -}; - -/** - @method create - @static - @return {Ember.OrderedSet} -*/ -OrderedSet.create = function() { - return new OrderedSet(); -}; - - -OrderedSet.prototype = { - /** - @method clear - */ - clear: function() { - this.presenceSet = {}; - this.list = []; - }, - - /** - @method add - @param obj - */ - add: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet, - list = this.list; - - if (guid in presenceSet) { return; } - - presenceSet[guid] = true; - list.push(obj); - }, - - /** - @method remove - @param obj - */ - remove: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet, - list = this.list; - - delete presenceSet[guid]; - - var index = indexOf.call(list, obj); - if (index > -1) { - list.splice(index, 1); - } - }, - - /** - @method isEmpty - @return {Boolean} - */ - isEmpty: function() { - return this.list.length === 0; - }, - - /** - @method has - @param obj - @return {Boolean} - */ - has: function(obj) { - var guid = guidFor(obj), - presenceSet = this.presenceSet; - - return guid in presenceSet; - }, - - /** - @method forEach - @param {Function} fn - @param self - */ - forEach: function(fn, self) { - // allow mutation during iteration - var list = this.toArray(); - - for (var i = 0, j = list.length; i < j; i++) { - fn.call(self, list[i]); - } - }, - - /** - @method toArray - @return {Array} - */ - toArray: function() { - return this.list.slice(); - }, - - /** - @method copy - @return {Ember.OrderedSet} - */ - copy: function() { - var set = new OrderedSet(); - - set.presenceSet = copy(this.presenceSet); - set.list = this.toArray(); - - return set; - } -}; - -/** - A Map stores values indexed by keys. Unlike JavaScript's - default Objects, the keys of a Map can be any JavaScript - object. - - Internally, a Map has two data structures: - - 1. `keys`: an OrderedSet of all of the existing keys - 2. `values`: a JavaScript Object indexed by the `Ember.guidFor(key)` - - When a key/value pair is added for the first time, we - add the key to the `keys` OrderedSet, and create or - replace an entry in `values`. When an entry is deleted, - we delete its entry in `keys` and `values`. - - @class Map - @namespace Ember - @private - @constructor -*/ -var Map = Ember.Map = function() { - this.keys = Ember.OrderedSet.create(); - this.values = {}; -}; - -/** - @method create - @static -*/ -Map.create = function() { - return new Map(); -}; - -Map.prototype = { - /** - This property will change as the number of objects in the map changes. - - @property length - @type number - @default 0 - */ - length: 0, - - - /** - Retrieve the value associated with a given key. - - @method get - @param {*} key - @return {*} the value associated with the key, or `undefined` - */ - get: function(key) { - var values = this.values, - guid = guidFor(key); - - return values[guid]; - }, - - /** - Adds a value to the map. If a value for the given key has already been - provided, the new value will replace the old value. - - @method set - @param {*} key - @param {*} value - */ - set: function(key, value) { - var keys = this.keys, - values = this.values, - guid = guidFor(key); - - keys.add(key); - values[guid] = value; - set(this, 'length', keys.list.length); - }, - - /** - Removes a value from the map for an associated key. - - @method remove - @param {*} key - @return {Boolean} true if an item was removed, false otherwise - */ - remove: function(key) { - // don't use ES6 "delete" because it will be annoying - // to use in browsers that are not ES6 friendly; - var keys = this.keys, - values = this.values, - guid = guidFor(key); - - if (values.hasOwnProperty(guid)) { - keys.remove(key); - delete values[guid]; - set(this, 'length', keys.list.length); - return true; - } else { - return false; - } - }, - - /** - Check whether a key is present. - - @method has - @param {*} key - @return {Boolean} true if the item was present, false otherwise - */ - has: function(key) { - var values = this.values, - guid = guidFor(key); - - return values.hasOwnProperty(guid); - }, - - /** - Iterate over all the keys and values. Calls the function once - for each key, passing in the key and value, in that order. - - The keys are guaranteed to be iterated over in insertion order. - - @method forEach - @param {Function} callback - @param {*} self if passed, the `this` value inside the - callback. By default, `this` is the map. - */ - forEach: function(callback, self) { - var keys = this.keys, - values = this.values; - - keys.forEach(function(key) { - var guid = guidFor(key); - callback.call(self, key, values[guid]); - }); - }, - - /** - @method copy - @return {Ember.Map} - */ - copy: function() { - return copyMap(this, new Map()); - } -}; - -/** - @class MapWithDefault - @namespace Ember - @extends Ember.Map - @private - @constructor - @param [options] - @param {*} [options.defaultValue] -*/ -var MapWithDefault = Ember.MapWithDefault = function(options) { - Map.call(this); - this.defaultValue = options.defaultValue; -}; - -/** - @method create - @static - @param [options] - @param {*} [options.defaultValue] - @return {Ember.MapWithDefault|Ember.Map} If options are passed, returns - `Ember.MapWithDefault` otherwise returns `Ember.Map` -*/ -MapWithDefault.create = function(options) { - if (options) { - return new MapWithDefault(options); - } else { - return new Map(); - } -}; - -MapWithDefault.prototype = Ember.create(Map.prototype); - -/** - Retrieve the value associated with a given key. - - @method get - @param {*} key - @return {*} the value associated with the key, or the default value -*/ -MapWithDefault.prototype.get = function(key) { - var hasValue = this.has(key); - - if (hasValue) { - return Map.prototype.get.call(this, key); - } else { - var defaultValue = this.defaultValue(key); - this.set(key, defaultValue); - return defaultValue; - } -}; - -/** - @method copy - @return {Ember.MapWithDefault} -*/ -MapWithDefault.prototype.copy = function() { - return copyMap(this, new MapWithDefault({ - defaultValue: this.defaultValue - })); -}; - -})(); - - - -(function() { -function consoleMethod(name) { - var consoleObj, logToConsole; - if (Ember.imports.console) { - consoleObj = Ember.imports.console; - } else if (typeof console !== 'undefined') { - consoleObj = console; - } - - var method = typeof consoleObj === 'object' ? consoleObj[name] : null; - - if (method) { - // Older IE doesn't support apply, but Chrome needs it - if (typeof method.apply === 'function') { - logToConsole = function() { - method.apply(consoleObj, arguments); - }; - logToConsole.displayName = 'console.' + name; - return logToConsole; - } else { - return function() { - var message = Array.prototype.join.call(arguments, ', '); - method(message); - }; - } - } -} - -function assertPolyfill(test, message) { - if (!test) { - try { - // attempt to preserve the stack - throw new Ember.Error("assertion failed: " + message); - } catch(error) { - setTimeout(function() { - throw error; - }, 0); - } - } -} - -/** - Inside Ember-Metal, simply uses the methods from `imports.console`. - Override this to provide more robust logging functionality. - - @class Logger - @namespace Ember -*/ -Ember.Logger = { - /** - Logs the arguments to the console. - You can pass as many arguments as you want and they will be joined together with a space. - - ```javascript - var foo = 1; - Ember.Logger.log('log value of foo:', foo); - // "log value of foo: 1" will be printed to the console - ``` - - @method log - @for Ember.Logger - @param {*} arguments - */ - log: consoleMethod('log') || Ember.K, - - /** - Prints the arguments to the console with a warning icon. - You can pass as many arguments as you want and they will be joined together with a space. - - ```javascript - Ember.Logger.warn('Something happened!'); - // "Something happened!" will be printed to the console with a warning icon. - ``` - - @method warn - @for Ember.Logger - @param {*} arguments - */ - warn: consoleMethod('warn') || Ember.K, - - /** - Prints the arguments to the console with an error icon, red text and a stack trace. - You can pass as many arguments as you want and they will be joined together with a space. - - ```javascript - Ember.Logger.error('Danger! Danger!'); - // "Danger! Danger!" will be printed to the console in red text. - ``` - - @method error - @for Ember.Logger - @param {*} arguments - */ - error: consoleMethod('error') || Ember.K, - - /** - Logs the arguments to the console. - You can pass as many arguments as you want and they will be joined together with a space. - - ```javascript - var foo = 1; - Ember.Logger.info('log value of foo:', foo); - // "log value of foo: 1" will be printed to the console - ``` - - @method info - @for Ember.Logger - @param {*} arguments - */ - info: consoleMethod('info') || Ember.K, - - /** - Logs the arguments to the console in blue text. - You can pass as many arguments as you want and they will be joined together with a space. - - ```javascript - var foo = 1; - Ember.Logger.debug('log value of foo:', foo); - // "log value of foo: 1" will be printed to the console - ``` - - @method debug - @for Ember.Logger - @param {*} arguments - */ - debug: consoleMethod('debug') || consoleMethod('info') || Ember.K, - - /** - If the value passed into `Ember.Logger.assert` is not truthy it will throw an error with a stack trace. - - ```javascript - Ember.Logger.assert(true); // undefined - Ember.Logger.assert(true === false); // Throws an Assertion failed error. - ``` - - @method assert - @for Ember.Logger - @param {Boolean} bool Value to test - */ - assert: consoleMethod('assert') || assertPolyfill -}; - - -})(); - - - -(function() { -/** -@module ember-metal -*/ - -var META_KEY = Ember.META_KEY, - metaFor = Ember.meta, - objectDefineProperty = Ember.platform.defineProperty; - -var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; - -// .......................................................... -// DESCRIPTOR -// - -/** - Objects of this type can implement an interface to respond to requests to - get and set. The default implementation handles simple properties. - - You generally won't need to create or subclass this directly. - - @class Descriptor - @namespace Ember - @private - @constructor -*/ -Ember.Descriptor = function() {}; - -// .......................................................... -// DEFINING PROPERTIES API -// - -var MANDATORY_SETTER_FUNCTION = Ember.MANDATORY_SETTER_FUNCTION = function(value) { - Ember.assert("You must use Ember.set() to access this property (of " + this + ")", false); -}; - -var DEFAULT_GETTER_FUNCTION = Ember.DEFAULT_GETTER_FUNCTION = function(name) { - return function() { - var meta = this[META_KEY]; - return meta && meta.values[name]; - }; -}; - -/** - NOTE: This is a low-level method used by other parts of the API. You almost - never want to call this method directly. Instead you should use - `Ember.mixin()` to define new properties. - - Defines a property on an object. This method works much like the ES5 - `Object.defineProperty()` method except that it can also accept computed - properties and other special descriptors. - - Normally this method takes only three parameters. However if you pass an - instance of `Ember.Descriptor` as the third param then you can pass an - optional value as the fourth parameter. This is often more efficient than - creating new descriptor hashes for each property. - - ## Examples - - ```javascript - // ES5 compatible mode - Ember.defineProperty(contact, 'firstName', { - writable: true, - configurable: false, - enumerable: true, - value: 'Charles' - }); - - // define a simple property - Ember.defineProperty(contact, 'lastName', undefined, 'Jolley'); - - // define a computed property - Ember.defineProperty(contact, 'fullName', Ember.computed(function() { - return this.firstName+' '+this.lastName; - }).property('firstName', 'lastName')); - ``` - - @private - @method defineProperty - @for Ember - @param {Object} obj the object to define this property on. This may be a prototype. - @param {String} keyName the name of the property - @param {Ember.Descriptor} [desc] an instance of `Ember.Descriptor` (typically a - computed property) or an ES5 descriptor. - You must provide this or `data` but not both. - @param {*} [data] something other than a descriptor, that will - become the explicit value of this property. -*/ -Ember.defineProperty = function(obj, keyName, desc, data, meta) { - var descs, existingDesc, watching, value; - - if (!meta) meta = metaFor(obj); - descs = meta.descs; - existingDesc = meta.descs[keyName]; - watching = meta.watching[keyName] > 0; - - if (existingDesc instanceof Ember.Descriptor) { - existingDesc.teardown(obj, keyName); - } - - if (desc instanceof Ember.Descriptor) { - value = desc; - - descs[keyName] = desc; - if (MANDATORY_SETTER && watching) { - objectDefineProperty(obj, keyName, { - configurable: true, - enumerable: true, - writable: true, - value: undefined // make enumerable - }); - } else { - obj[keyName] = undefined; // make enumerable - } - - if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - if (desc.func && desc._dependentCPs) { - addImplicitCPs(obj, desc._dependentCPs, meta); - } - } - } else { - descs[keyName] = undefined; // shadow descriptor in proto - if (desc == null) { - value = data; - - if (MANDATORY_SETTER && watching) { - meta.values[keyName] = data; - objectDefineProperty(obj, keyName, { - configurable: true, - enumerable: true, - set: MANDATORY_SETTER_FUNCTION, - get: DEFAULT_GETTER_FUNCTION(keyName) - }); - } else { - obj[keyName] = data; - } - } else { - value = desc; - - // compatibility with ES5 - objectDefineProperty(obj, keyName, desc); - } - } - - // if key is being watched, override chains that - // were initialized with the prototype - if (watching) { Ember.overrideChains(obj, keyName, meta); } - - // The `value` passed to the `didDefineProperty` hook is - // either the descriptor or data, whichever was passed. - if (obj.didDefineProperty) { obj.didDefineProperty(obj, keyName, value); } - - return this; -}; - -if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - var addImplicitCPs = function defineImplicitCPs(obj, implicitCPs, meta) { - var cp, key, length = implicitCPs.length; - - for (var i=0; i 1) { - watching[keyName]--; - } -}; - -})(); - - - -(function() { -var metaFor = Ember.meta, // utils.js - get = Ember.get, // property_get.js - normalizeTuple = Ember.normalizeTuple, // property_get.js - forEach = Ember.ArrayPolyfills.forEach, // array.js - warn = Ember.warn, - watchKey = Ember.watchKey, - unwatchKey = Ember.unwatchKey, - FIRST_KEY = /^([^\.\*]+)/, - META_KEY = Ember.META_KEY; - -function firstKey(path) { - return path.match(FIRST_KEY)[0]; -} - -var pendingQueue = []; - -// attempts to add the pendingQueue chains again. If some of them end up -// back in the queue and reschedule is true, schedules a timeout to try -// again. -Ember.flushPendingChains = function() { - if (pendingQueue.length === 0) { return; } // nothing to do - - var queue = pendingQueue; - pendingQueue = []; - - forEach.call(queue, function(q) { q[0].add(q[1]); }); - - warn('Watching an undefined global, Ember expects watched globals to be setup by the time the run loop is flushed, check for typos', pendingQueue.length === 0); -}; - - -function addChainWatcher(obj, keyName, node) { - if (!obj || ('object' !== typeof obj)) { return; } // nothing to do - - var m = metaFor(obj), nodes = m.chainWatchers; - - if (!m.hasOwnProperty('chainWatchers')) { - nodes = m.chainWatchers = {}; - } - - if (!nodes[keyName]) { nodes[keyName] = []; } - nodes[keyName].push(node); - watchKey(obj, keyName, m); -} - -var removeChainWatcher = Ember.removeChainWatcher = function(obj, keyName, node) { - if (!obj || 'object' !== typeof obj) { return; } // nothing to do - - var m = obj[META_KEY]; - if (m && !m.hasOwnProperty('chainWatchers')) { return; } // nothing to do - - var nodes = m && m.chainWatchers; - - if (nodes && nodes[keyName]) { - nodes = nodes[keyName]; - for (var i = 0, l = nodes.length; i < l; i++) { - if (nodes[i] === node) { nodes.splice(i, 1); } - } - } - unwatchKey(obj, keyName, m); -}; - -// A ChainNode watches a single key on an object. If you provide a starting -// value for the key then the node won't actually watch it. For a root node -// pass null for parent and key and object for value. -var ChainNode = Ember._ChainNode = function(parent, key, value) { - this._parent = parent; - this._key = key; - - // _watching is true when calling get(this._parent, this._key) will - // return the value of this node. - // - // It is false for the root of a chain (because we have no parent) - // and for global paths (because the parent node is the object with - // the observer on it) - this._watching = value===undefined; - - this._value = value; - this._paths = {}; - if (this._watching) { - this._object = parent.value(); - if (this._object) { addChainWatcher(this._object, this._key, this); } - } - - // Special-case: the EachProxy relies on immediate evaluation to - // establish its observers. - // - // TODO: Replace this with an efficient callback that the EachProxy - // can implement. - if (this._parent && this._parent._key === '@each') { - this.value(); - } -}; - -var ChainNodePrototype = ChainNode.prototype; - -function lazyGet(obj, key) { - if (!obj) return undefined; - - var meta = obj[META_KEY]; - // check if object meant only to be a prototype - if (meta && meta.proto === obj) return undefined; - - if (key === "@each") return get(obj, key); - - // if a CP only return cached value - var desc = meta && meta.descs[key]; - if (desc && desc._cacheable) { - if (key in meta.cache) { - return meta.cache[key]; - } else { - return undefined; - } - } - - return get(obj, key); -} - -ChainNodePrototype.value = function() { - if (this._value === undefined && this._watching) { - var obj = this._parent.value(); - this._value = lazyGet(obj, this._key); - } - return this._value; -}; - -ChainNodePrototype.destroy = function() { - if (this._watching) { - var obj = this._object; - if (obj) { removeChainWatcher(obj, this._key, this); } - this._watching = false; // so future calls do nothing - } -}; - -// copies a top level object only -ChainNodePrototype.copy = function(obj) { - var ret = new ChainNode(null, null, obj), - paths = this._paths, path; - for (path in paths) { - if (paths[path] <= 0) { continue; } // this check will also catch non-number vals. - ret.add(path); - } - return ret; -}; - -// called on the root node of a chain to setup watchers on the specified -// path. -ChainNodePrototype.add = function(path) { - var obj, tuple, key, src, paths; - - paths = this._paths; - paths[path] = (paths[path] || 0) + 1; - - obj = this.value(); - tuple = normalizeTuple(obj, path); - - // the path was a local path - if (tuple[0] && tuple[0] === obj) { - path = tuple[1]; - key = firstKey(path); - path = path.slice(key.length+1); - - // global path, but object does not exist yet. - // put into a queue and try to connect later. - } else if (!tuple[0]) { - pendingQueue.push([this, path]); - tuple.length = 0; - return; - - // global path, and object already exists - } else { - src = tuple[0]; - key = path.slice(0, 0-(tuple[1].length+1)); - path = tuple[1]; - } - - tuple.length = 0; - this.chain(key, path, src); -}; - -// called on the root node of a chain to teardown watcher on the specified -// path -ChainNodePrototype.remove = function(path) { - var obj, tuple, key, src, paths; - - paths = this._paths; - if (paths[path] > 0) { paths[path]--; } - - obj = this.value(); - tuple = normalizeTuple(obj, path); - if (tuple[0] === obj) { - path = tuple[1]; - key = firstKey(path); - path = path.slice(key.length+1); - } else { - src = tuple[0]; - key = path.slice(0, 0-(tuple[1].length+1)); - path = tuple[1]; - } - - tuple.length = 0; - this.unchain(key, path); -}; - -ChainNodePrototype.count = 0; - -ChainNodePrototype.chain = function(key, path, src) { - var chains = this._chains, node; - if (!chains) { chains = this._chains = {}; } - - node = chains[key]; - if (!node) { node = chains[key] = new ChainNode(this, key, src); } - node.count++; // count chains... - - // chain rest of path if there is one - if (path && path.length>0) { - key = firstKey(path); - path = path.slice(key.length+1); - node.chain(key, path); // NOTE: no src means it will observe changes... - } -}; - -ChainNodePrototype.unchain = function(key, path) { - var chains = this._chains, node = chains[key]; - - // unchain rest of path first... - if (path && path.length>1) { - key = firstKey(path); - path = path.slice(key.length+1); - node.unchain(key, path); - } - - // delete node if needed. - node.count--; - if (node.count<=0) { - delete chains[node._key]; - node.destroy(); - } - -}; - -ChainNodePrototype.willChange = function(events) { - var chains = this._chains; - if (chains) { - for(var key in chains) { - if (!chains.hasOwnProperty(key)) { continue; } - chains[key].willChange(events); - } - } - - if (this._parent) { this._parent.chainWillChange(this, this._key, 1, events); } -}; - -ChainNodePrototype.chainWillChange = function(chain, path, depth, events) { - if (this._key) { path = this._key + '.' + path; } - - if (this._parent) { - this._parent.chainWillChange(this, path, depth+1, events); - } else { - if (depth > 1) { - events.push(this.value(), path); - } - path = 'this.' + path; - if (this._paths[path] > 0) { - events.push(this.value(), path); - } - } -}; - -ChainNodePrototype.chainDidChange = function(chain, path, depth, events) { - if (this._key) { path = this._key + '.' + path; } - if (this._parent) { - this._parent.chainDidChange(this, path, depth+1, events); - } else { - if (depth > 1) { - events.push(this.value(), path); - } - path = 'this.' + path; - if (this._paths[path] > 0) { - events.push(this.value(), path); - } - } -}; - -ChainNodePrototype.didChange = function(events) { - // invalidate my own value first. - if (this._watching) { - var obj = this._parent.value(); - if (obj !== this._object) { - removeChainWatcher(this._object, this._key, this); - this._object = obj; - addChainWatcher(obj, this._key, this); - } - this._value = undefined; - - // Special-case: the EachProxy relies on immediate evaluation to - // establish its observers. - if (this._parent && this._parent._key === '@each') - this.value(); - } - - // then notify chains... - var chains = this._chains; - if (chains) { - for(var key in chains) { - if (!chains.hasOwnProperty(key)) { continue; } - chains[key].didChange(events); - } - } - - // if no events are passed in then we only care about the above wiring update - if (events === null) { return; } - - // and finally tell parent about my path changing... - if (this._parent) { this._parent.chainDidChange(this, this._key, 1, events); } -}; - -Ember.finishChains = function(obj) { - // We only create meta if we really have to - var m = obj[META_KEY], chains = m && m.chains; - if (chains) { - if (chains.value() !== obj) { - metaFor(obj).chains = chains = chains.copy(obj); - } else { - chains.didChange(null); - } - } -}; - -})(); - - - -(function() { -/** - @module ember-metal - */ - -var forEach = Ember.EnumerableUtils.forEach, -BRACE_EXPANSION = /^((?:[^\.]*\.)*)\{(.*)\}$/; - -/** - Expands `pattern`, invoking `callback` for each expansion. - - The only pattern supported is brace-expansion, anything else will be passed - once to `callback` directly. Brace expansion can only appear at the end of a - pattern, for example as the last item in a chain. - - Example - ```js - function echo(arg){ console.log(arg); } - - Ember.expandProperties('foo.bar', echo); //=> 'foo.bar' - Ember.expandProperties('{foo,bar}', echo); //=> 'foo', 'bar' - Ember.expandProperties('foo.{bar,baz}', echo); //=> 'foo.bar', 'foo.baz' - Ember.expandProperties('{foo,bar}.baz', echo); //=> '{foo,bar}.baz' - ``` - - @method - @private - @param {string} pattern The property pattern to expand. - @param {function} callback The callback to invoke. It is invoked once per - expansion, and is passed the expansion. - */ -Ember.expandProperties = function (pattern, callback) { - var match, prefix, list; - - if (match = BRACE_EXPANSION.exec(pattern)) { - prefix = match[1]; - list = match[2]; - - forEach(list.split(','), function (suffix) { - callback(prefix + suffix); - }); - } else { - callback(pattern); - } -}; - -})(); - - - -(function() { -var metaFor = Ember.meta, // utils.js - typeOf = Ember.typeOf, // utils.js - ChainNode = Ember._ChainNode; // chains.js - -// get the chains for the current object. If the current object has -// chains inherited from the proto they will be cloned and reconfigured for -// the current object. -function chainsFor(obj, meta) { - var m = meta || metaFor(obj), ret = m.chains; - if (!ret) { - ret = m.chains = new ChainNode(null, null, obj); - } else if (ret.value() !== obj) { - ret = m.chains = ret.copy(obj); - } - return ret; -} - -Ember.watchPath = function(obj, keyPath, meta) { - // can't watch length on Array - it is special... - if (keyPath === 'length' && typeOf(obj) === 'array') { return; } - - var m = meta || metaFor(obj), watching = m.watching; - - if (!watching[keyPath]) { // activate watching first time - watching[keyPath] = 1; - chainsFor(obj, m).add(keyPath); - } else { - watching[keyPath] = (watching[keyPath] || 0) + 1; - } -}; - -Ember.unwatchPath = function(obj, keyPath, meta) { - var m = meta || metaFor(obj), watching = m.watching; - - if (watching[keyPath] === 1) { - watching[keyPath] = 0; - chainsFor(obj, m).remove(keyPath); - } else if (watching[keyPath] > 1) { - watching[keyPath]--; - } -}; -})(); - - - -(function() { -/** -@module ember-metal -*/ - -var metaFor = Ember.meta, // utils.js - GUID_KEY = Ember.GUID_KEY, // utils.js - META_KEY = Ember.META_KEY, // utils.js - removeChainWatcher = Ember.removeChainWatcher, - watchKey = Ember.watchKey, // watch_key.js - unwatchKey = Ember.unwatchKey, - watchPath = Ember.watchPath, // watch_path.js - unwatchPath = Ember.unwatchPath, - typeOf = Ember.typeOf, // utils.js - generateGuid = Ember.generateGuid, - IS_PATH = /[\.\*]/; - -// returns true if the passed path is just a keyName -function isKeyName(path) { - return path==='*' || !IS_PATH.test(path); -} - -/** - Starts watching a property on an object. Whenever the property changes, - invokes `Ember.propertyWillChange` and `Ember.propertyDidChange`. This is the - primitive used by observers and dependent keys; usually you will never call - this method directly but instead use higher level methods like - `Ember.addObserver()` - - @private - @method watch - @for Ember - @param obj - @param {String} keyName -*/ -Ember.watch = function(obj, _keyPath, m) { - // can't watch length on Array - it is special... - if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } - - if (isKeyName(_keyPath)) { - watchKey(obj, _keyPath, m); - } else { - watchPath(obj, _keyPath, m); - } -}; - -Ember.isWatching = function isWatching(obj, key) { - var meta = obj[META_KEY]; - return (meta && meta.watching[key]) > 0; -}; - -Ember.watch.flushPending = Ember.flushPendingChains; - -Ember.unwatch = function(obj, _keyPath, m) { - // can't watch length on Array - it is special... - if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } - - if (isKeyName(_keyPath)) { - unwatchKey(obj, _keyPath, m); - } else { - unwatchPath(obj, _keyPath, m); - } -}; - -/** - Call on an object when you first beget it from another object. This will - setup any chained watchers on the object instance as needed. This method is - safe to call multiple times. - - @private - @method rewatch - @for Ember - @param obj -*/ -Ember.rewatch = function(obj) { - var m = obj[META_KEY], chains = m && m.chains; - - // make sure the object has its own guid. - if (GUID_KEY in obj && !obj.hasOwnProperty(GUID_KEY)) { - generateGuid(obj); - } - - // make sure any chained watchers update. - if (chains && chains.value() !== obj) { - m.chains = chains.copy(obj); - } -}; - -var NODE_STACK = []; - -/** - Tears down the meta on an object so that it can be garbage collected. - Multiple calls will have no effect. - - @method destroy - @for Ember - @param {Object} obj the object to destroy - @return {void} -*/ -Ember.destroy = function (obj) { - var meta = obj[META_KEY], node, nodes, key, nodeObject; - if (meta) { - obj[META_KEY] = null; - // remove chainWatchers to remove circular references that would prevent GC - node = meta.chains; - if (node) { - NODE_STACK.push(node); - // process tree - while (NODE_STACK.length > 0) { - node = NODE_STACK.pop(); - // push children - nodes = node._chains; - if (nodes) { - for (key in nodes) { - if (nodes.hasOwnProperty(key)) { - NODE_STACK.push(nodes[key]); - } - } - } - // remove chainWatcher in node object - if (node._watching) { - nodeObject = node._object; - if (nodeObject) { - removeChainWatcher(nodeObject, node._key, node); - } - } - } - } - } -}; - -})(); - - - -(function() { -/** -@module ember-metal -*/ - -Ember.warn("The CP_DEFAULT_CACHEABLE flag has been removed and computed properties are always cached by default. Use `volatile` if you don't want caching.", Ember.ENV.CP_DEFAULT_CACHEABLE !== false); - - -var get = Ember.get, - set = Ember.set, - metaFor = Ember.meta, - a_slice = [].slice, - o_create = Ember.create, - META_KEY = Ember.META_KEY, - watch = Ember.watch, - unwatch = Ember.unwatch; - -var expandProperties = Ember.expandProperties; - - -// .......................................................... -// DEPENDENT KEYS -// - -// data structure: -// meta.deps = { -// 'depKey': { -// 'keyName': count, -// } -// } - -/* - This function returns a map of unique dependencies for a - given object and key. -*/ -function keysForDep(depsMeta, depKey) { - var keys = depsMeta[depKey]; - if (!keys) { - // if there are no dependencies yet for a the given key - // create a new empty list of dependencies for the key - keys = depsMeta[depKey] = {}; - } else if (!depsMeta.hasOwnProperty(depKey)) { - // otherwise if the dependency list is inherited from - // a superclass, clone the hash - keys = depsMeta[depKey] = o_create(keys); - } - return keys; -} - -function metaForDeps(meta) { - return keysForDep(meta, 'deps'); -} - -function addDependentKeys(desc, obj, keyName, meta) { - // the descriptor has a list of dependent keys, so - // add all of its dependent keys. - var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys; - if (!depKeys) return; - - depsMeta = metaForDeps(meta); - - for(idx = 0, len = depKeys.length; idx < len; idx++) { - depKey = depKeys[idx]; - // Lookup keys meta for depKey - keys = keysForDep(depsMeta, depKey); - // Increment the number of times depKey depends on keyName. - keys[keyName] = (keys[keyName] || 0) + 1; - // Watch the depKey - watch(obj, depKey, meta); - } -} - -function removeDependentKeys(desc, obj, keyName, meta) { - // the descriptor has a list of dependent keys, so - // add all of its dependent keys. - var depKeys = desc._dependentKeys, depsMeta, idx, len, depKey, keys; - if (!depKeys) return; - - depsMeta = metaForDeps(meta); - - for(idx = 0, len = depKeys.length; idx < len; idx++) { - depKey = depKeys[idx]; - // Lookup keys meta for depKey - keys = keysForDep(depsMeta, depKey); - // Increment the number of times depKey depends on keyName. - keys[keyName] = (keys[keyName] || 0) - 1; - // Watch the depKey - unwatch(obj, depKey, meta); - } -} - -// .......................................................... -// COMPUTED PROPERTY -// - -/** - A computed property transforms an objects function into a property. - - By default the function backing the computed property will only be called - once and the result will be cached. You can specify various properties - that your computed property is dependent on. This will force the cached - result to be recomputed if the dependencies are modified. - - In the following example we declare a computed property (by calling - `.property()` on the fullName function) and setup the properties - dependencies (depending on firstName and lastName). The fullName function - will be called once (regardless of how many times it is accessed) as long - as it's dependencies have not been changed. Once firstName or lastName are updated - any future calls (or anything bound) to fullName will incorporate the new - values. - - ```javascript - Person = Ember.Object.extend({ - // these will be supplied by `create` - firstName: null, - lastName: null, - - fullName: function() { - var firstName = this.get('firstName'); - var lastName = this.get('lastName'); - - return firstName + ' ' + lastName; - }.property('firstName', 'lastName') - }); - - var tom = Person.create({ - firstName: "Tom", - lastName: "Dale" - }); - - tom.get('fullName') // "Tom Dale" - ``` - - You can also define what Ember should do when setting a computed property. - If you try to set a computed property, it will be invoked with the key and - value you want to set it to. You can also accept the previous value as the - third parameter. - - ```javascript - - Person = Ember.Object.extend({ - // these will be supplied by `create` - firstName: null, - lastName: null, - - fullName: function(key, value, oldValue) { - // getter - if (arguments.length === 1) { - var firstName = this.get('firstName'); - var lastName = this.get('lastName'); - - return firstName + ' ' + lastName; - - // setter - } else { - var name = value.split(" "); - - this.set('firstName', name[0]); - this.set('lastName', name[1]); - - return value; - } - }.property('firstName', 'lastName') - }); - - var person = Person.create(); - person.set('fullName', "Peter Wagenet"); - person.get('firstName') // Peter - person.get('lastName') // Wagenet - ``` - - @class ComputedProperty - @namespace Ember - @extends Ember.Descriptor - @constructor -*/ -function ComputedProperty(func, opts) { - this.func = func; - if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - setDependentKeys(this, opts && opts.dependentKeys); - } else { - this._dependentKeys = opts && opts.dependentKeys; - } - - this._cacheable = (opts && opts.cacheable !== undefined) ? opts.cacheable : true; - this._readOnly = opts && (opts.readOnly !== undefined || !!opts.readOnly); -} - -Ember.ComputedProperty = ComputedProperty; - -ComputedProperty.prototype = new Ember.Descriptor(); - -var ComputedPropertyPrototype = ComputedProperty.prototype; -ComputedPropertyPrototype._dependentKeys = undefined; -ComputedPropertyPrototype._suspended = undefined; -ComputedPropertyPrototype._meta = undefined; - -if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - ComputedPropertyPrototype._dependentCPs = undefined; - ComputedPropertyPrototype.implicitCPKey = undefined; - - ComputedPropertyPrototype.toString = function() { - if (this.implicitCPKey) { - return this.implicitCPKey; - } - return Ember.Descriptor.prototype.toString.apply(this, arguments); - }; -} - -/** - Properties are cacheable by default. Computed property will automatically - cache the return value of your function until one of the dependent keys changes. - - Call `volatile()` to set it into non-cached mode. When in this mode - the computed property will not automatically cache the return value. - - However, if a property is properly observable, there is no reason to disable - caching. - - @method cacheable - @param {Boolean} aFlag optional set to `false` to disable caching - @return {Ember.ComputedProperty} this - @chainable -*/ -ComputedPropertyPrototype.cacheable = function(aFlag) { - this._cacheable = aFlag !== false; - return this; -}; - -/** - Call on a computed property to set it into non-cached mode. When in this - mode the computed property will not automatically cache the return value. - - ```javascript - MyApp.outsideService = Ember.Object.extend({ - value: function() { - return OutsideService.getValue(); - }.property().volatile() - }).create(); - ``` - - @method volatile - @return {Ember.ComputedProperty} this - @chainable -*/ -ComputedPropertyPrototype.volatile = function() { - return this.cacheable(false); -}; - -/** - Call on a computed property to set it into read-only mode. When in this - mode the computed property will throw an error when set. - - ```javascript - MyApp.Person = Ember.Object.extend({ - guid: function() { - return 'guid-guid-guid'; - }.property().readOnly() - }); - - MyApp.person = MyApp.Person.create(); - - MyApp.person.set('guid', 'new-guid'); // will throw an exception - ``` - - @method readOnly - @return {Ember.ComputedProperty} this - @chainable -*/ -ComputedPropertyPrototype.readOnly = function(readOnly) { - this._readOnly = readOnly === undefined || !!readOnly; - return this; -}; - -/** - Sets the dependent keys on this computed property. Pass any number of - arguments containing key paths that this computed property depends on. - - ```javascript - MyApp.President = Ember.Object.extend({ - fullName: Ember.computed(function() { - return this.get('firstName') + ' ' + this.get('lastName'); - - // Tell Ember that this computed property depends on firstName - // and lastName - }).property('firstName', 'lastName') - }); - - MyApp.president = MyApp.President.create({ - firstName: 'Barack', - lastName: 'Obama', - }); - MyApp.president.get('fullName'); // Barack Obama - ``` - - @method property - @param {String} path* zero or more property paths - @return {Ember.ComputedProperty} this - @chainable -*/ -ComputedPropertyPrototype.property = function() { - var args; - - var addArg = function (property) { - args.push(property); - }; - - args = []; - for (var i = 0, l = arguments.length; i < l; i++) { - expandProperties(arguments[i], addArg); - } - - if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - setDependentKeys(this, args); - } else { - this._dependentKeys = args; - } - - return this; -}; - -/** - In some cases, you may want to annotate computed properties with additional - metadata about how they function or what values they operate on. For example, - computed property functions may close over variables that are then no longer - available for introspection. - - You can pass a hash of these values to a computed property like this: - - ``` - person: function() { - var personId = this.get('personId'); - return App.Person.create({ id: personId }); - }.property().meta({ type: App.Person }) - ``` - - The hash that you pass to the `meta()` function will be saved on the - computed property descriptor under the `_meta` key. Ember runtime - exposes a public API for retrieving these values from classes, - via the `metaForProperty()` function. - - @method meta - @param {Hash} meta - @chainable -*/ - -ComputedPropertyPrototype.meta = function(meta) { - if (arguments.length === 0) { - return this._meta || {}; - } else { - this._meta = meta; - return this; - } -}; - -/* impl descriptor API */ -ComputedPropertyPrototype.didChange = function(obj, keyName) { - // _suspended is set via a CP.set to ensure we don't clear - // the cached value set by the setter - if (this._cacheable && this._suspended !== obj) { - var meta = metaFor(obj); - if (keyName in meta.cache) { - delete meta.cache[keyName]; - removeDependentKeys(this, obj, keyName, meta); - } - } -}; - -function finishChains(chainNodes) -{ - for (var i=0, l=chainNodes.length; i 1) { - args = a_slice.call(arguments, 0, -1); - func = a_slice.call(arguments, -1)[0]; - } - - if (typeof func !== "function") { - throw new Ember.Error("Computed Property declared without a property function"); - } - - var cp = new ComputedProperty(func); - - if (args) { - cp.property.apply(cp, args); - } - - return cp; -}; - -/** - Returns the cached value for a property, if one exists. - This can be useful for peeking at the value of a computed - property that is generated lazily, without accidentally causing - it to be created. - - @method cacheFor - @for Ember - @param {Object} obj the object whose property you want to check - @param {String} key the name of the property whose cached value you want - to return - @return {Object} the cached value -*/ -Ember.cacheFor = function cacheFor(obj, key) { - var meta = obj[META_KEY], - cache = meta && meta.cache; - - if (cache && key in cache) { - return cache[key]; - } -}; - -function getProperties(self, propertyNames) { - var ret = {}; - for(var i = 0; i < propertyNames.length; i++) { - ret[propertyNames[i]] = get(self, propertyNames[i]); - } - return ret; -} - -var registerComputed, registerComputedWithProperties; - -if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - var guidFor = Ember.guidFor, - map = Ember.EnumerableUtils.map, - filter = Ember.EnumerableUtils.filter, - typeOf = Ember.typeOf; - - var implicitKey = function (cp) { - return [guidFor(cp)].concat(cp._dependentKeys).join('_').replace(/\./g, '_DOT_'); - }; - - var normalizeDependentKey = function (key) { - if (key instanceof Ember.ComputedProperty) { - return implicitKey(key); - } else { - return key; - } - }; - - var normalizeDependentKeys = function (keys) { - return map(keys, function (key) { - return normalizeDependentKey(key); - }); - }; - - var selectDependentCPs = function (keys) { - return filter(keys, function (key) { - return key instanceof Ember.ComputedProperty; - }); - }; - - var setDependentKeys = function(cp, dependentKeys) { - if (dependentKeys) { - cp._dependentKeys = normalizeDependentKeys(dependentKeys); - cp._dependentCPs = selectDependentCPs(dependentKeys); - } else { - cp._dependentKeys = cp._dependentCPs = []; - } - cp.implicitCPKey = implicitKey(cp); - }; - // expose `normalizeDependentKey[s]` so user CP macros can easily support - // composition - Ember.computed.normalizeDependentKey = normalizeDependentKey; - Ember.computed.normalizeDependentKeys = normalizeDependentKeys; - - registerComputed = function (name, macro) { - Ember.computed[name] = function(dependentKey) { - var args = normalizeDependentKeys(a_slice.call(arguments)); - return Ember.computed(dependentKey, function() { - return macro.apply(this, args); - }); - }; - }; -} - -if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - registerComputedWithProperties = function(name, macro) { - Ember.computed[name] = function() { - var args = a_slice.call(arguments); - var properties = normalizeDependentKeys(args); - - var computed = Ember.computed(function() { - return macro.apply(this, [getProperties(this, properties)]); - }); - - return computed.property.apply(computed, args); - }; - }; -} else { - registerComputed = function (name, macro) { - Ember.computed[name] = function(dependentKey) { - var args = a_slice.call(arguments); - return Ember.computed(dependentKey, function() { - return macro.apply(this, args); - }); - }; - }; - - registerComputedWithProperties = function(name, macro) { - Ember.computed[name] = function() { - var properties = a_slice.call(arguments); - - var computed = Ember.computed(function() { - return macro.apply(this, [getProperties(this, properties)]); - }); - - return computed.property.apply(computed, properties); - }; - }; -} - - -if (Ember.FEATURES.isEnabled('composable-computed-properties')) { - Ember.computed.literal = function (value) { - return Ember.computed(function () { - return value; - }); - }; -} - - - /** - A computed property that returns true if the value of the dependent - property is null, an empty string, empty array, or empty function. - - Note: When using `Ember.computed.empty` to watch an array make sure to - use the `array.[]` syntax so the computed can subscribe to transitions - from empty to non-empty states. - - Example - - ```javascript - var ToDoList = Ember.Object.extend({ - done: Ember.computed.empty('todos.[]') // detect array changes - }); - var todoList = ToDoList.create({todos: ['Unit Test', 'Documentation', 'Release']}); - todoList.get('done'); // false - todoList.get('todos').clear(); // [] - todoList.get('done'); // true - ``` - - @method computed.empty - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which negate - the original value for property - */ - registerComputed('empty', function(dependentKey) { - return Ember.isEmpty(get(this, dependentKey)); - }); - - -/** - A computed property that returns true if the value of the dependent - property is NOT null, an empty string, empty array, or empty function. - - Note: When using `Ember.computed.notEmpty` to watch an array make sure to - use the `array.[]` syntax so the computed can subscribe to transitions - from empty to non-empty states. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - hasStuff: Ember.computed.notEmpty('backpack.[]') - }); - var hamster = Hamster.create({backpack: ['Food', 'Sleeping Bag', 'Tent']}); - hamster.get('hasStuff'); // true - hamster.get('backpack').clear(); // [] - hamster.get('hasStuff'); // false - ``` - - @method computed.notEmpty - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which returns true if - original value for property is not empty. -*/ -registerComputed('notEmpty', function(dependentKey) { - return !Ember.isEmpty(get(this, dependentKey)); -}); - -/** - A computed property that returns true if the value of the dependent - property is null or undefined. This avoids errors from JSLint complaining - about use of ==, which can be technically confusing. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - isHungry: Ember.computed.none('food') - }); - var hamster = Hamster.create(); - hamster.get('isHungry'); // true - hamster.set('food', 'Banana'); - hamster.get('isHungry'); // false - hamster.set('food', null); - hamster.get('isHungry'); // true - ``` - - @method computed.none - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which - returns true if original value for property is null or undefined. -*/ -registerComputed('none', function(dependentKey) { - return Ember.isNone(get(this, dependentKey)); -}); - -/** - A computed property that returns the inverse boolean value - of the original value for the dependent property. - - Example - - ```javascript - var User = Ember.Object.extend({ - isAnonymous: Ember.computed.not('loggedIn') - }); - var user = User.create({loggedIn: false}); - user.get('isAnonymous'); // true - user.set('loggedIn', true); - user.get('isAnonymous'); // false - ``` - - @method computed.not - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which returns - inverse of the original value for property -*/ -registerComputed('not', function(dependentKey) { - return !get(this, dependentKey); -}); - -/** - A computed property that converts the provided dependent property - into a boolean value. - - ```javascript - var Hamster = Ember.Object.extend({ - hasBananas: Ember.computed.bool('numBananas') - }); - var hamster = Hamster.create(); - hamster.get('hasBananas'); // false - hamster.set('numBananas', 0); - hamster.get('hasBananas'); // false - hamster.set('numBananas', 1); - hamster.get('hasBananas'); // true - hamster.set('numBananas', null); - hamster.get('hasBananas'); // false - ``` - - @method computed.bool - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which converts - to boolean the original value for property -*/ -registerComputed('bool', function(dependentKey) { - return !!get(this, dependentKey); -}); - -/** - A computed property which matches the original value for the - dependent property against a given RegExp, returning `true` - if they values matches the RegExp and `false` if it does not. - - Example - - ```javascript - var User = Ember.Object.extend({ - hasValidEmail: Ember.computed.match('email', /^.+@.+\..+$/) - }); - var user = User.create({loggedIn: false}); - user.get('hasValidEmail'); // false - user.set('email', ''); - user.get('hasValidEmail'); // false - user.set('email', 'ember_hamster@example.com'); - user.get('hasValidEmail'); // true - ``` - - @method computed.match - @for Ember - @param {String} dependentKey - @param {RegExp} regexp - @return {Ember.ComputedProperty} computed property which match - the original value for property against a given RegExp -*/ -registerComputed('match', function(dependentKey, regexp) { - var value = get(this, dependentKey); - return typeof value === 'string' ? regexp.test(value) : false; -}); - -/** - A computed property that returns true if the provided dependent property - is equal to the given value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - napTime: Ember.computed.equal('state', 'sleepy') - }); - var hamster = Hamster.create(); - hamster.get('napTime'); // false - hamster.set('state', 'sleepy'); - hamster.get('napTime'); // true - hamster.set('state', 'hungry'); - hamster.get('napTime'); // false - ``` - - @method computed.equal - @for Ember - @param {String} dependentKey - @param {String|Number|Object} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is equal to the given value. -*/ -registerComputed('equal', function(dependentKey, value) { - return get(this, dependentKey) === value; -}); - -/** - A computed property that returns true if the provied dependent property - is greater than the provided value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - hasTooManyBananas: Ember.computed.gt('numBananas', 10) - }); - var hamster = Hamster.create(); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 3); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 11); - hamster.get('hasTooManyBananas'); // true - ``` - - @method computed.gt - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is greater then given value. -*/ -registerComputed('gt', function(dependentKey, value) { - return get(this, dependentKey) > value; -}); - -/** - A computed property that returns true if the provided dependent property - is greater than or equal to the provided value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - hasTooManyBananas: Ember.computed.gte('numBananas', 10) - }); - var hamster = Hamster.create(); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 3); - hamster.get('hasTooManyBananas'); // false - hamster.set('numBananas', 10); - hamster.get('hasTooManyBananas'); // true - ``` - - @method computed.gte - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is greater or equal then given value. -*/ -registerComputed('gte', function(dependentKey, value) { - return get(this, dependentKey) >= value; -}); - -/** - A computed property that returns true if the provided dependent property - is less than the provided value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - needsMoreBananas: Ember.computed.lt('numBananas', 3) - }); - var hamster = Hamster.create(); - hamster.get('needsMoreBananas'); // true - hamster.set('numBananas', 3); - hamster.get('needsMoreBananas'); // false - hamster.set('numBananas', 2); - hamster.get('needsMoreBananas'); // true - ``` - - @method computed.lt - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is less then given value. -*/ -registerComputed('lt', function(dependentKey, value) { - return get(this, dependentKey) < value; -}); - -/** - A computed property that returns true if the provided dependent property - is less than or equal to the provided value. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - needsMoreBananas: Ember.computed.lte('numBananas', 3) - }); - var hamster = Hamster.create(); - hamster.get('needsMoreBananas'); // true - hamster.set('numBananas', 5); - hamster.get('needsMoreBananas'); // false - hamster.set('numBananas', 3); - hamster.get('needsMoreBananas'); // true - ``` - - @method computed.lte - @for Ember - @param {String} dependentKey - @param {Number} value - @return {Ember.ComputedProperty} computed property which returns true if - the original value for property is less or equal then given value. -*/ -registerComputed('lte', function(dependentKey, value) { - return get(this, dependentKey) <= value; -}); - -/** - A computed property that performs a logical `and` on the - original values for the provided dependent properties. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - readyForCamp: Ember.computed.and('hasTent', 'hasBackpack') - }); - var hamster = Hamster.create(); - hamster.get('readyForCamp'); // false - hamster.set('hasTent', true); - hamster.get('readyForCamp'); // false - hamster.set('hasBackpack', true); - hamster.get('readyForCamp'); // true - ``` - - @method computed.and - @for Ember - @param {String} dependentKey* - @return {Ember.ComputedProperty} computed property which performs - a logical `and` on the values of all the original values for properties. -*/ -registerComputedWithProperties('and', function(properties) { - for (var key in properties) { - if (properties.hasOwnProperty(key) && !properties[key]) { - return false; - } - } - return true; -}); - -/** - A computed property which performs a logical `or` on the - original values for the provided dependent properties. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - readyForRain: Ember.computed.or('hasJacket', 'hasUmbrella') - }); - var hamster = Hamster.create(); - hamster.get('readyForRain'); // false - hamster.set('hasJacket', true); - hamster.get('readyForRain'); // true - ``` - - @method computed.or - @for Ember - @param {String} dependentKey* - @return {Ember.ComputedProperty} computed property which performs - a logical `or` on the values of all the original values for properties. -*/ -registerComputedWithProperties('or', function(properties) { - for (var key in properties) { - if (properties.hasOwnProperty(key) && properties[key]) { - return true; - } - } - return false; -}); - -/** - A computed property that returns the first truthy value - from a list of dependent properties. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - hasClothes: Ember.computed.any('hat', 'shirt') - }); - var hamster = Hamster.create(); - hamster.get('hasClothes'); // null - hamster.set('shirt', 'Hawaiian Shirt'); - hamster.get('hasClothes'); // 'Hawaiian Shirt' - ``` - - @method computed.any - @for Ember - @param {String} dependentKey* - @return {Ember.ComputedProperty} computed property which returns - the first truthy value of given list of properties. -*/ -registerComputedWithProperties('any', function(properties) { - for (var key in properties) { - if (properties.hasOwnProperty(key) && properties[key]) { - return properties[key]; - } - } - return null; -}); - -/** - A computed property that returns the array of values - for the provided dependent properties. - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - clothes: Ember.computed.collect('hat', 'shirt') - }); - var hamster = Hamster.create(); - hamster.get('clothes'); // [null, null] - hamster.set('hat', 'Camp Hat'); - hamster.set('shirt', 'Camp Shirt'); - hamster.get('clothes'); // ['Camp Hat', 'Camp Shirt'] - ``` - - @method computed.collect - @for Ember - @param {String} dependentKey* - @return {Ember.ComputedProperty} computed property which maps - values of all passed properties in to an array. -*/ -registerComputedWithProperties('collect', function(properties) { - var res = []; - for (var key in properties) { - if (properties.hasOwnProperty(key)) { - if (Ember.isNone(properties[key])) { - res.push(null); - } else { - res.push(properties[key]); - } - } - } - return res; -}); - -/** - Creates a new property that is an alias for another property - on an object. Calls to `get` or `set` this property behave as - though they were called on the original property. - - ```javascript - Person = Ember.Object.extend({ - name: 'Alex Matchneer', - nomen: Ember.computed.alias('name') - }); - - alex = Person.create(); - alex.get('nomen'); // 'Alex Matchneer' - alex.get('name'); // 'Alex Matchneer' - - alex.set('nomen', '@machty'); - alex.get('name'); // '@machty' - ``` - @method computed.alias - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which creates an - alias to the original value for property. -*/ -Ember.computed.alias = function(dependentKey) { - return Ember.computed(dependentKey, function(key, value) { - if (arguments.length > 1) { - set(this, dependentKey, value); - return value; - } else { - return get(this, dependentKey); - } - }); -}; - -/** - Where `computed.alias` aliases `get` and `set`, and allows for bidirectional - data flow, `computed.oneWay` only provides an aliased `get`. The `set` will - not mutate the upstream property, rather causes the current property to - become the value set. This causes the downstream property to permentantly - diverge from the upstream property. - - Example - - ```javascript - User = Ember.Object.extend({ - firstName: null, - lastName: null, - nickName: Ember.computed.oneWay('firstName') - }); - - user = User.create({ - firstName: 'Teddy', - lastName: 'Zeenny' - }); - - user.get('nickName'); - # 'Teddy' - - user.set('nickName', 'TeddyBear'); - # 'TeddyBear' - - user.get('firstName'); - # 'Teddy' - ``` - - @method computed.oneWay - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which creates a - one way computed property to the original value for property. -*/ -Ember.computed.oneWay = function(dependentKey) { - return Ember.computed(dependentKey, function() { - return get(this, dependentKey); - }); -}; - - - -/** - Where `computed.oneWay` provides oneWay bindings, `computed.readOnly` provides - a readOnly one way binding. Very often when using `computed.oneWay` one does - not also want changes to propogate back up, as they will replace the value. - - This prevents the reverse flow, and also throws an exception when it occurs. - - Example - - ```javascript - User = Ember.Object.extend({ - firstName: null, - lastName: null, - nickName: Ember.computed.readOnly('firstName') - }); - - user = User.create({ - firstName: 'Teddy', - lastName: 'Zeenny' - }); - - user.get('nickName'); - # 'Teddy' - - user.set('nickName', 'TeddyBear'); - # throws Exception - # throw new Ember.Error('Cannot Set: nickName on: ' );` - - user.get('firstName'); - # 'Teddy' - ``` - - @method computed.readOnly - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computed property which creates a - one way computed property to the original value for property. -*/ -Ember.computed.readOnly = function(dependentKey) { - return Ember.computed(dependentKey, function() { - return get(this, dependentKey); - }).readOnly(); -}; - -/** - A computed property that acts like a standard getter and setter, - but returns the value at the provided `defaultPath` if the - property itself has not been set to a value - - Example - - ```javascript - var Hamster = Ember.Object.extend({ - wishList: Ember.computed.defaultTo('favoriteFood') - }); - var hamster = Hamster.create({favoriteFood: 'Banana'}); - hamster.get('wishList'); // 'Banana' - hamster.set('wishList', 'More Unit Tests'); - hamster.get('wishList'); // 'More Unit Tests' - hamster.get('favoriteFood'); // 'Banana' - ``` - - @method computed.defaultTo - @for Ember - @param {String} defaultPath - @return {Ember.ComputedProperty} computed property which acts like - a standard getter and setter, but defaults to the value from `defaultPath`. -*/ -Ember.computed.defaultTo = function(defaultPath) { - return Ember.computed(function(key, newValue, cachedValue) { - if (arguments.length === 1) { - return cachedValue != null ? cachedValue : get(this, defaultPath); - } - return newValue != null ? newValue : get(this, defaultPath); - }); -}; - - -})(); - - - -(function() { -// Ember.tryFinally -/** -@module ember-metal -*/ - -var AFTER_OBSERVERS = ':change', - BEFORE_OBSERVERS = ':before'; - -function changeEvent(keyName) { - return keyName+AFTER_OBSERVERS; -} - -function beforeEvent(keyName) { - return keyName+BEFORE_OBSERVERS; -} - -/** - @method addObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] -*/ -Ember.addObserver = function(obj, _path, target, method) { - Ember.addListener(obj, changeEvent(_path), target, method); - Ember.watch(obj, _path); - - return this; -}; - -Ember.observersFor = function(obj, path) { - return Ember.listenersFor(obj, changeEvent(path)); -}; - -/** - @method removeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] -*/ -Ember.removeObserver = function(obj, _path, target, method) { - Ember.unwatch(obj, _path); - Ember.removeListener(obj, changeEvent(_path), target, method); - - return this; -}; - -/** - @method addBeforeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] -*/ -Ember.addBeforeObserver = function(obj, _path, target, method) { - Ember.addListener(obj, beforeEvent(_path), target, method); - Ember.watch(obj, _path); - - return this; -}; - -// Suspend observer during callback. -// -// This should only be used by the target of the observer -// while it is setting the observed path. -Ember._suspendBeforeObserver = function(obj, path, target, method, callback) { - return Ember._suspendListener(obj, beforeEvent(path), target, method, callback); -}; - -Ember._suspendObserver = function(obj, path, target, method, callback) { - return Ember._suspendListener(obj, changeEvent(path), target, method, callback); -}; - -var map = Ember.ArrayPolyfills.map; - -Ember._suspendBeforeObservers = function(obj, paths, target, method, callback) { - var events = map.call(paths, beforeEvent); - return Ember._suspendListeners(obj, events, target, method, callback); -}; - -Ember._suspendObservers = function(obj, paths, target, method, callback) { - var events = map.call(paths, changeEvent); - return Ember._suspendListeners(obj, events, target, method, callback); -}; - -Ember.beforeObserversFor = function(obj, path) { - return Ember.listenersFor(obj, beforeEvent(path)); -}; - -/** - @method removeBeforeObserver - @for Ember - @param obj - @param {String} path - @param {Object|Function} targetOrMethod - @param {Function|String} [method] -*/ -Ember.removeBeforeObserver = function(obj, _path, target, method) { - Ember.unwatch(obj, _path); - Ember.removeListener(obj, beforeEvent(_path), target, method); - - return this; -}; - -})(); - - - -(function() { -define("backburner/queue", - ["exports"], - function(__exports__) { - "use strict"; - function Queue(daq, name, options) { - this.daq = daq; - this.name = name; - this.options = options; - this._queue = []; - } - - Queue.prototype = { - daq: null, - name: null, - options: 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}; // TODO: test this code path - } - } - - this._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, - options = this.options, - before = options && options.before, - after = options && options.after, - 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) { - method.apply(target, args); - } 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/deferred_action_queues", - ["backburner/queue","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Queue = __dependency1__.Queue; - - function DeferredActionQueues(queueNames, options) { - var queues = this.queues = {}; - this.queueNames = queueNames = queueNames || []; - - var queueName; - for (var i = 0, l = queueNames.length; i < l; i++) { - queueName = queueNames[i]; - queues[queueName] = new Queue(this, queueName, options[queueName]); - } - } - - DeferredActionQueues.prototype = { - queueNames: null, - queues: 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); - } - }, - - flush: function() { - var queues = this.queues, - queueNames = this.queueNames, - queueName, queue, queueItems, priorQueueNameIndex, - queueNameIndex = 0, numberOfQueues = queueNames.length; - - outerloop: - while (queueNameIndex < numberOfQueues) { - queueName = queueNames[queueNameIndex]; - queue = queues[queueName]; - queueItems = queue._queueBeingFlushed = queue._queue.slice(); - queue._queue = []; - - var options = queue.options, - before = options && options.before, - after = options && options.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 (typeof method === 'string') { method = target[method]; } - - // method could have been nullified / canceled during flush - if (method) { - // TODO: error handling - if (args && args.length > 0) { - method.apply(target, args); - } else { - method.call(target); - } - } - - 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", - ["backburner/deferred_action_queues","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var DeferredActionQueues = __dependency1__.DeferredActionQueues; - - var slice = [].slice, - pop = [].pop, - throttlers = [], - debouncees = [], - timers = [], - autorun, laterTimer, laterTimerExpiresAt, - global = this, - NUMBER = /\d+/; - - function isCoercableNumber(number) { - return typeof number === '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 = []; - } - - Backburner.prototype = { - queueNames: null, - options: null, - currentInstance: null, - instanceStack: null, - - begin: function() { - var onBegin = this.options && this.options.onBegin, - previousInstance = this.currentInstance; - - if (previousInstance) { - this.instanceStack.push(previousInstance); - } - - this.currentInstance = new DeferredActionQueues(this.queueNames, this.options); - if (onBegin) { - onBegin(this.currentInstance, previousInstance); - } - }, - - end: function() { - var onEnd = this.options && this.options.onEnd, - currentInstance = this.currentInstance, - nextInstance = null; - - try { - currentInstance.flush(); - } finally { - 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 ret; - this.begin(); - - if (!method) { - method = target; - target = null; - } - - if (typeof method === 'string') { - method = target[method]; - } - - // Prevent Safari double-finally. - var finallyAlreadyCalled = false; - try { - if (arguments.length > 2) { - ret = method.apply(target, slice.call(arguments, 2)); - } else { - ret = method.call(target); - } - } finally { - if (!finallyAlreadyCalled) { - finallyAlreadyCalled = true; - this.end(); - } - } - return ret; - }, - - defer: function(queueName, target, method /* , args */) { - if (!method) { - method = target; - target = null; - } - - if (typeof method === 'string') { - 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 (typeof method === 'string') { - 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); - var length = args.length; - var method, wait, target; - var self = this; - var 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 (typeof methodOrWait === 'function' || typeof methodOrTarget[methodOrWait] === 'function') { - 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(); - } - - methodOrTarget = args[0]; - methodOrArgs = args[1]; - - if (typeof methodOrArgs === 'function' || (typeof methodOrArgs === 'string' && - methodOrTarget !== null && - methodOrArgs in methodOrTarget)) { - target = args.shift(); - method = args.shift(); - } else { - method = args.shift(); - } - } - - var executeAt = (+new Date()) + parseInt(wait, 10); - - if (typeof method === 'string') { - method = target[method]; - } - - function fn() { - method.apply(target, args); - } - - // find position to insert - TODO: binary search - var i, l; - for (i = 0, l = timers.length; i < l; i += 2) { - if (executeAt < timers[i]) { break; } - } - - timers.splice(i, 0, executeAt, fn); - - updateLaterTimer(self, 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 (typeof immediate === "number" || typeof immediate === "string") { - wait = immediate; - immediate = true; - } else { - wait = pop.call(args); - } - - wait = parseInt(wait, 10); - - index = findThrottler(target, method); - if (index > -1) { return throttlers[index]; } // throttled - - timer = global.setTimeout(function() { - if (!immediate) { - self.run.apply(self, args); - } - var index = findThrottler(target, method); - if (index > -1) { throttlers.splice(index, 1); } - }, wait); - - if (immediate) { - self.run.apply(self, args); - } - - throttler = [target, method, timer]; - - 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 (typeof immediate === "number" || typeof immediate === "string") { - wait = immediate; - immediate = false; - } else { - wait = pop.call(args); - } - - wait = parseInt(wait, 10); - // Remove debouncee - index = findDebouncee(target, method); - - if (index > -1) { - debouncee = debouncees[index]; - debouncees.splice(index, 1); - clearTimeout(debouncee[2]); - } - - timer = global.setTimeout(function() { - if (!immediate) { - self.run.apply(self, args); - } - var index = findDebouncee(target, method); - if (index > -1) { - debouncees.splice(index, 1); - } - }, wait); - - if (immediate && index === -1) { - self.run.apply(self, args); - } - - debouncee = [target, method, timer]; - - debouncees.push(debouncee); - - return debouncee; - }, - - cancelTimers: function() { - var i, len; - - for (i = 0, len = throttlers.length; i < len; i++) { - clearTimeout(throttlers[i][2]); - } - throttlers = []; - - for (i = 0, len = debouncees.length; i < len; i++) { - clearTimeout(debouncees[i][2]); - } - debouncees = []; - - if (laterTimer) { - clearTimeout(laterTimer); - laterTimer = null; - } - timers = []; - - if (autorun) { - clearTimeout(autorun); - autorun = null; - } - }, - - hasTimers: function() { - return !!timers.length || 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, throttlers, timer) || - this._cancelItem(findDebouncee, 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]); - - 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; - - function createAutorun(backburner) { - backburner.begin(); - autorun = global.setTimeout(function() { - autorun = null; - backburner.end(); - }); - } - - function updateLaterTimer(self, executeAt, wait) { - if (!laterTimer || executeAt < laterTimerExpiresAt) { - if (laterTimer) { - clearTimeout(laterTimer); - } - laterTimer = global.setTimeout(function() { - laterTimer = null; - laterTimerExpiresAt = null; - executeTimers(self); - }, wait); - laterTimerExpiresAt = executeAt; - } - } - - function executeTimers(self) { - var now = +new Date(), - time, fns, i, l; - - self.run(function() { - // TODO: binary search - for (i = 0, l = timers.length; i < l; i += 2) { - time = timers[i]; - if (time > now) { break; } - } - - 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) { - var debouncee, - index = -1; - - for (var i = 0, l = debouncees.length; i < l; i++) { - debouncee = debouncees[i]; - if (debouncee[0] === target && debouncee[1] === method) { - index = i; - break; - } - } - - return index; - } - - function findThrottler(target, method) { - var throttler, - index = -1; - - for (var i = 0, l = throttlers.length; i < l; i++) { - throttler = throttlers[i]; - if (throttler[0] === target && throttler[1] === method) { - index = i; - break; - } - } - - return index; - } - - __exports__.Backburner = Backburner; - }); -})(); - - - -(function() { -var onBegin = function(current) { - Ember.run.currentRunLoop = current; -}; - -var onEnd = function(current, next) { - Ember.run.currentRunLoop = next; -}; - -var Backburner = requireModule('backburner').Backburner, - backburner = new Backburner(['sync', 'actions', 'destroy'], { - sync: { - before: Ember.beginPropertyChanges, - after: Ember.endPropertyChanges - }, - defaultQueue: 'actions', - onBegin: onBegin, - onEnd: onEnd - }), - slice = [].slice, - concat = [].concat; - -// .......................................................... -// Ember.run - this is ideally the only public API the dev sees -// - -/** - Runs the passed target and method inside of a RunLoop, ensuring any - deferred actions including bindings and views updates are flushed at the - end. - - Normally you should not need to invoke this method yourself. However if - you are implementing raw event handlers when interfacing with other - libraries or plugins, you should probably wrap all of your code inside this - call. - - ```javascript - Ember.run(function() { - // code to be execute within a RunLoop - }); - ``` - - @class run - @namespace Ember - @static - @constructor - @param {Object} [target] target of method to call - @param {Function|String} method Method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Any additional arguments you wish to pass to the method. - @return {Object} return value from invoking the passed function. -*/ -Ember.run = function() { - if (Ember.onerror) { - return onerror(arguments); - } else { - return backburner.run.apply(backburner, arguments); - } -}; - -function onerror(args) { - try { - return backburner.run.apply(backburner, args); - } catch(error) { - Ember.onerror(error); - } -} -/** - If no run-loop is present, it creates a new one. If a run loop is - present it will queue itself to run on the existing run-loops action - queue. - - Please note: This is not for normal usage, and should be used sparingly. - - If invoked when not within a run loop: - - ```javascript - Ember.run.join(function() { - // creates a new run-loop - }); - ``` - - Alternatively, if called within an existing run loop: - - ```javascript - Ember.run(function() { - // creates a new run-loop - Ember.run.join(function() { - // joins with the existing run-loop, and queues for invocation on - // the existing run-loops action queue. - }); - }); - ``` - - @method join - @namespace Ember - @param {Object} [target] target of method to call - @param {Function|String} method Method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Any additional arguments you wish to pass to the method. - @return {Object} Return value from invoking the passed function. Please note, - when called within an existing loop, no return value is possible. -*/ -Ember.run.join = function(target, method /* args */) { - if (!Ember.run.currentRunLoop) { - return Ember.run.apply(Ember.run, arguments); - } - - var args = slice.call(arguments); - args.unshift('actions'); - Ember.run.schedule.apply(Ember.run, args); -}; - -/** - Provides a useful utility for when integrating with non-Ember libraries - that provide asynchronous callbacks. - - Ember utilizes a run-loop to batch and coalesce changes. This works by - marking the start and end of Ember-related Javascript execution. - - When using events such as a View's click handler, Ember wraps the event - handler in a run-loop, but when integrating with non-Ember libraries this - can be tedious. - - For example, the following is rather verbose but is the correct way to combine - third-party events and Ember code. - - ```javascript - var that = this; - jQuery(window).on('resize', function(){ - Ember.run(function(){ - that.handleResize(); - }); - }); - ``` - - To reduce the boilerplate, the following can be used to construct a - run-loop-wrapped callback handler. - - ```javascript - jQuery(window).on('resize', Ember.run.bind(this, this.handleResize)); - ``` - - @method bind - @namespace Ember.run - @param {Object} [target] target of method to call - @param {Function|String} method Method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Any additional arguments you wish to pass to the method. - @return {Object} return value from invoking the passed function. Please note, - when called within an existing loop, no return value is possible. -*/ -Ember.run.bind = function(target, method /* args*/) { - var args = slice.call(arguments); - return function() { - return Ember.run.join.apply(Ember.run, args.concat(slice.call(arguments))); - }; -}; - -Ember.run.backburner = backburner; - -var run = Ember.run; - -Ember.run.currentRunLoop = null; - -Ember.run.queues = backburner.queueNames; - -/** - Begins a new RunLoop. Any deferred actions invoked after the begin will - be buffered until you invoke a matching call to `Ember.run.end()`. This is - a lower-level way to use a RunLoop instead of using `Ember.run()`. - - ```javascript - Ember.run.begin(); - // code to be execute within a RunLoop - Ember.run.end(); - ``` - - @method begin - @return {void} -*/ -Ember.run.begin = function() { - backburner.begin(); -}; - -/** - Ends a RunLoop. This must be called sometime after you call - `Ember.run.begin()` to flush any deferred actions. This is a lower-level way - to use a RunLoop instead of using `Ember.run()`. - - ```javascript - Ember.run.begin(); - // code to be execute within a RunLoop - Ember.run.end(); - ``` - - @method end - @return {void} -*/ -Ember.run.end = function() { - backburner.end(); -}; - -/** - Array of named queues. This array determines the order in which queues - are flushed at the end of the RunLoop. You can define your own queues by - simply adding the queue name to this array. Normally you should not need - to inspect or modify this property. - - @property queues - @type Array - @default ['sync', 'actions', 'destroy'] -*/ - -/** - Adds the passed target/method and any optional arguments to the named - queue to be executed at the end of the RunLoop. If you have not already - started a RunLoop when calling this method one will be started for you - automatically. - - At the end of a RunLoop, any methods scheduled in this way will be invoked. - Methods will be invoked in an order matching the named queues defined in - the `Ember.run.queues` property. - - ```javascript - Ember.run.schedule('sync', this, function() { - // this will be executed in the first RunLoop queue, when bindings are synced - console.log("scheduled on sync queue"); - }); - - Ember.run.schedule('actions', this, function() { - // this will be executed in the 'actions' queue, after bindings have synced. - console.log("scheduled on actions queue"); - }); - - // Note the functions will be run in order based on the run queues order. - // Output would be: - // scheduled on sync queue - // scheduled on actions queue - ``` - - @method schedule - @param {String} queue The name of the queue to schedule against. - Default queues are 'sync' and 'actions' - @param {Object} [target] target object to use as the context when invoking a method. - @param {String|Function} method The method to invoke. If you pass a string it - will be resolved on the target object at the time the scheduled item is - invoked allowing you to change the target function. - @param {Object} [arguments*] Optional arguments to be passed to the queued method. - @return {void} -*/ -Ember.run.schedule = function(queue, target, method) { - checkAutoRun(); - backburner.schedule.apply(backburner, arguments); -}; - -// Used by global test teardown -Ember.run.hasScheduledTimers = function() { - return backburner.hasTimers(); -}; - -// Used by global test teardown -Ember.run.cancelTimers = function () { - backburner.cancelTimers(); -}; - -/** - Immediately flushes any events scheduled in the 'sync' queue. Bindings - use this queue so this method is a useful way to immediately force all - bindings in the application to sync. - - You should call this method anytime you need any changed state to propagate - throughout the app immediately without repainting the UI (which happens - in the later 'render' queue added by the `ember-views` package). - - ```javascript - Ember.run.sync(); - ``` - - @method sync - @return {void} -*/ -Ember.run.sync = function() { - if (backburner.currentInstance) { - backburner.currentInstance.queues.sync.flush(); - } -}; - -/** - Invokes the passed target/method and optional arguments after a specified - period if time. The last parameter of this method must always be a number - of milliseconds. - - You should use this method whenever you need to run some action after a - period of time instead of using `setTimeout()`. This method will ensure that - items that expire during the same script execution cycle all execute - together, which is often more efficient than using a real setTimeout. - - ```javascript - Ember.run.later(myContext, function() { - // code here will execute within a RunLoop in about 500ms with this == myContext - }, 500); - ``` - - @method later - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @param {Number} wait Number of milliseconds to wait. - @return {String} a string you can use to cancel the timer in - `Ember.run.cancel` later. -*/ -Ember.run.later = function(target, method) { - return backburner.later.apply(backburner, arguments); -}; - -/** - Schedule a function to run one time during the current RunLoop. This is equivalent - to calling `scheduleOnce` with the "actions" queue. - - @method once - @param {Object} [target] The target of the method to invoke. - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @return {Object} Timer information for use in cancelling, see `Ember.run.cancel`. -*/ -Ember.run.once = function(target, method) { - checkAutoRun(); - var args = slice.call(arguments); - args.unshift('actions'); - return backburner.scheduleOnce.apply(backburner, args); -}; - -/** - Schedules a function to run one time in a given queue of the current RunLoop. - Calling this method with the same queue/target/method combination will have - no effect (past the initial call). - - Note that although you can pass optional arguments these will not be - considered when looking for duplicates. New arguments will replace previous - calls. - - ```javascript - Ember.run(function() { - var sayHi = function() { console.log('hi'); } - Ember.run.scheduleOnce('afterRender', myContext, sayHi); - Ember.run.scheduleOnce('afterRender', myContext, sayHi); - // sayHi will only be executed once, in the afterRender queue of the RunLoop - }); - ``` - - Also note that passing an anonymous function to `Ember.run.scheduleOnce` will - not prevent additional calls with an identical anonymous function from - scheduling the items multiple times, e.g.: - - ```javascript - function scheduleIt() { - Ember.run.scheduleOnce('actions', myContext, function() { console.log("Closure"); }); - } - scheduleIt(); - scheduleIt(); - // "Closure" will print twice, even though we're using `Ember.run.scheduleOnce`, - // because the function we pass to it is anonymous and won't match the - // previously scheduled operation. - ``` - - Available queues, and their order, can be found at `Ember.run.queues` - - @method scheduleOnce - @param {String} [queue] The name of the queue to schedule against. Default queues are 'sync' and 'actions'. - @param {Object} [target] The target of the method to invoke. - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @return {Object} Timer information for use in cancelling, see `Ember.run.cancel`. -*/ -Ember.run.scheduleOnce = function(queue, target, method) { - checkAutoRun(); - return backburner.scheduleOnce.apply(backburner, arguments); -}; - -/** - Schedules an item to run from within a separate run loop, after - control has been returned to the system. This is equivalent to calling - `Ember.run.later` with a wait time of 1ms. - - ```javascript - Ember.run.next(myContext, function() { - // code to be executed in the next run loop, - // which will be scheduled after the current one - }); - ``` - - Multiple operations scheduled with `Ember.run.next` will coalesce - into the same later run loop, along with any other operations - scheduled by `Ember.run.later` that expire right around the same - time that `Ember.run.next` operations will fire. - - Note that there are often alternatives to using `Ember.run.next`. - For instance, if you'd like to schedule an operation to happen - after all DOM element operations have completed within the current - run loop, you can make use of the `afterRender` run loop queue (added - by the `ember-views` package, along with the preceding `render` queue - where all the DOM element operations happen). Example: - - ```javascript - App.MyCollectionView = Ember.CollectionView.extend({ - didInsertElement: function() { - Ember.run.scheduleOnce('afterRender', this, 'processChildElements'); - }, - processChildElements: function() { - // ... do something with collectionView's child view - // elements after they've finished rendering, which - // can't be done within the CollectionView's - // `didInsertElement` hook because that gets run - // before the child elements have been added to the DOM. - } - }); - ``` - - One benefit of the above approach compared to using `Ember.run.next` is - that you will be able to perform DOM/CSS operations before unprocessed - elements are rendered to the screen, which may prevent flickering or - other artifacts caused by delaying processing until after rendering. - - The other major benefit to the above approach is that `Ember.run.next` - introduces an element of non-determinism, which can make things much - harder to test, due to its reliance on `setTimeout`; it's much harder - to guarantee the order of scheduled operations when they are scheduled - outside of the current run loop, i.e. with `Ember.run.next`. - - @method next - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - If you pass a string it will be resolved on the - target at the time the method is invoked. - @param {Object} [args*] Optional arguments to pass to the timeout. - @return {Object} Timer information for use in cancelling, see `Ember.run.cancel`. -*/ -Ember.run.next = function() { - var args = slice.call(arguments); - args.push(1); - return backburner.later.apply(backburner, args); -}; - -/** - Cancels a scheduled item. Must be a value returned by `Ember.run.later()`, - `Ember.run.once()`, `Ember.run.next()`, `Ember.run.debounce()`, or - `Ember.run.throttle()`. - - ```javascript - var runNext = Ember.run.next(myContext, function() { - // will not be executed - }); - Ember.run.cancel(runNext); - - var runLater = Ember.run.later(myContext, function() { - // will not be executed - }, 500); - Ember.run.cancel(runLater); - - var runOnce = Ember.run.once(myContext, function() { - // will not be executed - }); - Ember.run.cancel(runOnce); - - var throttle = Ember.run.throttle(myContext, function() { - // will not be executed - }, 1, false); - Ember.run.cancel(throttle); - - var debounce = Ember.run.debounce(myContext, function() { - // will not be executed - }, 1); - Ember.run.cancel(debounce); - - var debounceImmediate = Ember.run.debounce(myContext, function() { - // will be executed since we passed in true (immediate) - }, 100, true); - // the 100ms delay until this method can be called again will be cancelled - Ember.run.cancel(debounceImmediate); - ``` - - @method cancel - @param {Object} timer Timer object to cancel - @return {Boolean} true if cancelled or false/undefined if it wasn't found -*/ -Ember.run.cancel = function(timer) { - return backburner.cancel(timer); -}; - -/** - Delay calling the target method until the debounce period has elapsed - with no additional debounce calls. If `debounce` is called again before - the specified time has elapsed, the timer is reset and the entire period - must pass again before the target method is called. - - This method should be used when an event may be called multiple times - but the action should only be called once when the event is done firing. - A common example is for scroll events where you only want updates to - happen once scrolling has ceased. - - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'debounce'}; - - Ember.run.debounce(myContext, myFunc, 150); - - // less than 150ms passes - - Ember.run.debounce(myContext, myFunc, 150); - - // 150ms passes - // myFunc is invoked with context myContext - // console logs 'debounce ran.' one time. - ``` - - Immediate allows you to run the function immediately, but debounce - other calls for this function until the wait time has elapsed. If - `debounce` is called again before the specified time has elapsed, - the timer is reset and the entire period must pass again before - the method can be called again. - - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'debounce'}; - - Ember.run.debounce(myContext, myFunc, 150, true); - - // console logs 'debounce ran.' one time immediately. - // 100ms passes - - Ember.run.debounce(myContext, myFunc, 150, true); - - // 150ms passes and nothing else is logged to the console and - // the debouncee is no longer being watched - - Ember.run.debounce(myContext, myFunc, 150, true); - - // console logs 'debounce ran.' one time immediately. - // 150ms passes and nothing else is logged tot he console and - // the debouncee is no longer being watched - - ``` - - @method debounce - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Optional arguments to pass to the timeout. - @param {Number} wait Number of milliseconds to wait. - @param {Boolean} immediate Trigger the function on the leading instead - of the trailing edge of the wait interval. Defaults to false. - @return {Array} Timer information for use in cancelling, see `Ember.run.cancel`. -*/ -Ember.run.debounce = function() { - return backburner.debounce.apply(backburner, arguments); -}; - -/** - Ensure that the target method is never called more frequently than - the specified spacing period. - - ```javascript - var myFunc = function() { console.log(this.name + ' ran.'); }; - var myContext = {name: 'throttle'}; - - Ember.run.throttle(myContext, myFunc, 150); - // myFunc is invoked with context myContext - - // 50ms passes - Ember.run.throttle(myContext, myFunc, 150); - - // 50ms passes - Ember.run.throttle(myContext, myFunc, 150); - - // 150ms passes - Ember.run.throttle(myContext, myFunc, 150); - // myFunc is invoked with context myContext - // console logs 'throttle ran.' twice, 250ms apart. - ``` - - @method throttle - @param {Object} [target] target of method to invoke - @param {Function|String} method The method to invoke. - May be a function or a string. If you pass a string - then it will be looked up on the passed target. - @param {Object} [args*] Optional arguments to pass to the timeout. - @param {Number} spacing Number of milliseconds to space out requests. - @return {Array} Timer information for use in cancelling, see `Ember.run.cancel`. -*/ -Ember.run.throttle = function() { - return backburner.throttle.apply(backburner, arguments); -}; - -// Make sure it's not an autorun during testing -function checkAutoRun() { - if (!Ember.run.currentRunLoop) { - Ember.assert("You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in an Ember.run", !Ember.testing); - } -} - -})(); - - - -(function() { -// Ember.Logger -// get -// set -// guidFor, meta -// addObserver, removeObserver -// Ember.run.schedule -/** -@module ember-metal -*/ - -// .......................................................... -// CONSTANTS -// - -/** - Debug parameter you can turn on. This will log all bindings that fire to - the console. This should be disabled in production code. Note that you - can also enable this from the console or temporarily. - - @property LOG_BINDINGS - @for Ember - @type Boolean - @default false -*/ -Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS; - -var get = Ember.get, - set = Ember.set, - guidFor = Ember.guidFor, - IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/; - -/** - Returns true if the provided path is global (e.g., `MyApp.fooController.bar`) - instead of local (`foo.bar.baz`). - - @method isGlobalPath - @for Ember - @private - @param {String} path - @return Boolean -*/ -var isGlobalPath = Ember.isGlobalPath = function(path) { - return IS_GLOBAL.test(path); -}; - -function getWithGlobals(obj, path) { - return get(isGlobalPath(path) ? Ember.lookup : obj, path); -} - -// .......................................................... -// BINDING -// - -var Binding = function(toPath, fromPath) { - this._direction = 'fwd'; - this._from = fromPath; - this._to = toPath; - this._directionMap = Ember.Map.create(); -}; - -/** -@class Binding -@namespace Ember -*/ - -Binding.prototype = { - /** - This copies the Binding so it can be connected to another object. - - @method copy - @return {Ember.Binding} `this` - */ - copy: function () { - var copy = new Binding(this._to, this._from); - if (this._oneWay) { copy._oneWay = true; } - return copy; - }, - - // .......................................................... - // CONFIG - // - - /** - This will set `from` property path to the specified value. It will not - attempt to resolve this property path to an actual object until you - connect the binding. - - The binding will search for the property path starting at the root object - you pass when you `connect()` the binding. It follows the same rules as - `get()` - see that method for more information. - - @method from - @param {String} path the property path to connect to - @return {Ember.Binding} `this` - */ - from: function(path) { - this._from = path; - return this; - }, - - /** - This will set the `to` property path to the specified value. It will not - attempt to resolve this property path to an actual object until you - connect the binding. - - The binding will search for the property path starting at the root object - you pass when you `connect()` the binding. It follows the same rules as - `get()` - see that method for more information. - - @method to - @param {String|Tuple} path A property path or tuple - @return {Ember.Binding} `this` - */ - to: function(path) { - this._to = path; - return this; - }, - - /** - Configures the binding as one way. A one-way binding will relay changes - on the `from` side to the `to` side, but not the other way around. This - means that if you change the `to` side directly, the `from` side may have - a different value. - - @method oneWay - @return {Ember.Binding} `this` - */ - oneWay: function() { - this._oneWay = true; - return this; - }, - - /** - @method toString - @return {String} string representation of binding - */ - toString: function() { - var oneWay = this._oneWay ? '[oneWay]' : ''; - return "Ember.Binding<" + guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay; - }, - - // .......................................................... - // CONNECT AND SYNC - // - - /** - Attempts to connect this binding instance so that it can receive and relay - changes. This method will raise an exception if you have not set the - from/to properties yet. - - @method connect - @param {Object} obj The root object for this binding. - @return {Ember.Binding} `this` - */ - connect: function(obj) { - Ember.assert('Must pass a valid object to Ember.Binding.connect()', !!obj); - - var fromPath = this._from, toPath = this._to; - Ember.trySet(obj, toPath, getWithGlobals(obj, fromPath)); - - // add an observer on the object to be notified when the binding should be updated - Ember.addObserver(obj, fromPath, this, this.fromDidChange); - - // if the binding is a two-way binding, also set up an observer on the target - if (!this._oneWay) { Ember.addObserver(obj, toPath, this, this.toDidChange); } - - this._readyToSync = true; - - return this; - }, - - /** - Disconnects the binding instance. Changes will no longer be relayed. You - will not usually need to call this method. - - @method disconnect - @param {Object} obj The root object you passed when connecting the binding. - @return {Ember.Binding} `this` - */ - disconnect: function(obj) { - Ember.assert('Must pass a valid object to Ember.Binding.disconnect()', !!obj); - - var twoWay = !this._oneWay; - - // remove an observer on the object so we're no longer notified of - // changes that should update bindings. - Ember.removeObserver(obj, this._from, this, this.fromDidChange); - - // if the binding is two-way, remove the observer from the target as well - if (twoWay) { Ember.removeObserver(obj, this._to, this, this.toDidChange); } - - this._readyToSync = false; // disable scheduled syncs... - return this; - }, - - // .......................................................... - // PRIVATE - // - - /* called when the from side changes */ - fromDidChange: function(target) { - this._scheduleSync(target, 'fwd'); - }, - - /* called when the to side changes */ - toDidChange: function(target) { - this._scheduleSync(target, 'back'); - }, - - _scheduleSync: function(obj, dir) { - var directionMap = this._directionMap; - var existingDir = directionMap.get(obj); - - // if we haven't scheduled the binding yet, schedule it - if (!existingDir) { - Ember.run.schedule('sync', this, this._sync, obj); - directionMap.set(obj, dir); - } - - // If both a 'back' and 'fwd' sync have been scheduled on the same object, - // default to a 'fwd' sync so that it remains deterministic. - if (existingDir === 'back' && dir === 'fwd') { - directionMap.set(obj, 'fwd'); - } - }, - - _sync: function(obj) { - var log = Ember.LOG_BINDINGS; - - // don't synchronize destroyed objects or disconnected bindings - if (obj.isDestroyed || !this._readyToSync) { return; } - - // get the direction of the binding for the object we are - // synchronizing from - var directionMap = this._directionMap; - var direction = directionMap.get(obj); - - var fromPath = this._from, toPath = this._to; - - directionMap.remove(obj); - - // if we're synchronizing from the remote object... - if (direction === 'fwd') { - var fromValue = getWithGlobals(obj, this._from); - if (log) { - Ember.Logger.log(' ', this.toString(), '->', fromValue, obj); - } - if (this._oneWay) { - Ember.trySet(obj, toPath, fromValue); - } else { - Ember._suspendObserver(obj, toPath, this, this.toDidChange, function () { - Ember.trySet(obj, toPath, fromValue); - }); - } - // if we're synchronizing *to* the remote object - } else if (direction === 'back') { - var toValue = get(obj, this._to); - if (log) { - Ember.Logger.log(' ', this.toString(), '<-', toValue, obj); - } - Ember._suspendObserver(obj, fromPath, this, this.fromDidChange, function () { - Ember.trySet(Ember.isGlobalPath(fromPath) ? Ember.lookup : obj, fromPath, toValue); - }); - } - } - -}; - -function mixinProperties(to, from) { - for (var key in from) { - if (from.hasOwnProperty(key)) { - to[key] = from[key]; - } - } -} - -mixinProperties(Binding, { - - /* - See `Ember.Binding.from`. - - @method from - @static - */ - from: function() { - var C = this, binding = new C(); - return binding.from.apply(binding, arguments); - }, - - /* - See `Ember.Binding.to`. - - @method to - @static - */ - to: function() { - var C = this, binding = new C(); - return binding.to.apply(binding, arguments); - }, - - /** - Creates a new Binding instance and makes it apply in a single direction. - A one-way binding will relay changes on the `from` side object (supplied - as the `from` argument) the `to` side, but not the other way around. - This means that if you change the "to" side directly, the "from" side may have - a different value. - - See `Binding.oneWay`. - - @method oneWay - @param {String} from from path. - @param {Boolean} [flag] (Optional) passing nothing here will make the - binding `oneWay`. You can instead pass `false` to disable `oneWay`, making the - binding two way again. - @return {Ember.Binding} `this` - */ - oneWay: function(from, flag) { - var C = this, binding = new C(null, from); - return binding.oneWay(flag); - } - -}); - -/** - An `Ember.Binding` connects the properties of two objects so that whenever - the value of one property changes, the other property will be changed also. - - ## Automatic Creation of Bindings with `/^*Binding/`-named Properties - - You do not usually create Binding objects directly but instead describe - bindings in your class or object definition using automatic binding - detection. - - Properties ending in a `Binding` suffix will be converted to `Ember.Binding` - instances. The value of this property should be a string representing a path - to another object or a custom binding instanced created using Binding helpers - (see "One Way Bindings"): - - ``` - valueBinding: "MyApp.someController.title" - ``` - - This will create a binding from `MyApp.someController.title` to the `value` - property of your object instance automatically. Now the two values will be - kept in sync. - - ## One Way Bindings - - One especially useful binding customization you can use is the `oneWay()` - helper. This helper tells Ember that you are only interested in - receiving changes on the object you are binding from. For example, if you - are binding to a preference and you want to be notified if the preference - has changed, but your object will not be changing the preference itself, you - could do: - - ``` - bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles") - ``` - - This way if the value of `MyApp.preferencesController.bigTitles` changes the - `bigTitles` property of your object will change also. However, if you - change the value of your `bigTitles` property, it will not update the - `preferencesController`. - - One way bindings are almost twice as fast to setup and twice as fast to - execute because the binding only has to worry about changes to one side. - - You should consider using one way bindings anytime you have an object that - may be created frequently and you do not intend to change a property; only - to monitor it for changes (such as in the example above). - - ## Adding Bindings Manually - - All of the examples above show you how to configure a custom binding, but the - result of these customizations will be a binding template, not a fully active - Binding instance. The binding will actually become active only when you - instantiate the object the binding belongs to. It is useful however, to - understand what actually happens when the binding is activated. - - For a binding to function it must have at least a `from` property and a `to` - property. The `from` property path points to the object/key that you want to - bind from while the `to` path points to the object/key you want to bind to. - - When you define a custom binding, you are usually describing the property - you want to bind from (such as `MyApp.someController.value` in the examples - above). When your object is created, it will automatically assign the value - you want to bind `to` based on the name of your binding key. In the - examples above, during init, Ember objects will effectively call - something like this on your binding: - - ```javascript - binding = Ember.Binding.from(this.valueBinding).to("value"); - ``` - - This creates a new binding instance based on the template you provide, and - sets the to path to the `value` property of the new object. Now that the - binding is fully configured with a `from` and a `to`, it simply needs to be - connected to become active. This is done through the `connect()` method: - - ```javascript - binding.connect(this); - ``` - - Note that when you connect a binding you pass the object you want it to be - connected to. This object will be used as the root for both the from and - to side of the binding when inspecting relative paths. This allows the - binding to be automatically inherited by subclassed objects as well. - - Now that the binding is connected, it will observe both the from and to side - and relay changes. - - If you ever needed to do so (you almost never will, but it is useful to - understand this anyway), you could manually create an active binding by - using the `Ember.bind()` helper method. (This is the same method used by - to setup your bindings on objects): - - ```javascript - Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value"); - ``` - - Both of these code fragments have the same effect as doing the most friendly - form of binding creation like so: - - ```javascript - MyApp.anotherObject = Ember.Object.create({ - valueBinding: "MyApp.someController.value", - - // OTHER CODE FOR THIS OBJECT... - }); - ``` - - Ember's built in binding creation method makes it easy to automatically - create bindings for you. You should always use the highest-level APIs - available, even if you understand how it works underneath. - - @class Binding - @namespace Ember - @since Ember 0.9 -*/ -Ember.Binding = Binding; - - -/** - Global helper method to create a new binding. Just pass the root object - along with a `to` and `from` path to create and connect the binding. - - @method bind - @for Ember - @param {Object} obj The root object of the transform. - @param {String} to The path to the 'to' side of the binding. - Must be relative to obj. - @param {String} from The path to the 'from' side of the binding. - Must be relative to obj or a global path. - @return {Ember.Binding} binding instance -*/ -Ember.bind = function(obj, to, from) { - return new Ember.Binding(to, from).connect(obj); -}; - -/** - @method oneWay - @for Ember - @param {Object} obj The root object of the transform. - @param {String} to The path to the 'to' side of the binding. - Must be relative to obj. - @param {String} from The path to the 'from' side of the binding. - Must be relative to obj or a global path. - @return {Ember.Binding} binding instance -*/ -Ember.oneWay = function(obj, to, from) { - return new Ember.Binding(to, from).oneWay().connect(obj); -}; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-metal -*/ - -var Mixin, REQUIRED, Alias, - a_map = Ember.ArrayPolyfills.map, - a_indexOf = Ember.ArrayPolyfills.indexOf, - a_forEach = Ember.ArrayPolyfills.forEach, - a_slice = [].slice, - o_create = Ember.create, - defineProperty = Ember.defineProperty, - guidFor = Ember.guidFor, - metaFor = Ember.meta, - META_KEY = Ember.META_KEY; - -var expandProperties = Ember.expandProperties; - -function superFunction(){ - var ret, func = this.__nextSuper; - if (func) { - this.__nextSuper = null; - ret = func.apply(this, arguments); - this.__nextSuper = func; - } - return ret; -} - -function mixinsMeta(obj) { - var m = metaFor(obj, true), ret = m.mixins; - if (!ret) { - ret = m.mixins = {}; - } else if (!m.hasOwnProperty('mixins')) { - ret = m.mixins = o_create(ret); - } - return ret; -} - -function initMixin(mixin, args) { - if (args && args.length > 0) { - mixin.mixins = a_map.call(args, function(x) { - if (x instanceof Mixin) { return x; } - - // Note: Manually setup a primitive mixin here. This is the only - // way to actually get a primitive mixin. This way normal creation - // of mixins will give you combined mixins... - var mixin = new Mixin(); - mixin.properties = x; - return mixin; - }); - } - return mixin; -} - -function isMethod(obj) { - return 'function' === typeof obj && - obj.isMethod !== false && - obj !== Boolean && obj !== Object && obj !== Number && obj !== Array && obj !== Date && obj !== String; -} - -var CONTINUE = {}; - -function mixinProperties(mixinsMeta, mixin) { - var guid; - - if (mixin instanceof Mixin) { - guid = guidFor(mixin); - if (mixinsMeta[guid]) { return CONTINUE; } - mixinsMeta[guid] = mixin; - return mixin.properties; - } else { - return mixin; // apply anonymous mixin properties - } -} - -function concatenatedMixinProperties(concatProp, props, values, base) { - var concats; - - // reset before adding each new mixin to pickup concats from previous - concats = values[concatProp] || base[concatProp]; - if (props[concatProp]) { - concats = concats ? concats.concat(props[concatProp]) : props[concatProp]; - } - - return concats; -} - -function giveDescriptorSuper(meta, key, property, values, descs) { - var superProperty; - - // Computed properties override methods, and do not call super to them - if (values[key] === undefined) { - // Find the original descriptor in a parent mixin - superProperty = descs[key]; - } - - // If we didn't find the original descriptor in a parent mixin, find - // it on the original object. - superProperty = superProperty || meta.descs[key]; - - if (!superProperty || !(superProperty instanceof Ember.ComputedProperty)) { - return property; - } - - // Since multiple mixins may inherit from the same parent, we need - // to clone the computed property so that other mixins do not receive - // the wrapped version. - property = o_create(property); - property.func = Ember.wrap(property.func, superProperty.func); - - return property; -} - -function giveMethodSuper(obj, key, method, values, descs) { - var superMethod; - - // Methods overwrite computed properties, and do not call super to them. - if (descs[key] === undefined) { - // Find the original method in a parent mixin - superMethod = values[key]; - } - - // If we didn't find the original value in a parent mixin, find it in - // the original object - superMethod = superMethod || obj[key]; - - // Only wrap the new method if the original method was a function - if ('function' !== typeof superMethod) { - return method; - } - - return Ember.wrap(method, superMethod); -} - -function applyConcatenatedProperties(obj, key, value, values) { - var baseValue = values[key] || obj[key]; - - if (baseValue) { - if ('function' === typeof baseValue.concat) { - return baseValue.concat(value); - } else { - return Ember.makeArray(baseValue).concat(value); - } - } else { - return Ember.makeArray(value); - } -} - -function applyMergedProperties(obj, key, value, values) { - var baseValue = values[key] || obj[key]; - - if (!baseValue) { return value; } - - var newBase = Ember.merge({}, baseValue), - hasFunction = false; - - for (var prop in value) { - if (!value.hasOwnProperty(prop)) { continue; } - - var propValue = value[prop]; - if (isMethod(propValue)) { - // TODO: support for Computed Properties, etc? - hasFunction = true; - newBase[prop] = giveMethodSuper(obj, prop, propValue, baseValue, {}); - } else { - newBase[prop] = propValue; - } - } - - if (hasFunction) { - newBase._super = superFunction; - } - - return newBase; -} - -function addNormalizedProperty(base, key, value, meta, descs, values, concats, mergings) { - if (value instanceof Ember.Descriptor) { - if (value === REQUIRED && descs[key]) { return CONTINUE; } - - // Wrap descriptor function to implement - // __nextSuper() if needed - if (value.func) { - value = giveDescriptorSuper(meta, key, value, values, descs); - } - - descs[key] = value; - values[key] = undefined; - } else { - if ((concats && a_indexOf.call(concats, key) >= 0) || - key === 'concatenatedProperties' || - key === 'mergedProperties') { - value = applyConcatenatedProperties(base, key, value, values); - } else if ((mergings && a_indexOf.call(mergings, key) >= 0)) { - value = applyMergedProperties(base, key, value, values); - } else if (isMethod(value)) { - value = giveMethodSuper(base, key, value, values, descs); - } - - descs[key] = undefined; - values[key] = value; - } -} - -function mergeMixins(mixins, m, descs, values, base, keys) { - var mixin, props, key, concats, mergings, meta; - - function removeKeys(keyName) { - delete descs[keyName]; - delete values[keyName]; - } - - for(var i=0, l=mixins.length; i= 0) { - if (_detect(mixins[loc], targetMixin, seen)) { return true; } - } - return false; -} - -/** - @method detect - @param obj - @return {Boolean} -*/ -MixinPrototype.detect = function(obj) { - if (!obj) { return false; } - if (obj instanceof Mixin) { return _detect(obj, this, {}); } - var m = obj[META_KEY], - mixins = m && m.mixins; - if (mixins) { - return !!mixins[guidFor(this)]; - } - return false; -}; - -MixinPrototype.without = function() { - var ret = new Mixin(this); - ret._without = a_slice.call(arguments); - return ret; -}; - -function _keys(ret, mixin, seen) { - if (seen[guidFor(mixin)]) { return; } - seen[guidFor(mixin)] = true; - - if (mixin.properties) { - var props = mixin.properties; - for (var key in props) { - if (props.hasOwnProperty(key)) { ret[key] = true; } - } - } else if (mixin.mixins) { - a_forEach.call(mixin.mixins, function(x) { _keys(ret, x, seen); }); - } -} - -MixinPrototype.keys = function() { - var keys = {}, seen = {}, ret = []; - _keys(keys, this, seen); - for(var key in keys) { - if (keys.hasOwnProperty(key)) { ret.push(key); } - } - return ret; -}; - -// returns the mixins currently applied to the specified object -// TODO: Make Ember.mixin -Mixin.mixins = function(obj) { - var m = obj[META_KEY], - mixins = m && m.mixins, ret = []; - - if (!mixins) { return ret; } - - for (var key in mixins) { - var mixin = mixins[key]; - - // skip primitive mixins since these are always anonymous - if (!mixin.properties) { ret.push(mixin); } - } - - return ret; -}; - -REQUIRED = new Ember.Descriptor(); -REQUIRED.toString = function() { return '(Required Property)'; }; - -/** - Denotes a required property for a mixin - - @method required - @for Ember -*/ -Ember.required = function() { - return REQUIRED; -}; - -Alias = function(methodName) { - this.methodName = methodName; -}; -Alias.prototype = new Ember.Descriptor(); - -/** - Makes a method available via an additional name. - - ```javascript - App.Person = Ember.Object.extend({ - name: function() { - return 'Tomhuda Katzdale'; - }, - moniker: Ember.aliasMethod('name') - }); - - var goodGuy = App.Person.create() - ``` - - @method aliasMethod - @for Ember - @param {String} methodName name of the method to alias - @return {Ember.Descriptor} -*/ -Ember.aliasMethod = function(methodName) { - return new Alias(methodName); -}; - -// .......................................................... -// OBSERVER HELPER -// - -/** - Specify a method that observes property changes. - - ```javascript - Ember.Object.extend({ - valueObserver: Ember.observer('value', function() { - // Executes whenever the "value" property changes - }) - }); - ``` - - In the future this method may become asynchronous. If you want to ensure - synchronous behavior, use `immediateObserver`. - - Also available as `Function.prototype.observes` if prototype extensions are - enabled. - - @method observer - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func -*/ -Ember.observer = function() { - var func = a_slice.call(arguments, -1)[0]; - var paths; - - var addWatchedProperty = function (path) { paths.push(path); }; - var _paths = a_slice.call(arguments, 0, -1); - - if (typeof func !== "function") { - // revert to old, soft-deprecated argument ordering - - func = arguments[0]; - _paths = a_slice.call(arguments, 1); - } - - paths = []; - - for (var i=0; i<_paths.length; ++i) { - expandProperties(_paths[i], addWatchedProperty); - } - - if (typeof func !== "function") { - throw new Ember.Error("Ember.observer called without a function"); - } - - func.__ember_observes__ = paths; - return func; -}; - -/** - Specify a method that observes property changes. - - ```javascript - Ember.Object.extend({ - valueObserver: Ember.immediateObserver('value', function() { - // Executes whenever the "value" property changes - }) - }); - ``` - - In the future, `Ember.observer` may become asynchronous. In this event, - `Ember.immediateObserver` will maintain the synchronous behavior. - - Also available as `Function.prototype.observesImmediately` if prototype extensions are - enabled. - - @method immediateObserver - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func -*/ -Ember.immediateObserver = function() { - for (var i=0, l=arguments.length; i this.changingFrom ? 'green' : 'red'; - // logic - } - }), - - friendsDidChange: Ember.observer('friends.@each.name', function(obj, keyName) { - // some logic - // obj.get(keyName) returns friends array - }) - }); - ``` - - Also available as `Function.prototype.observesBefore` if prototype extensions are - enabled. - - @method beforeObserver - @for Ember - @param {String} propertyNames* - @param {Function} func - @return func -*/ -Ember.beforeObserver = function() { - var func = a_slice.call(arguments, -1)[0]; - var paths; - - var addWatchedProperty = function(path) { paths.push(path); }; - - var _paths = a_slice.call(arguments, 0, -1); - - if (typeof func !== "function") { - // revert to old, soft-deprecated argument ordering - - func = arguments[0]; - _paths = a_slice.call(arguments, 1); - } - - paths = []; - - for (var i=0; i<_paths.length; ++i) { - expandProperties(_paths[i], addWatchedProperty); - } - - if (typeof func !== "function") { - throw new Ember.Error("Ember.beforeObserver called without a function"); - } - - func.__ember_observesBefore__ = paths; - return func; -}; - -})(); - - - -(function() { -// Provides a way to register library versions with ember. -var forEach = Ember.EnumerableUtils.forEach, - indexOf = Ember.EnumerableUtils.indexOf; - -Ember.libraries = function() { - var libraries = []; - var coreLibIndex = 0; - - var getLibrary = function(name) { - for (var i = 0; i < libraries.length; i++) { - if (libraries[i].name === name) { - return libraries[i]; - } - } - }; - - libraries.register = function(name, version) { - if (!getLibrary(name)) { - libraries.push({name: name, version: version}); - } - }; - - libraries.registerCoreLibrary = function(name, version) { - if (!getLibrary(name)) { - libraries.splice(coreLibIndex++, 0, {name: name, version: version}); - } - }; - - libraries.deRegister = function(name) { - var lib = getLibrary(name); - if (lib) libraries.splice(indexOf(libraries, lib), 1); - }; - - libraries.each = function (callback) { - forEach(libraries, function(lib) { - callback(lib.name, lib.version); - }); - }; - - return libraries; -}(); - -Ember.libraries.registerCoreLibrary('Ember', Ember.VERSION); - -})(); - - - -(function() { -/** -Ember Metal - -@module ember -@submodule ember-metal -*/ - -})(); - -(function() { -/** - @class RSVP - @module RSVP - */ -define("rsvp/all", - ["./promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - - /** - This is a convenient alias for `RSVP.Promise.all`. - - @method all - @for RSVP - @param {Array} array Array of promises. - @param {String} label An optional label. This is useful - for tooling. - @static - */ - __exports__["default"] = function all(array, label) { - return Promise.all(array, label); - }; - }); -define("rsvp/all_settled", - ["./promise","./utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - var isArray = __dependency2__.isArray; - var isNonThenable = __dependency2__.isNonThenable; - - /** - `RSVP.allSettled` is similar to `RSVP.all`, but instead of implementing - a fail-fast method, it waits until all the promises have returned and - shows you all the results. This is useful if you want to handle multiple - promises' failure states together as a set. - - Returns a promise that is fulfilled when all the given promises have been - settled. The return promise is fulfilled with an array of the states of - the promises passed into the `promises` array argument. - - Each state object will either indicate fulfillment or rejection, and - provide the corresponding value or reason. The states will take one of - the following formats: - - ```javascript - { state: 'fulfilled', value: value } - or - { state: 'rejected', reason: reason } - ``` - - Example: - - ```javascript - var promise1 = RSVP.Promise.resolve(1); - var promise2 = RSVP.Promise.reject(new Error('2')); - var promise3 = RSVP.Promise.reject(new Error('3')); - var promises = [ promise1, promise2, promise3 ]; - - RSVP.allSettled(promises).then(function(array){ - // array == [ - // { state: 'fulfilled', value: 1 }, - // { state: 'rejected', reason: Error }, - // { state: 'rejected', reason: Error } - // ] - // Note that for the second item, reason.message will be "2", and for the - // third item, reason.message will be "3". - }, function(error) { - // Not run. (This block would only be called if allSettled had failed, - // for instance if passed an incorrect argument type.) - }); - ``` - - @method allSettled - @for RSVP - @param {Array} promises - @param {String} label - optional string that describes the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled with an array of the settled - states of the constituent promises. - @static - */ - - __exports__["default"] = function allSettled(entries, label) { - return new Promise(function(resolve, reject) { - if (!isArray(entries)) { - throw new TypeError('You must pass an array to allSettled.'); - } - - var remaining = entries.length; - var entry; - - if (remaining === 0) { - resolve([]); - return; - } - - var results = new Array(remaining); - - function fulfilledResolver(index) { - return function(value) { - resolveAll(index, fulfilled(value)); - }; - } - - function rejectedResolver(index) { - return function(reason) { - resolveAll(index, rejected(reason)); - }; - } - - function resolveAll(index, value) { - results[index] = value; - if (--remaining === 0) { - resolve(results); - } - } - - for (var index = 0; index < entries.length; index++) { - entry = entries[index]; - - if (isNonThenable(entry)) { - resolveAll(index, fulfilled(entry)); - } else { - Promise.cast(entry).then(fulfilledResolver(index), rejectedResolver(index)); - } - } - }, label); - }; - - function fulfilled(value) { - return { state: 'fulfilled', value: value }; - } - - function rejected(reason) { - return { state: 'rejected', reason: reason }; - } - }); -define("rsvp/config", - ["./events","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var EventTarget = __dependency1__["default"]; - - var config = { - instrument: false - }; - - EventTarget.mixin(config); - - function configure(name, value) { - if (name === 'onerror') { - // handle for legacy users that expect the actual - // error to be passed to their function added via - // `RSVP.configure('onerror', someFunctionHere);` - config.on('error', value); - return; - } - - if (arguments.length === 2) { - config[name] = value; - } else { - return config[name]; - } - } - - __exports__.config = config; - __exports__.configure = configure; - }); -define("rsvp/defer", - ["./promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - - /** - `RSVP.defer` returns an object similar to jQuery's `$.Deferred`. - `RSVP.defer` should be used when porting over code reliant on `$.Deferred`'s - interface. New code should use the `RSVP.Promise` constructor instead. - - The object returned from `RSVP.defer` is a plain object with three properties: - - * promise - an `RSVP.Promise`. - * reject - a function that causes the `promise` property on this object to - become rejected - * resolve - a function that causes the `promise` property on this object to - become fulfilled. - - Example: - - ```javascript - var deferred = RSVP.defer(); - - deferred.resolve("Success!"); - - defered.promise.then(function(value){ - // value here is "Success!" - }); - ``` - - @method defer - @for RSVP - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Object} - */ - - __exports__["default"] = function defer(label) { - var deferred = { }; - - deferred.promise = new Promise(function(resolve, reject) { - deferred.resolve = resolve; - deferred.reject = reject; - }, label); - - return deferred; - }; - }); -define("rsvp/events", - ["exports"], - function(__exports__) { - "use strict"; - var indexOf = function(callbacks, callback) { - for (var i=0, l=callbacks.length; i 1; - }; - - RSVP.filter(promises, filterFn).then(function(result){ - // result is [ 2, 3 ] - }); - ``` - - If any of the `promises` given to `RSVP.filter` are rejected, the first promise - that is rejected will be given as an argument to the returned promise's - rejection handler. For example: - - ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.reject(new Error("2")); - var promise3 = RSVP.reject(new Error("3")); - var promises = [ promise1, promise2, promise3 ]; - - var filterFn = function(item){ - return item > 1; - }; - - RSVP.filter(promises, filterFn).then(function(array){ - // Code here never runs because there are rejected promises! - }, function(reason) { - // reason.message === "2" - }); - ``` - - `RSVP.filter` will also wait for any promises returned from `filterFn`. - For instance, you may want to fetch a list of users then return a subset - of those users based on some asynchronous operation: - - ```javascript - - var alice = { name: 'alice' }; - var bob = { name: 'bob' }; - var users = [ alice, bob ]; - - var promises = users.map(function(user){ - return RSVP.resolve(user); - }); - - var filterFn = function(user){ - // Here, Alice has permissions to create a blog post, but Bob does not. - return getPrivilegesForUser(user).then(function(privs){ - return privs.can_create_blog_post === true; - }); - }; - RSVP.filter(promises, filterFn).then(function(users){ - // true, because the server told us only Alice can create a blog post. - users.length === 1; - // false, because Alice is the only user present in `users` - users[0] === bob; - }); - ``` - - @method filter - @for RSVP - @param {Array} promises - @param {Function} filterFn - function to be called on each resolved value to - filter the final results. - @param {String} label optional string describing the promise. Useful for - tooling. - @return {Promise} - */ - function filter(promises, filterFn, label) { - return all(promises, label).then(function(values){ - if (!isArray(promises)) { - throw new TypeError('You must pass an array to filter.'); - } - - if (!isFunction(filterFn)){ - throw new TypeError("You must pass a function to filter's second argument."); - } - - return map(promises, filterFn, label).then(function(filterResults){ - var i, - valuesLen = values.length, - filtered = []; - - for (i = 0; i < valuesLen; i++){ - if(filterResults[i]) filtered.push(values[i]); - } - return filtered; - }); - }); - } - - __exports__["default"] = filter; - }); -define("rsvp/hash", - ["./promise","./utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - var isNonThenable = __dependency2__.isNonThenable; - var keysOf = __dependency2__.keysOf; - - /** - `RSVP.hash` is similar to `RSVP.all`, but takes an object instead of an array - for its `promises` argument. - - Returns a promise that is fulfilled when all the given promises have been - fulfilled, or rejected if any of them become rejected. The returned promise - is fulfilled with a hash that has the same key names as the `promises` object - argument. If any of the values in the object are not promises, they will - simply be copied over to the fulfilled object. - - Example: - - ```javascript - var promises = { - myPromise: RSVP.resolve(1), - yourPromise: RSVP.resolve(2), - theirPromise: RSVP.resolve(3), - notAPromise: 4 - }; - - RSVP.hash(promises).then(function(hash){ - // hash here is an object that looks like: - // { - // myPromise: 1, - // yourPromise: 2, - // theirPromise: 3, - // notAPromise: 4 - // } - }); - ```` - - If any of the `promises` given to `RSVP.hash` are rejected, the first promise - that is rejected will be given as the reason to the rejection handler. - - Example: - - ```javascript - var promises = { - myPromise: RSVP.resolve(1), - rejectedPromise: RSVP.reject(new Error("rejectedPromise")), - anotherRejectedPromise: RSVP.reject(new Error("anotherRejectedPromise")), - }; - - RSVP.hash(promises).then(function(hash){ - // Code here never runs because there are rejected promises! - }, function(reason) { - // reason.message === "rejectedPromise" - }); - ``` - - An important note: `RSVP.hash` is intended for plain JavaScript objects that - are just a set of keys and values. `RSVP.hash` will NOT preserve prototype - chains. - - Example: - - ```javascript - function MyConstructor(){ - this.example = RSVP.resolve("Example"); - } - - MyConstructor.prototype = { - protoProperty: RSVP.resolve("Proto Property") - }; - - var myObject = new MyConstructor(); - - RSVP.hash(myObject).then(function(hash){ - // protoProperty will not be present, instead you will just have an - // object that looks like: - // { - // example: "Example" - // } - // - // hash.hasOwnProperty('protoProperty'); // false - // 'undefined' === typeof hash.protoProperty - }); - ``` - - @method hash - @for RSVP - @param {Object} promises - @param {String} label optional string that describes the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled when all properties of `promises` - have been fulfilled, or rejected if any of them become rejected. - @static - */ - __exports__["default"] = function hash(object, label) { - return new Promise(function(resolve, reject){ - var results = {}; - var keys = keysOf(object); - var remaining = keys.length; - var entry, property; - - if (remaining === 0) { - resolve(results); - return; - } - - function fulfilledTo(property) { - return function(value) { - results[property] = value; - if (--remaining === 0) { - resolve(results); - } - }; - } - - function onRejection(reason) { - remaining = 0; - reject(reason); - } - - for (var i = 0; i < keys.length; i++) { - property = keys[i]; - entry = object[property]; - - if (isNonThenable(entry)) { - results[property] = entry; - if (--remaining === 0) { - resolve(results); - } - } else { - Promise.cast(entry).then(fulfilledTo(property), onRejection); - } - } - }); - }; - }); -define("rsvp/instrument", - ["./config","./utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { - "use strict"; - var config = __dependency1__.config; - var now = __dependency2__.now; - - __exports__["default"] = function instrument(eventName, promise, child) { - // instrumentation should not disrupt normal usage. - try { - config.trigger(eventName, { - guid: promise._guidKey + promise._id, - eventName: eventName, - detail: promise._detail, - childGuid: child && promise._guidKey + child._id, - label: promise._label, - timeStamp: now(), - stack: new Error(promise._label).stack - }); - } catch(error) { - setTimeout(function(){ - throw error; - }, 0); - } - }; - }); -define("rsvp/map", - ["./promise","./all","./utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - var all = __dependency2__["default"]; - var isArray = __dependency3__.isArray; - var isFunction = __dependency3__.isFunction; - - /** - `RSVP.map` is similar to JavaScript's native `map` method, except that it - waits for all promises to become fulfilled before running the `mapFn` on - each item in given to `promises`. `RSVP.map` returns a promise that will - become fulfilled with the result of running `mapFn` on the values the promises - become fulfilled with. - - For example: - - ```javascript - - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.resolve(2); - var promise3 = RSVP.resolve(3); - var promises = [ promise1, promise2, promise3 ]; - - var mapFn = function(item){ - return item + 1; - }; - - RSVP.map(promises, mapFn).then(function(result){ - // result is [ 2, 3, 4 ] - }); - ``` - - If any of the `promises` given to `RSVP.map` are rejected, the first promise - that is rejected will be given as an argument to the returned promise's - rejection handler. For example: - - ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.reject(new Error("2")); - var promise3 = RSVP.reject(new Error("3")); - var promises = [ promise1, promise2, promise3 ]; - - var mapFn = function(item){ - return item + 1; - }; - - RSVP.map(promises, mapFn).then(function(array){ - // Code here never runs because there are rejected promises! - }, function(reason) { - // reason.message === "2" - }); - ``` - - `RSVP.map` will also wait if a promise is returned from `mapFn`. For example, - say you want to get all comments from a set of blog posts, but you need - the blog posts first becuase they contain a url to those comments. - - ```javscript - - var mapFn = function(blogPost){ - // getComments does some ajax and returns an RSVP.Promise that is fulfilled - // with some comments data - return getComments(blogPost.comments_url); - }; - - // getBlogPosts does some ajax and returns an RSVP.Promise that is fulfilled - // with some blog post data - RSVP.map(getBlogPosts(), mapFn).then(function(comments){ - // comments is the result of asking the server for the comments - // of all blog posts returned from getBlogPosts() - }); - ``` - - @method map - @for RSVP - @param {Array} promises - @param {Function} mapFn function to be called on each fulfilled promise. - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled with the result of calling - `mapFn` on each fulfilled promise or value when they become fulfilled. - The promise will be rejected if any of the given `promises` become rejected. - @static - */ - __exports__["default"] = function map(promises, mapFn, label) { - return all(promises, label).then(function(results){ - if (!isArray(promises)) { - throw new TypeError('You must pass an array to map.'); - } - - if (!isFunction(mapFn)){ - throw new TypeError("You must pass a function to map's second argument."); - } - - - var resultLen = results.length, - mappedResults = [], - i; - - for (i = 0; i < resultLen; i++){ - mappedResults.push(mapFn(results[i])); - } - - return all(mappedResults, label); - }); - }; - }); -define("rsvp/node", - ["./promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - - var slice = Array.prototype.slice; - - function makeNodeCallbackFor(resolve, reject) { - return function (error, value) { - if (error) { - reject(error); - } else if (arguments.length > 2) { - resolve(slice.call(arguments, 1)); - } else { - resolve(value); - } - }; - } - - /** - `RSVP.denodeify` takes a "node-style" function and returns a function that - will return an `RSVP.Promise`. You can use `denodeify` in Node.js or the - browser when you'd prefer to use promises over using callbacks. For example, - `denodeify` transforms the following: - - ```javascript - var fs = require('fs'); - - fs.readFile('myfile.txt', function(err, data){ - if (err) return handleError(err); - handleData(data); - }); - ``` - - into: - - ```javascript - var fs = require('fs'); - - var readFile = RSVP.denodeify(fs.readFile); - - readFile('myfile.txt').then(handleData, handleError); - ``` - - Using `denodeify` makes it easier to compose asynchronous operations instead - of using callbacks. For example, instead of: - - ```javascript - var fs = require('fs'); - var log = require('some-async-logger'); - - fs.readFile('myfile.txt', function(err, data){ - if (err) return handleError(err); - fs.writeFile('myfile2.txt', data, function(err){ - if (err) throw err; - log('success', function(err) { - if (err) throw err; - }); - }); - }); - ``` - - You can chain the operations together using `then` from the returned promise: - - ```javascript - var fs = require('fs'); - var denodeify = RSVP.denodeify; - var readFile = denodeify(fs.readFile); - var writeFile = denodeify(fs.writeFile); - var log = denodeify(require('some-async-logger')); - - readFile('myfile.txt').then(function(data){ - return writeFile('myfile2.txt', data); - }).then(function(){ - return log('SUCCESS'); - }).then(function(){ - // success handler - }, function(reason){ - // rejection handler - }); - ``` - - @method denodeify - @for RSVP - @param {Function} nodeFunc a "node-style" function that takes a callback as - its last argument. The callback expects an error to be passed as its first - argument (if an error occurred, otherwise null), and the value from the - operation as its second argument ("function(err, value){ }"). - @param {Any} binding optional argument for binding the "this" value when - calling the `nodeFunc` function. - @return {Function} a function that wraps `nodeFunc` to return an - `RSVP.Promise` - @static - */ - __exports__["default"] = function denodeify(nodeFunc, binding) { - return function() { - var nodeArgs = slice.call(arguments), resolve, reject; - var thisArg = this || binding; - - return new Promise(function(resolve, reject) { - Promise.all(nodeArgs).then(function(nodeArgs) { - try { - nodeArgs.push(makeNodeCallbackFor(resolve, reject)); - nodeFunc.apply(thisArg, nodeArgs); - } catch(e) { - reject(e); - } - }); - }); - }; - }; - }); -define("rsvp/promise", - ["./config","./events","./instrument","./utils","./promise/cast","./promise/all","./promise/race","./promise/resolve","./promise/reject","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __exports__) { - "use strict"; - var config = __dependency1__.config; - var EventTarget = __dependency2__["default"]; - var instrument = __dependency3__["default"]; - var objectOrFunction = __dependency4__.objectOrFunction; - var isFunction = __dependency4__.isFunction; - var now = __dependency4__.now; - var cast = __dependency5__["default"]; - var all = __dependency6__["default"]; - var race = __dependency7__["default"]; - var Resolve = __dependency8__["default"]; - var Reject = __dependency9__["default"]; - - var guidKey = 'rsvp_' + now() + '-'; - var counter = 0; - - function noop() {} - - __exports__["default"] = Promise; - - - /** - Promise objects represent the eventual result of an asynchronous operation. The - primary way of interacting with a promise is through its `then` method, which - registers callbacks to receive either a promise’s eventual value or the reason - why the promise cannot be fulfilled. - - Terminology - ----------- - - - `promise` is an object or function with a `then` method whose behavior conforms to this specification. - - `thenable` is an object or function that defines a `then` method. - - `value` is any legal JavaScript value (including undefined, a thenable, or a promise). - - `exception` is a value that is thrown using the throw statement. - - `reason` is a value that indicates why a promise was rejected. - - `settled` the final resting state of a promise, fulfilled or rejected. - - A promise can be in one of three states: pending, fulfilled, or rejected. - - Promises that are fulfilled have a fulfillment value and are in the fulfilled - state. Promises that are rejected have a rejection reason and are in the - rejected state. A fulfillment value is never a thenable. Similarly, a - rejection reason is never a thenable. - - Promises can also be said to *resolve* a value. If this value is also a - promise, then the original promise's settled state will match the value's - settled state. So a promise that *resolves* a promise that rejects will - itself reject, and a promise that *resolves* a promise that fulfills will - itself fulfill. - - - Basic Usage: - ------------ - - ```js - var promise = new Promise(function(resolve, reject) { - // on success - resolve(value); - - // on failure - reject(reason); - }); - - promise.then(function(value) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` - - Advanced Usage: - --------------- - - Promises shine when abstracting away asynchronous interactions such as - `XMLHttpRequest`s. - - ```js - function getJSON(url) { - return new Promise(function(resolve, reject){ - var xhr = new XMLHttpRequest(); - - xhr.open('GET', url); - xhr.onreadystatechange = handler; - xhr.responseType = 'json'; - xhr.setRequestHeader('Accept', 'application/json'); - xhr.send(); - - function handler() { - if (this.readyState === this.DONE) { - if (this.status === 200) { - resolve(this.response); - } else { - reject(new Error("getJSON: `" + url + "` failed with status: [" + this.status + "]"); - } - } - }; - }); - } - - getJSON('/posts.json').then(function(json) { - // on fulfillment - }, function(reason) { - // on rejection - }); - ``` - - Unlike callbacks, promises are great composable primitives. - - ```js - Promise.all([ - getJSON('/posts'), - getJSON('/comments') - ]).then(function(values){ - values[0] // => postsJSON - values[1] // => commentsJSON - - return values; - }); - ``` - - @class RSVP.Promise - @param {function} - @param {String} label optional string for labeling the promise. - Useful for tooling. - @constructor - */ - function Promise(resolver, label) { - if (!isFunction(resolver)) { - throw new TypeError('You must pass a resolver function as the first argument to the promise constructor'); - } - - if (!(this instanceof Promise)) { - throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function."); - } - - this._id = counter++; - this._label = label; - this._subscribers = []; - - if (config.instrument) { - instrument('created', this); - } - - if (noop !== resolver) { - invokeResolver(resolver, this); - } - } - - function invokeResolver(resolver, promise) { - function resolvePromise(value) { - resolve(promise, value); - } - - function rejectPromise(reason) { - reject(promise, reason); - } - - try { - resolver(resolvePromise, rejectPromise); - } catch(e) { - rejectPromise(e); - } - } - - Promise.cast = cast; - Promise.all = all; - Promise.race = race; - Promise.resolve = Resolve; - Promise.reject = Reject; - - var PENDING = void 0; - var SEALED = 0; - var FULFILLED = 1; - var REJECTED = 2; - - function subscribe(parent, child, onFulfillment, onRejection) { - var subscribers = parent._subscribers; - var length = subscribers.length; - - subscribers[length] = child; - subscribers[length + FULFILLED] = onFulfillment; - subscribers[length + REJECTED] = onRejection; - } - - function publish(promise, settled) { - var child, callback, subscribers = promise._subscribers, detail = promise._detail; - - if (config.instrument) { - instrument(settled === FULFILLED ? 'fulfilled' : 'rejected', promise); - } - - for (var i = 0; i < subscribers.length; i += 3) { - child = subscribers[i]; - callback = subscribers[i + settled]; - - invokeCallback(settled, child, callback, detail); - } - - promise._subscribers = null; - } - - Promise.prototype = { - constructor: Promise, - - _id: undefined, - _guidKey: guidKey, - _label: undefined, - - _state: undefined, - _detail: undefined, - _subscribers: undefined, - - _onerror: function (reason) { - config.trigger('error', reason); - }, - - /** - The primary way of interacting with a promise is through its `then` method, - which registers callbacks to receive either a promise's eventual value or the - reason why the promise cannot be fulfilled. - - ```js - findUser().then(function(user){ - // user is available - }, function(reason){ - // user is unavailable, and you are given the reason why - }); - ``` - - Chaining - -------- - - The return value of `then` is itself a promise. This second, "downstream" - promise is resolved with the return value of the first promise's fulfillment - or rejection handler, or rejected if the handler throws an exception. - - ```js - findUser().then(function (user) { - return user.name; - }, function (reason) { - return "default name"; - }).then(function (userName) { - // If `findUser` fulfilled, `userName` will be the user's name, otherwise it - // will be `"default name"` - }); - - findUser().then(function (user) { - throw new Error("Found user, but still unhappy"); - }, function (reason) { - throw new Error("`findUser` rejected and we're unhappy"); - }).then(function (value) { - // never reached - }, function (reason) { - // if `findUser` fulfilled, `reason` will be "Found user, but still unhappy". - // If `findUser` rejected, `reason` will be "`findUser` rejected and we're unhappy". - }); - ``` - If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream. - - ```js - findUser().then(function (user) { - throw new PedagogicalException("Upstream error"); - }).then(function (value) { - // never reached - }).then(function (value) { - // never reached - }, function (reason) { - // The `PedgagocialException` is propagated all the way down to here - }); - ``` - - Assimilation - ------------ - - Sometimes the value you want to propagate to a downstream promise can only be - retrieved asynchronously. This can be achieved by returning a promise in the - fulfillment or rejection handler. The downstream promise will then be pending - until the returned promise is settled. This is called *assimilation*. - - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // The user's comments are now available - }); - ``` - - If the assimliated promise rejects, then the downstream promise will also reject. - - ```js - findUser().then(function (user) { - return findCommentsByAuthor(user); - }).then(function (comments) { - // If `findCommentsByAuthor` fulfills, we'll have the value here - }, function (reason) { - // If `findCommentsByAuthor` rejects, we'll have the reason here - }); - ``` - - Simple Example - -------------- - - Synchronous Example - - ```javascript - var result; - - try { - result = findResult(); - // success - } catch(reason) { - // failure - } - ``` - - Errback Example - - ```js - findResult(function(result, err){ - if (err) { - // failure - } else { - // success - } - }); - ``` - - Promise Example; - - ```javascript - findResult().then(function(result){ - // success - }, function(reason){ - // failure - }); - ``` - - Advanced Example - -------------- - - Synchronous Example - - ```javascript - var author, books; - - try { - author = findAuthor(); - books = findBooksByAuthor(author); - // success - } catch(reason) { - // failure - } - ``` - - Errback Example - - ```js - - function foundBooks(books) { - - } - - function failure(reason) { - - } - - findAuthor(function(author, err){ - if (err) { - failure(err); - // failure - } else { - try { - findBoooksByAuthor(author, function(books, err) { - if (err) { - failure(err); - } else { - try { - foundBooks(books); - } catch(reason) { - failure(reason); - } - } - }); - } catch(error) { - failure(err); - } - // success - } - }); - ``` - - Promise Example; - - ```javascript - findAuthor(). - then(findBooksByAuthor). - then(function(books){ - // found books - }).catch(function(reason){ - // something went wrong - }); - ``` - - @method then - @param {Function} onFulfilled - @param {Function} onRejected - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} - */ - then: function(onFulfillment, onRejection, label) { - var promise = this; - this._onerror = null; - - var thenPromise = new this.constructor(noop, label); - - if (this._state) { - var callbacks = arguments; - config.async(function invokePromiseCallback() { - invokeCallback(promise._state, thenPromise, callbacks[promise._state - 1], promise._detail); - }); - } else { - subscribe(this, thenPromise, onFulfillment, onRejection); - } - - if (config.instrument) { - instrument('chained', promise, thenPromise); - } - - return thenPromise; - }, - - /** - `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same - as the catch block of a try/catch statement. - - ```js - function findAuthor(){ - throw new Error("couldn't find that author"); - } - - // synchronous - try { - findAuthor(); - } catch(reason) { - // something went wrong - } - - // async with promises - findAuthor().catch(function(reason){ - // something went wrong - }); - ``` - - @method catch - @param {Function} onRejection - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} - */ - 'catch': function(onRejection, label) { - return this.then(null, onRejection, label); - }, - - /** - `finally` will be invoked regardless of the promise's fate just as native - try/catch/finally behaves - - Synchronous example: - - ```js - findAuthor() { - if (Math.random() > 0.5) { - throw new Error(); - } - return new Author(); - } - - try { - return findAuthor(); // succeed or fail - } catch(error) { - return findOtherAuther(); - } finally { - // always runs - // doesn't affect the return value - } - ``` - - Asynchronous example: - - ```js - findAuthor().catch(function(reason){ - return findOtherAuther(); - }).finally(function(){ - // author was either found, or not - }); - ``` - - @method finally - @param {Function} callback - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} - */ - 'finally': function(callback, label) { - var constructor = this.constructor; - - return this.then(function(value) { - return constructor.cast(callback()).then(function(){ - return value; - }); - }, function(reason) { - return constructor.cast(callback()).then(function(){ - throw reason; - }); - }, label); - } - }; - - function invokeCallback(settled, promise, callback, detail) { - var hasCallback = isFunction(callback), - value, error, succeeded, failed; - - if (hasCallback) { - try { - value = callback(detail); - succeeded = true; - } catch(e) { - failed = true; - error = e; - } - } else { - value = detail; - succeeded = true; - } - - if (handleThenable(promise, value)) { - return; - } else if (hasCallback && succeeded) { - resolve(promise, value); - } else if (failed) { - reject(promise, error); - } else if (settled === FULFILLED) { - resolve(promise, value); - } else if (settled === REJECTED) { - reject(promise, value); - } - } - - function handleThenable(promise, value) { - var then = null, - resolved; - - try { - if (promise === value) { - throw new TypeError("A promises callback cannot return that same promise."); - } - - if (objectOrFunction(value)) { - then = value.then; - - if (isFunction(then)) { - then.call(value, function(val) { - if (resolved) { return true; } - resolved = true; - - if (value !== val) { - resolve(promise, val); - } else { - fulfill(promise, val); - } - }, function(val) { - if (resolved) { return true; } - resolved = true; - - reject(promise, val); - }, 'derived from: ' + (promise._label || ' unknown promise')); - - return true; - } - } - } catch (error) { - if (resolved) { return true; } - reject(promise, error); - return true; - } - - return false; - } - - function resolve(promise, value) { - if (promise === value) { - fulfill(promise, value); - } else if (!handleThenable(promise, value)) { - fulfill(promise, value); - } - } - - function fulfill(promise, value) { - if (promise._state !== PENDING) { return; } - promise._state = SEALED; - promise._detail = value; - - config.async(publishFulfillment, promise); - } - - function reject(promise, reason) { - if (promise._state !== PENDING) { return; } - promise._state = SEALED; - promise._detail = reason; - - config.async(publishRejection, promise); - } - - function publishFulfillment(promise) { - publish(promise, promise._state = FULFILLED); - } - - function publishRejection(promise) { - if (promise._onerror) { - promise._onerror(promise._detail); - } - - publish(promise, promise._state = REJECTED); - } - }); -define("rsvp/promise/all", - ["../utils","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var isArray = __dependency1__.isArray; - var isNonThenable = __dependency1__.isNonThenable; - - /** - `RSVP.Promise.all` accepts an array of promises, and returns a new promise which - is fulfilled with an array of fulfillment values for the passed promises, or - rejected with the reason of the first passed promise to be rejected. It casts all - elements of the passed iterable to promises as it runs this algorithm. - - Example: - - ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.resolve(2); - var promise3 = RSVP.resolve(3); - var promises = [ promise1, promise2, promise3 ]; - - RSVP.Promise.all(promises).then(function(array){ - // The array here would be [ 1, 2, 3 ]; - }); - ``` - - If any of the `promises` given to `RSVP.all` are rejected, the first promise - that is rejected will be given as an argument to the returned promises's - rejection handler. For example: - - Example: - - ```javascript - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.reject(new Error("2")); - var promise3 = RSVP.reject(new Error("3")); - var promises = [ promise1, promise2, promise3 ]; - - RSVP.Promise.all(promises).then(function(array){ - // Code here never runs because there are rejected promises! - }, function(error) { - // error.message === "2" - }); - ``` - - @method all - @for Ember.RSVP.Promise - @param {Array} entries array of promises - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} promise that is fulfilled when all `promises` have been - fulfilled, or rejected if any of them become rejected. - @static - */ - __exports__["default"] = function all(entries, label) { - - /*jshint validthis:true */ - var Constructor = this; - - return new Constructor(function(resolve, reject) { - if (!isArray(entries)) { - throw new TypeError('You must pass an array to all.'); - } - - var remaining = entries.length; - var results = new Array(remaining); - var entry, pending = true; - - if (remaining === 0) { - resolve(results); - return; - } - - function fulfillmentAt(index) { - return function(value) { - results[index] = value; - if (--remaining === 0) { - resolve(results); - } - }; - } - - function onRejection(reason) { - remaining = 0; - reject(reason); - } - - for (var index = 0; index < entries.length; index++) { - entry = entries[index]; - if (isNonThenable(entry)) { - results[index] = entry; - if (--remaining === 0) { - resolve(results); - } - } else { - Constructor.cast(entry).then(fulfillmentAt(index), onRejection); - } - } - }, label); - }; - }); -define("rsvp/promise/cast", - ["exports"], - function(__exports__) { - "use strict"; - /** - `RSVP.Promise.cast` coerces its argument to a promise, or returns the - argument if it is already a promise which shares a constructor with the caster. - - Example: - - ```javascript - var promise = RSVP.Promise.resolve(1); - var casted = RSVP.Promise.cast(promise); - - console.log(promise === casted); // true - ``` - - In the case of a promise whose constructor does not match, it is assimilated. - The resulting promise will fulfill or reject based on the outcome of the - promise being casted. - - Example: - - ```javascript - var thennable = $.getJSON('/api/foo'); - var casted = RSVP.Promise.cast(thennable); - - console.log(thennable === casted); // false - console.log(casted instanceof RSVP.Promise) // true - - casted.then(function(data) { - // data is the value getJSON fulfills with - }); - ``` - - In the case of a non-promise, a promise which will fulfill with that value is - returned. - - Example: - - ```javascript - var value = 1; // could be a number, boolean, string, undefined... - var casted = RSVP.Promise.cast(value); - - console.log(value === casted); // false - console.log(casted instanceof RSVP.Promise) // true - - casted.then(function(val) { - val === value // => true - }); - ``` - - `RSVP.Promise.cast` is similar to `RSVP.Promise.resolve`, but `RSVP.Promise.cast` differs in the - following ways: - - * `RSVP.Promise.cast` serves as a memory-efficient way of getting a promise, when you - have something that could either be a promise or a value. RSVP.resolve - will have the same effect but will create a new promise wrapper if the - argument is a promise. - * `RSVP.Promise.cast` is a way of casting incoming thenables or promise subclasses to - promises of the exact class specified, so that the resulting object's `then` is - ensured to have the behavior of the constructor you are calling cast on (i.e., RSVP.Promise). - - @method cast - @param {Object} object to be casted - @param {String} label optional string for labeling the promise. - Useful for tooling. - @return {Promise} promise - @static - */ - - __exports__["default"] = function cast(object, label) { - /*jshint validthis:true */ - var Constructor = this; - - if (object && typeof object === 'object' && object.constructor === Constructor) { - return object; - } - - return new Constructor(function(resolve) { - resolve(object); - }, label); - }; - }); -define("rsvp/promise/race", - ["../utils","exports"], - function(__dependency1__, __exports__) { - "use strict"; - /* global toString */ - - var isArray = __dependency1__.isArray; - var isFunction = __dependency1__.isFunction; - var isNonThenable = __dependency1__.isNonThenable; - - /** - `RSVP.Promise.race` returns a new promise which is settled in the same way as the - first passed promise to settle. - - Example: - - ```javascript - var promise1 = new RSVP.Promise(function(resolve, reject){ - setTimeout(function(){ - resolve("promise 1"); - }, 200); - }); - - var promise2 = new RSVP.Promise(function(resolve, reject){ - setTimeout(function(){ - resolve("promise 2"); - }, 100); - }); - - RSVP.Promise.race([promise1, promise2]).then(function(result){ - // result === "promise 2" because it was resolved before promise1 - // was resolved. - }); - ``` - - `RSVP.Promise.race` is deterministic in that only the state of the first - settled promise matters. For example, even if other promises given to the - `promises` array argument are resolved, but the first settled promise has - become rejected before the other promises became fulfilled, the returned - promise will become rejected: - - ```javascript - var promise1 = new RSVP.Promise(function(resolve, reject){ - setTimeout(function(){ - resolve("promise 1"); - }, 200); - }); - - var promise2 = new RSVP.Promise(function(resolve, reject){ - setTimeout(function(){ - reject(new Error("promise 2")); - }, 100); - }); - - RSVP.Promise.race([promise1, promise2]).then(function(result){ - // Code here never runs - }, function(reason){ - // reason.message === "promise2" because promise 2 became rejected before - // promise 1 became fulfilled - }); - ``` - - An example real-world use case is implementing timeouts: - - ```javascript - RSVP.Promise.race([ajax('foo.json'), timeout(5000)]) - ``` - - @method race - @param {Array} promises array of promises to observe - @param {String} label optional string for describing the promise returned. - Useful for tooling. - @return {Promise} a promise which settles in the same way as the first passed - promise to settle. - @static - */ - __exports__["default"] = function race(entries, label) { - /*jshint validthis:true */ - var Constructor = this, entry; - - return new Constructor(function(resolve, reject) { - if (!isArray(entries)) { - throw new TypeError('You must pass an array to race.'); - } - - var pending = true; - - function onFulfillment(value) { if (pending) { pending = false; resolve(value); } } - function onRejection(reason) { if (pending) { pending = false; reject(reason); } } - - for (var i = 0; i < entries.length; i++) { - entry = entries[i]; - if (isNonThenable(entry)) { - pending = false; - resolve(entry); - return; - } else { - Constructor.cast(entry).then(onFulfillment, onRejection); - } - } - }, label); - }; - }); -define("rsvp/promise/reject", - ["exports"], - function(__exports__) { - "use strict"; - /** - `RSVP.Promise.reject` returns a promise rejected with the passed `reason`. - It is shorthand for the following: - - ```javascript - var promise = new RSVP.Promise(function(resolve, reject){ - reject(new Error('WHOOPS')); - }); - - promise.then(function(value){ - // Code here doesn't run because the promise is rejected! - }, function(reason){ - // reason.message === 'WHOOPS' - }); - ``` - - Instead of writing the above, your code now simply becomes the following: - - ```javascript - var promise = RSVP.Promise.reject(new Error('WHOOPS')); - - promise.then(function(value){ - // Code here doesn't run because the promise is rejected! - }, function(reason){ - // reason.message === 'WHOOPS' - }); - ``` - - @method reject - @param {Any} reason value that the returned promise will be rejected with. - @param {String} label optional string for identifying the returned promise. - Useful for tooling. - @return {Promise} a promise rejected with the given `reason`. - @static - */ - __exports__["default"] = function reject(reason, label) { - /*jshint validthis:true */ - var Constructor = this; - - return new Constructor(function (resolve, reject) { - reject(reason); - }, label); - }; - }); -define("rsvp/promise/resolve", - ["exports"], - function(__exports__) { - "use strict"; - /** - `RSVP.Promise.resolve` returns a promise that will become resolved with the - passed `value`. It is shorthand for the following: - - ```javascript - var promise = new RSVP.Promise(function(resolve, reject){ - resolve(1); - }); - - promise.then(function(value){ - // value === 1 - }); - ``` - - Instead of writing the above, your code now simply becomes the following: - - ```javascript - var promise = RSVP.Promise.resolve(1); - - promise.then(function(value){ - // value === 1 - }); - ``` - - @method resolve - @param {Any} value value that the returned promise will be resolved with - @param {String} label optional string for identifying the returned promise. - Useful for tooling. - @return {Promise} a promise that will become fulfilled with the given - `value` - @static - */ - __exports__["default"] = function resolve(value, label) { - /*jshint validthis:true */ - var Constructor = this; - - return new Constructor(function(resolve, reject) { - resolve(value); - }, label); - }; - }); -define("rsvp/race", - ["./promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - - /** - This is a convenient alias for `RSVP.Promise.race`. - - @method race - @param {Array} array Array of promises. - @param {String} label An optional label. This is useful - for tooling. - @static - */ - __exports__["default"] = function race(array, label) { - return Promise.race(array, label); - }; - }); -define("rsvp/reject", - ["./promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - - /** - This is a convenient alias for `RSVP.Promise.reject`. - - @method reject - @for RSVP - @param {Any} reason value that the returned promise will be rejected with. - @param {String} label optional string for identifying the returned promise. - Useful for tooling. - @return {Promise} a promise rejected with the given `reason`. - @static - */ - __exports__["default"] = function reject(reason, label) { - return Promise.reject(reason, label); - }; - }); -define("rsvp/resolve", - ["./promise","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - - /** - This is a convenient alias for `RSVP.Promise.resolve`. - - @method resolve - @for RSVP - @param {Any} value value that the returned promise will be resolved with - @param {String} label optional string for identifying the returned promise. - Useful for tooling. - @return {Promise} a promise that will become fulfilled with the given - `value` - @static - */ - __exports__["default"] = function resolve(value, label) { - return Promise.resolve(value, label); - }; - }); -define("rsvp/rethrow", - ["exports"], - function(__exports__) { - "use strict"; - /** - `RSVP.rethrow` will rethrow an error on the next turn of the JavaScript event - loop in order to aid debugging. - - Promises A+ specifies that any exceptions that occur with a promise must be - caught by the promises implementation and bubbled to the last handler. For - this reason, it is recommended that you always specify a second rejection - handler function to `then`. However, `RSVP.rethrow` will throw the exception - outside of the promise, so it bubbles up to your console if in the browser, - or domain/cause uncaught exception in Node. `rethrow` will also throw the - error again so the error can be handled by the promise per the spec. - - ```javascript - function throws(){ - throw new Error('Whoops!'); - } - - var promise = new RSVP.Promise(function(resolve, reject){ - throws(); - }); - - promise.catch(RSVP.rethrow).then(function(){ - // Code here doesn't run because the promise became rejected due to an - // error! - }, function (err){ - // handle the error here - }); - ``` - - The 'Whoops' error will be thrown on the next turn of the event loop - and you can watch for it in your console. You can also handle it using a - rejection handler given to `.then` or `.catch` on the returned promise. - - @method rethrow - @for RSVP - @param {Error} reason reason the promise became rejected. - @throws Error - @static - */ - __exports__["default"] = function rethrow(reason) { - setTimeout(function() { - throw reason; - }); - throw reason; - }; - }); -define("rsvp/utils", - ["exports"], - function(__exports__) { - "use strict"; - function objectOrFunction(x) { - return typeof x === "function" || (typeof x === "object" && x !== null); - } - - __exports__.objectOrFunction = objectOrFunction;function isFunction(x) { - return typeof x === "function"; - } - - __exports__.isFunction = isFunction;function isNonThenable(x) { - return !objectOrFunction(x); - } - - __exports__.isNonThenable = isNonThenable;function isArray(x) { - return Object.prototype.toString.call(x) === "[object Array]"; - } - - __exports__.isArray = isArray;// Date.now is not available in browsers < IE9 - // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/now#Compatibility - var now = Date.now || function() { return new Date().getTime(); }; - __exports__.now = now; - var keysOf = Object.keys || function(object) { - var result = []; - - for (var prop in object) { - result.push(prop); - } - - return result; - }; - __exports__.keysOf = keysOf; - }); -define("rsvp", - ["./rsvp/promise","./rsvp/events","./rsvp/node","./rsvp/all","./rsvp/all_settled","./rsvp/race","./rsvp/hash","./rsvp/rethrow","./rsvp/defer","./rsvp/config","./rsvp/map","./rsvp/resolve","./rsvp/reject","./rsvp/filter","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __dependency12__, __dependency13__, __dependency14__, __exports__) { - "use strict"; - var Promise = __dependency1__["default"]; - var EventTarget = __dependency2__["default"]; - var denodeify = __dependency3__["default"]; - var all = __dependency4__["default"]; - var allSettled = __dependency5__["default"]; - var race = __dependency6__["default"]; - var hash = __dependency7__["default"]; - var rethrow = __dependency8__["default"]; - var defer = __dependency9__["default"]; - var config = __dependency10__.config; - var configure = __dependency10__.configure; - var map = __dependency11__["default"]; - var resolve = __dependency12__["default"]; - var reject = __dependency13__["default"]; - var filter = __dependency14__["default"]; - - function async(callback, arg) { - config.async(callback, arg); - } - - function on() { - config.on.apply(config, arguments); - } - - function off() { - config.off.apply(config, arguments); - } - - // Set up instrumentation through `window.__PROMISE_INTRUMENTATION__` - if (typeof window !== 'undefined' && typeof window.__PROMISE_INSTRUMENTATION__ === 'object') { - var callbacks = window.__PROMISE_INSTRUMENTATION__; - configure('instrument', true); - for (var eventName in callbacks) { - if (callbacks.hasOwnProperty(eventName)) { - on(eventName, callbacks[eventName]); - } - } - } - - __exports__.Promise = Promise; - __exports__.EventTarget = EventTarget; - __exports__.all = all; - __exports__.allSettled = allSettled; - __exports__.race = race; - __exports__.hash = hash; - __exports__.rethrow = rethrow; - __exports__.defer = defer; - __exports__.denodeify = denodeify; - __exports__.configure = configure; - __exports__.on = on; - __exports__.off = off; - __exports__.resolve = resolve; - __exports__.reject = reject; - __exports__.async = async; - __exports__.map = map; - __exports__.filter = filter; - }); - -})(); - -(function() { -define("container/container", - ["container/inheriting_dict","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var InheritingDict = __dependency1__["default"]; - - // 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) { - 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) { - 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 oppertunity 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) { - validateFullName(fullName); - - var normalizedName = this.normalize(fullName); - var cached = this.resolveCache.get(normalizedName); - - if (cached) { return cached; } - - var resolved = this.resolver(normalizedName) || this.registry.get(normalizedName); - - this.resolveCache.set(normalizedName, resolved); - - return resolved; - }, - - /** - 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'); - twitter 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) { - 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) { - 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) { - 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) { - validateFullName(fullName); - if (this.parent) { illegalChildOperation('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); - } - - validateFullName(fullName); - var normalizedName = this.normalize(fullName); - - 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); - } - - validateFullName(fullName); - - 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, l=this.children.length; i w. -*/ -Ember.compare = function compare(v, w) { - if (v === w) { return 0; } - - var type1 = Ember.typeOf(v); - var type2 = Ember.typeOf(w); - - var Comparable = Ember.Comparable; - if (Comparable) { - if (type1==='instance' && Comparable.detect(v.constructor)) { - return v.constructor.compare(v, w); - } - - if (type2 === 'instance' && Comparable.detect(w.constructor)) { - return 1-w.constructor.compare(w, v); - } - } - - // If we haven't yet generated a reverse-mapping of Ember.ORDER_DEFINITION, - // do so now. - var mapping = Ember.ORDER_DEFINITION_MAPPING; - if (!mapping) { - var order = Ember.ORDER_DEFINITION; - mapping = Ember.ORDER_DEFINITION_MAPPING = {}; - var idx, len; - for (idx = 0, len = order.length; idx < len; ++idx) { - mapping[order[idx]] = idx; - } - - // We no longer need Ember.ORDER_DEFINITION. - delete Ember.ORDER_DEFINITION; - } - - var type1Index = mapping[type1]; - var type2Index = mapping[type2]; - - if (type1Index < type2Index) { return -1; } - if (type1Index > type2Index) { return 1; } - - // types are equal - so we have to check values now - switch (type1) { - case 'boolean': - case 'number': - if (v < w) { return -1; } - if (v > w) { return 1; } - return 0; - - case 'string': - var comp = v.localeCompare(w); - if (comp < 0) { return -1; } - if (comp > 0) { return 1; } - return 0; - - case 'array': - var vLen = v.length; - var wLen = w.length; - var l = Math.min(vLen, wLen); - var r = 0; - var i = 0; - while (r === 0 && i < l) { - r = compare(v[i],w[i]); - i++; - } - if (r !== 0) { return r; } - - // all elements are equal now - // shorter array should be ordered first - if (vLen < wLen) { return -1; } - if (vLen > wLen) { return 1; } - // arrays are equal now - return 0; - - case 'instance': - if (Ember.Comparable && Ember.Comparable.detect(v)) { - return v.compare(v, w); - } - return 0; - - case 'date': - var vNum = v.getTime(); - var wNum = w.getTime(); - if (vNum < wNum) { return -1; } - if (vNum > wNum) { return 1; } - return 0; - - default: - return 0; - } -}; - -function _copy(obj, deep, seen, copies) { - var ret, loc, key; - - // primitive data types are immutable, just return them. - if ('object' !== typeof obj || obj===null) return obj; - - // avoid cyclical loops - if (deep && (loc=indexOf(seen, obj))>=0) return copies[loc]; - - Ember.assert('Cannot clone an Ember.Object that does not implement Ember.Copyable', !(obj instanceof Ember.Object) || (Ember.Copyable && Ember.Copyable.detect(obj))); - - // IMPORTANT: this specific test will detect a native array only. Any other - // object will need to implement Copyable. - if (Ember.typeOf(obj) === 'array') { - ret = obj.slice(); - if (deep) { - loc = ret.length; - while(--loc>=0) ret[loc] = _copy(ret[loc], deep, seen, copies); - } - } else if (Ember.Copyable && Ember.Copyable.detect(obj)) { - ret = obj.copy(deep, seen, copies); - } else if (obj instanceof Date) { - ret = new Date(obj.getTime()); - } else { - ret = {}; - for(key in obj) { - if (!obj.hasOwnProperty(key)) continue; - - // Prevents browsers that don't respect non-enumerability from - // copying internal Ember properties - if (key.substring(0,2) === '__') continue; - - ret[key] = deep ? _copy(obj[key], deep, seen, copies) : obj[key]; - } - } - - if (deep) { - seen.push(obj); - copies.push(ret); - } - - return ret; -} - -/** - Creates a clone of the passed object. This function can take just about - any type of object and create a clone of it, including primitive values - (which are not actually cloned because they are immutable). - - If the passed object implements the `clone()` method, then this function - will simply call that method and return the result. - - @method copy - @for Ember - @param {Object} obj The object to clone - @param {Boolean} deep If true, a deep copy of the object is made - @return {Object} The cloned object -*/ -Ember.copy = function(obj, deep) { - // fast paths - if ('object' !== typeof obj || obj===null) return obj; // can't copy primitives - if (Ember.Copyable && Ember.Copyable.detect(obj)) return obj.copy(deep); - return _copy(obj, deep, deep ? [] : null, deep ? [] : null); -}; - -/** - Compares two objects, returning true if they are logically equal. This is - a deeper comparison than a simple triple equal. For sets it will compare the - internal objects. For any other object that implements `isEqual()` it will - respect that method. - - ```javascript - Ember.isEqual('hello', 'hello'); // true - Ember.isEqual(1, 2); // false - Ember.isEqual([4,2], [4,2]); // false - ``` - - @method isEqual - @for Ember - @param {Object} a first object to compare - @param {Object} b second object to compare - @return {Boolean} -*/ -Ember.isEqual = function(a, b) { - if (a && 'function'===typeof a.isEqual) return a.isEqual(b); - return a === b; -}; - -// Used by Ember.compare -Ember.ORDER_DEFINITION = Ember.ENV.ORDER_DEFINITION || [ - 'undefined', - 'null', - 'boolean', - 'number', - 'string', - 'array', - 'object', - 'instance', - 'function', - 'class', - 'date' -]; - -/** - Returns all of the keys defined on an object or hash. This is useful - when inspecting objects for debugging. On browsers that support it, this - uses the native `Object.keys` implementation. - - @method keys - @for Ember - @param {Object} obj - @return {Array} Array containing keys of obj -*/ -Ember.keys = Object.keys; - -if (!Ember.keys || Ember.create.isSimulated) { - var prototypeProperties = [ - 'constructor', - 'hasOwnProperty', - 'isPrototypeOf', - 'propertyIsEnumerable', - 'valueOf', - 'toLocaleString', - 'toString' - ], - pushPropertyName = function(obj, array, key) { - // Prevents browsers that don't respect non-enumerability from - // copying internal Ember properties - if (key.substring(0,2) === '__') return; - if (key === '_super') return; - if (indexOf(array, key) >= 0) return; - if (!obj.hasOwnProperty(key)) return; - - array.push(key); - }; - - Ember.keys = function(obj) { - var ret = [], key; - for (key in obj) { - pushPropertyName(obj, ret, key); - } - - // IE8 doesn't enumerate property that named the same as prototype properties. - for (var i = 0, l = prototypeProperties.length; i < l; i++) { - key = prototypeProperties[i]; - - pushPropertyName(obj, ret, key); - } - - return ret; - }; -} - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var STRING_DASHERIZE_REGEXP = (/[ _]/g); -var STRING_DASHERIZE_CACHE = {}; -var STRING_DECAMELIZE_REGEXP = (/([a-z\d])([A-Z])/g); -var STRING_CAMELIZE_REGEXP = (/(\-|_|\.|\s)+(.)?/g); -var STRING_UNDERSCORE_REGEXP_1 = (/([a-z\d])([A-Z]+)/g); -var STRING_UNDERSCORE_REGEXP_2 = (/\-|\s+/g); -var STRING_PARAMETERIZE_REGEXP_1 = (/[_|\/|\s]+/g); -var STRING_PARAMETERIZE_REGEXP_2 = (/[^a-z0-9\-]+/gi); -var STRING_PARAMETERIZE_REGEXP_3 = (/[\-]+/g); -var STRING_PARAMETERIZE_REGEXP_4 = (/^-+|-+$/g); - -/** - Defines the hash of localized strings for the current language. Used by - the `Ember.String.loc()` helper. To localize, add string values to this - hash. - - @property STRINGS - @for Ember - @type Hash -*/ -Ember.STRINGS = {}; - -/** - Defines string helper methods including string formatting and localization. - Unless `Ember.EXTEND_PROTOTYPES.String` is `false` these methods will also be - added to the `String.prototype` as well. - - @class String - @namespace Ember - @static -*/ -Ember.String = { - - /** - Apply formatting options to the string. This will look for occurrences - of "%@" in your string and substitute them with the arguments you pass into - this method. If you want to control the specific order of replacement, - you can add a number after the key as well to indicate which argument - you want to insert. - - Ordered insertions are most useful when building loc strings where values - you need to insert may appear in different orders. - - ```javascript - "Hello %@ %@".fmt('John', 'Doe'); // "Hello John Doe" - "Hello %@2, %@1".fmt('John', 'Doe'); // "Hello Doe, John" - ``` - - @method fmt - @param {String} str The string to format - @param {Array} formats An array of parameters to interpolate into string. - @return {String} formatted string - */ - fmt: function(str, formats) { - // first, replace any ORDERED replacements. - var idx = 0; // the current index for non-numerical replacements - return str.replace(/%@([0-9]+)?/g, function(s, argIndex) { - argIndex = (argIndex) ? parseInt(argIndex, 10) - 1 : idx++; - s = formats[argIndex]; - return (s === null) ? '(null)' : (s === undefined) ? '' : Ember.inspect(s); - }) ; - }, - - /** - Formats the passed string, but first looks up the string in the localized - strings hash. This is a convenient way to localize text. See - `Ember.String.fmt()` for more information on formatting. - - Note that it is traditional but not required to prefix localized string - keys with an underscore or other character so you can easily identify - localized strings. - - ```javascript - Ember.STRINGS = { - '_Hello World': 'Bonjour le monde', - '_Hello %@ %@': 'Bonjour %@ %@' - }; - - Ember.String.loc("_Hello World"); // 'Bonjour le monde'; - Ember.String.loc("_Hello %@ %@", ["John", "Smith"]); // "Bonjour John Smith"; - ``` - - @method loc - @param {String} str The string to format - @param {Array} formats Optional array of parameters to interpolate into string. - @return {String} formatted string - */ - loc: function(str, formats) { - str = Ember.STRINGS[str] || str; - return Ember.String.fmt(str, formats) ; - }, - - /** - Splits a string into separate units separated by spaces, eliminating any - empty strings in the process. This is a convenience method for split that - is mostly useful when applied to the `String.prototype`. - - ```javascript - Ember.String.w("alpha beta gamma").forEach(function(key) { - console.log(key); - }); - - // > alpha - // > beta - // > gamma - ``` - - @method w - @param {String} str The string to split - @return {String} split string - */ - w: function(str) { return str.split(/\s+/); }, - - /** - Converts a camelized string into all lower case separated by underscores. - - ```javascript - 'innerHTML'.decamelize(); // 'inner_html' - 'action_name'.decamelize(); // 'action_name' - 'css-class-name'.decamelize(); // 'css-class-name' - 'my favorite items'.decamelize(); // 'my favorite items' - ``` - - @method decamelize - @param {String} str The string to decamelize. - @return {String} the decamelized string. - */ - decamelize: function(str) { - return str.replace(STRING_DECAMELIZE_REGEXP, '$1_$2').toLowerCase(); - }, - - /** - Replaces underscores, spaces, or camelCase with dashes. - - ```javascript - 'innerHTML'.dasherize(); // 'inner-html' - 'action_name'.dasherize(); // 'action-name' - 'css-class-name'.dasherize(); // 'css-class-name' - 'my favorite items'.dasherize(); // 'my-favorite-items' - ``` - - @method dasherize - @param {String} str The string to dasherize. - @return {String} the dasherized string. - */ - dasherize: function(str) { - var cache = STRING_DASHERIZE_CACHE, - hit = cache.hasOwnProperty(str), - ret; - - if (hit) { - return cache[str]; - } else { - ret = Ember.String.decamelize(str).replace(STRING_DASHERIZE_REGEXP,'-'); - cache[str] = ret; - } - - return ret; - }, - - /** - Returns the lowerCamelCase form of a string. - - ```javascript - 'innerHTML'.camelize(); // 'innerHTML' - 'action_name'.camelize(); // 'actionName' - 'css-class-name'.camelize(); // 'cssClassName' - 'my favorite items'.camelize(); // 'myFavoriteItems' - 'My Favorite Items'.camelize(); // 'myFavoriteItems' - ``` - - @method camelize - @param {String} str The string to camelize. - @return {String} the camelized string. - */ - camelize: function(str) { - return str.replace(STRING_CAMELIZE_REGEXP, function(match, separator, chr) { - return chr ? chr.toUpperCase() : ''; - }).replace(/^([A-Z])/, function(match, separator, chr) { - return match.toLowerCase(); - }); - }, - - /** - Returns the UpperCamelCase form of a string. - - ```javascript - 'innerHTML'.classify(); // 'InnerHTML' - 'action_name'.classify(); // 'ActionName' - 'css-class-name'.classify(); // 'CssClassName' - 'my favorite items'.classify(); // 'MyFavoriteItems' - ``` - - @method classify - @param {String} str the string to classify - @return {String} the classified string - */ - classify: function(str) { - var parts = str.split("."), - out = []; - - for (var i=0, l=parts.length; i= 0) { - var baseValue = this[keyName]; - - if (baseValue) { - if ('function' === typeof baseValue.concat) { - value = baseValue.concat(value); - } else { - value = Ember.makeArray(baseValue).concat(value); - } - } else { - value = Ember.makeArray(value); - } - } - - if (desc) { - desc.set(this, keyName, value); - } else { - if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) { - this.setUnknownProperty(keyName, value); - } else if (MANDATORY_SETTER) { - Ember.defineProperty(this, keyName, null, value); // setup mandatory setter - } else { - this[keyName] = value; - } - } - } - } - } - finishPartial(this, m); - this.init.apply(this, arguments); - m.proto = proto; - finishChains(this); - sendEvent(this, "init"); - }; - - Class.toString = Mixin.prototype.toString; - Class.willReopen = function() { - if (wasApplied) { - Class.PrototypeMixin = Mixin.create(Class.PrototypeMixin); - } - - wasApplied = false; - }; - Class._initMixins = function(args) { initMixins = args; }; - Class._initProperties = function(args) { initProperties = args; }; - - Class.proto = function() { - var superclass = Class.superclass; - if (superclass) { superclass.proto(); } - - if (!wasApplied) { - wasApplied = true; - Class.PrototypeMixin.applyPartial(Class.prototype); - rewatch(Class.prototype); - } - - return this.prototype; - }; - - return Class; - -} - -/** - @class CoreObject - @namespace Ember -*/ -var CoreObject = makeCtor(); -CoreObject.toString = function() { return "Ember.CoreObject"; }; - -CoreObject.PrototypeMixin = Mixin.create({ - reopen: function() { - applyMixin(this, arguments, true); - return this; - }, - - /** - An overridable method called when objects are instantiated. By default, - does nothing unless it is overridden during class definition. - - Example: - - ```javascript - App.Person = Ember.Object.extend({ - init: function() { - alert('Name is ' + this.get('name')); - } - }); - - var steve = App.Person.create({ - name: "Steve" - }); - - // alerts 'Name is Steve'. - ``` - - NOTE: If you do override `init` for a framework class like `Ember.View` or - `Ember.ArrayController`, be sure to call `this._super()` in your - `init` declaration! If you don't, Ember may not have an opportunity to - do important setup work, and you'll see strange behavior in your - application. - - @method init - */ - init: function() {}, - - /** - Defines the properties that will be concatenated from the superclass - (instead of overridden). - - By default, when you extend an Ember class a property defined in - the subclass overrides a property with the same name that is defined - in the superclass. However, there are some cases where it is preferable - to build up a property's value by combining the superclass' property - value with the subclass' value. An example of this in use within Ember - is the `classNames` property of `Ember.View`. - - Here is some sample code showing the difference between a concatenated - property and a normal one: - - ```javascript - App.BarView = Ember.View.extend({ - someNonConcatenatedProperty: ['bar'], - classNames: ['bar'] - }); - - App.FooBarView = App.BarView.extend({ - someNonConcatenatedProperty: ['foo'], - classNames: ['foo'], - }); - - var fooBarView = App.FooBarView.create(); - fooBarView.get('someNonConcatenatedProperty'); // ['foo'] - fooBarView.get('classNames'); // ['ember-view', 'bar', 'foo'] - ``` - - This behavior extends to object creation as well. Continuing the - above example: - - ```javascript - var view = App.FooBarView.create({ - someNonConcatenatedProperty: ['baz'], - classNames: ['baz'] - }) - view.get('someNonConcatenatedProperty'); // ['baz'] - view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] - ``` - Adding a single property that is not an array will just add it in the array: - - ```javascript - var view = App.FooBarView.create({ - classNames: 'baz' - }) - view.get('classNames'); // ['ember-view', 'bar', 'foo', 'baz'] - ``` - - Using the `concatenatedProperties` property, we can tell to Ember that mix - the content of the properties. - - In `Ember.View` the `classNameBindings` and `attributeBindings` properties - are also concatenated, in addition to `classNames`. - - This feature is available for you to use throughout the Ember object model, - although typical app developers are likely to use it infrequently. Since - it changes expectations about behavior of properties, you should properly - document its usage in each individual concatenated property (to not - mislead your users to think they can override the property in a subclass). - - @property concatenatedProperties - @type Array - @default null - */ - concatenatedProperties: null, - - /** - Destroyed object property flag. - - if this property is `true` the observers and bindings were already - removed by the effect of calling the `destroy()` method. - - @property isDestroyed - @default false - */ - isDestroyed: false, - - /** - Destruction scheduled flag. The `destroy()` method has been called. - - The object stays intact until the end of the run loop at which point - the `isDestroyed` flag is set. - - @property isDestroying - @default false - */ - isDestroying: false, - - /** - Destroys an object by setting the `isDestroyed` flag and removing its - metadata, which effectively destroys observers and bindings. - - If you try to set a property on a destroyed object, an exception will be - raised. - - Note that destruction is scheduled for the end of the run loop and does not - happen immediately. It will set an isDestroying flag immediately. - - @method destroy - @return {Ember.Object} receiver - */ - destroy: function() { - if (this.isDestroying) { return; } - this.isDestroying = true; - - schedule('actions', this, this.willDestroy); - schedule('destroy', this, this._scheduledDestroy); - return this; - }, - - /** - Override to implement teardown. - - @method willDestroy - */ - willDestroy: Ember.K, - - /** - Invoked by the run loop to actually destroy the object. This is - scheduled for execution by the `destroy` method. - - @private - @method _scheduledDestroy - */ - _scheduledDestroy: function() { - if (this.isDestroyed) { return; } - destroy(this); - this.isDestroyed = true; - }, - - bind: function(to, from) { - if (!(from instanceof Ember.Binding)) { from = Ember.Binding.from(from); } - from.to(to).connect(this); - return from; - }, - - /** - Returns a string representation which attempts to provide more information - than Javascript's `toString` typically does, in a generic way for all Ember - objects. - - ```javascript - App.Person = Em.Object.extend() - person = App.Person.create() - person.toString() //=> "" - ``` - - If the object's class is not defined on an Ember namespace, it will - indicate it is a subclass of the registered superclass: - - ```javascript - Student = App.Person.extend() - student = Student.create() - student.toString() //=> "<(subclass of App.Person):ember1025>" - ``` - - If the method `toStringExtension` is defined, its return value will be - included in the output. - - ```javascript - App.Teacher = App.Person.extend({ - toStringExtension: function() { - return this.get('fullName'); - } - }); - teacher = App.Teacher.create() - teacher.toString(); //=> "" - ``` - - @method toString - @return {String} string representation - */ - toString: function toString() { - var hasToStringExtension = typeof this.toStringExtension === 'function', - extension = hasToStringExtension ? ":" + this.toStringExtension() : ''; - var ret = '<'+this.constructor.toString()+':'+guidFor(this)+extension+'>'; - this.toString = makeToString(ret); - return ret; - } -}); - -CoreObject.PrototypeMixin.ownerConstructor = CoreObject; - -function makeToString(ret) { - return function() { return ret; }; -} - -if (Ember.config.overridePrototypeMixin) { - Ember.config.overridePrototypeMixin(CoreObject.PrototypeMixin); -} - -CoreObject.__super__ = null; - -var ClassMixin = Mixin.create({ - - ClassMixin: Ember.required(), - - PrototypeMixin: Ember.required(), - - isClass: true, - - isMethod: false, - - /** - Creates a new subclass. - - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(thing); - } - }); - ``` - - This defines a new subclass of Ember.Object: `App.Person`. It contains one method: `say()`. - - You can also create a subclass from any existing class by calling its `extend()` method. For example, you might want to create a subclass of Ember's built-in `Ember.View` class: - - ```javascript - App.PersonView = Ember.View.extend({ - tagName: 'li', - classNameBindings: ['isAdministrator'] - }); - ``` - - When defining a subclass, you can override methods but still access the implementation of your parent class by calling the special `_super()` method: - - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - var name = this.get('name'); - alert(name + ' says: ' + thing); - } - }); - - App.Soldier = App.Person.extend({ - say: function(thing) { - this._super(thing + ", sir!"); - }, - march: function(numberOfHours) { - alert(this.get('name') + ' marches for ' + numberOfHours + ' hours.') - } - }); - - var yehuda = App.Soldier.create({ - name: "Yehuda Katz" - }); - - yehuda.say("Yes"); // alerts "Yehuda Katz says: Yes, sir!" - ``` - - The `create()` on line #17 creates an *instance* of the `App.Soldier` class. The `extend()` on line #8 creates a *subclass* of `App.Person`. Any instance of the `App.Person` class will *not* have the `march()` method. - - You can also pass `Ember.Mixin` classes to add additional properties to the subclass. - - ```javascript - App.Person = Ember.Object.extend({ - say: function(thing) { - alert(this.get('name') + ' says: ' + thing); - } - }); - - App.SingingMixin = Ember.Mixin.create({ - sing: function(thing){ - alert(this.get('name') + ' sings: la la la ' + thing); - } - }); - - App.BroadwayStar = App.Person.extend(App.SingingMixin, { - dance: function() { - alert(this.get('name') + ' dances: tap tap tap tap '); - } - }); - ``` - - The `App.BroadwayStar` class contains three methods: `say()`, `sing()`, and `dance()`. - - @method extend - @static - - @param {Ember.Mixin} [mixins]* One or more Ember.Mixin classes - @param {Object} [arguments]* Object containing values to use within the new class - */ - extend: function() { - var Class = makeCtor(), proto; - Class.ClassMixin = Mixin.create(this.ClassMixin); - Class.PrototypeMixin = Mixin.create(this.PrototypeMixin); - - Class.ClassMixin.ownerConstructor = Class; - Class.PrototypeMixin.ownerConstructor = Class; - - reopen.apply(Class.PrototypeMixin, arguments); - - Class.superclass = this; - Class.__super__ = this.prototype; - - proto = Class.prototype = o_create(this.prototype); - proto.constructor = Class; - generateGuid(proto); - meta(proto).proto = proto; // this will disable observers on prototype - - Class.ClassMixin.apply(Class); - return Class; - }, - - /** - Equivalent to doing `extend(arguments).create()`. - If possible use the normal `create` method instead. - - @method createWithMixins - @static - @param [arguments]* - */ - createWithMixins: function() { - var C = this; - if (arguments.length>0) { this._initMixins(arguments); } - return new C(); - }, - - /** - Creates an instance of a class. Accepts either no arguments, or an object - containing values to initialize the newly instantiated object with. - - ```javascript - App.Person = Ember.Object.extend({ - helloWorld: function() { - alert("Hi, my name is " + this.get('name')); - } - }); - - var tom = App.Person.create({ - name: 'Tom Dale' - }); - - tom.helloWorld(); // alerts "Hi, my name is Tom Dale". - ``` - - `create` will call the `init` function if defined during - `Ember.AnyObject.extend` - - If no arguments are passed to `create`, it will not set values to the new - instance during initialization: - - ```javascript - var noName = App.Person.create(); - noName.helloWorld(); // alerts undefined - ``` - - NOTE: For performance reasons, you cannot declare methods or computed - properties during `create`. You should instead declare methods and computed - properties when using `extend` or use the `createWithMixins` shorthand. - - @method create - @static - @param [arguments]* - */ - create: function() { - var C = this; - if (arguments.length>0) { this._initProperties(arguments); } - return new C(); - }, - - /** - Augments a constructor's prototype with additional - properties and functions: - - ```javascript - MyObject = Ember.Object.extend({ - name: 'an object' - }); - - o = MyObject.create(); - o.get('name'); // 'an object' - - MyObject.reopen({ - say: function(msg){ - console.log(msg); - } - }) - - o2 = MyObject.create(); - o2.say("hello"); // logs "hello" - - o.say("goodbye"); // logs "goodbye" - ``` - - To add functions and properties to the constructor itself, - see `reopenClass` - - @method reopen - */ - reopen: function() { - this.willReopen(); - reopen.apply(this.PrototypeMixin, arguments); - return this; - }, - - /** - Augments a constructor's own properties and functions: - - ```javascript - MyObject = Ember.Object.extend({ - name: 'an object' - }); - - MyObject.reopenClass({ - canBuild: false - }); - - MyObject.canBuild; // false - o = MyObject.create(); - ``` - - In other words, this creates static properties and functions for the class. These are only available on the class - and not on any instance of that class. - - ```javascript - App.Person = Ember.Object.extend({ - name : "", - sayHello : function(){ - alert("Hello. My name is " + this.get('name')); - } - }); - - App.Person.reopenClass({ - species : "Homo sapiens", - createPerson: function(newPersonsName){ - return App.Person.create({ - name:newPersonsName - }); - } - }); - - var tom = App.Person.create({ - name : "Tom Dale" - }); - var yehuda = App.Person.createPerson("Yehuda Katz"); - - tom.sayHello(); // "Hello. My name is Tom Dale" - yehuda.sayHello(); // "Hello. My name is Yehuda Katz" - alert(App.Person.species); // "Homo sapiens" - ``` - - Note that `species` and `createPerson` are *not* valid on the `tom` and `yehuda` - variables. They are only valid on `App.Person`. - - To add functions and properties to instances of - a constructor by extending the constructor's prototype - see `reopen` - - @method reopenClass - */ - reopenClass: function() { - reopen.apply(this.ClassMixin, arguments); - applyMixin(this, arguments, false); - return this; - }, - - detect: function(obj) { - if ('function' !== typeof obj) { return false; } - while(obj) { - if (obj===this) { return true; } - obj = obj.superclass; - } - return false; - }, - - detectInstance: function(obj) { - return obj instanceof this; - }, - - /** - In some cases, you may want to annotate computed properties with additional - metadata about how they function or what values they operate on. For - example, computed property functions may close over variables that are then - no longer available for introspection. - - You can pass a hash of these values to a computed property like this: - - ```javascript - person: function() { - var personId = this.get('personId'); - return App.Person.create({ id: personId }); - }.property().meta({ type: App.Person }) - ``` - - Once you've done this, you can retrieve the values saved to the computed - property from your class like this: - - ```javascript - MyClass.metaForProperty('person'); - ``` - - This will return the original hash that was passed to `meta()`. - - @method metaForProperty - @param key {String} property name - */ - metaForProperty: function(key) { - var meta = this.proto()[META_KEY], - desc = meta && meta.descs[key]; - - Ember.assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof Ember.ComputedProperty); - return desc._meta || {}; - }, - - /** - Iterate over each computed property for the class, passing its name - and any associated metadata (see `metaForProperty`) to the callback. - - @method eachComputedProperty - @param {Function} callback - @param {Object} binding - */ - eachComputedProperty: function(callback, binding) { - var proto = this.proto(), - descs = meta(proto).descs, - empty = {}, - property; - - for (var name in descs) { - property = descs[name]; - - if (property instanceof Ember.ComputedProperty) { - callback.call(binding || this, name, property._meta || empty); - } - } - } - -}); - -ClassMixin.ownerConstructor = CoreObject; - -if (Ember.config.overrideClassMixin) { - Ember.config.overrideClassMixin(ClassMixin); -} - -CoreObject.ClassMixin = ClassMixin; -ClassMixin.apply(CoreObject); - -Ember.CoreObject = CoreObject; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -/** - `Ember.Object` is the main base class for all Ember objects. It is a subclass - of `Ember.CoreObject` with the `Ember.Observable` mixin applied. For details, - see the documentation for each of these. - - @class Object - @namespace Ember - @extends Ember.CoreObject - @uses Ember.Observable -*/ -Ember.Object = Ember.CoreObject.extend(Ember.Observable); -Ember.Object.toString = function() { return "Ember.Object"; }; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var get = Ember.get, indexOf = Ember.ArrayPolyfills.indexOf; - -/** - A Namespace is an object usually used to contain other objects or methods - such as an application or framework. Create a namespace anytime you want - to define one of these new containers. - - # Example Usage - - ```javascript - MyFramework = Ember.Namespace.create({ - VERSION: '1.0.0' - }); - ``` - - @class Namespace - @namespace Ember - @extends Ember.Object -*/ -var Namespace = Ember.Namespace = Ember.Object.extend({ - isNamespace: true, - - init: function() { - Ember.Namespace.NAMESPACES.push(this); - Ember.Namespace.PROCESSED = false; - }, - - toString: function() { - var name = get(this, 'name'); - if (name) { return name; } - - findNamespaces(); - return this[Ember.GUID_KEY+'_name']; - }, - - nameClasses: function() { - processNamespace([this.toString()], this, {}); - }, - - destroy: function() { - var namespaces = Ember.Namespace.NAMESPACES; - - Ember.lookup[this.toString()] = undefined; - delete Ember.Namespace.NAMESPACES_BY_ID[this.toString()]; - namespaces.splice(indexOf.call(namespaces, this), 1); - this._super(); - } -}); - -Namespace.reopenClass({ - NAMESPACES: [Ember], - NAMESPACES_BY_ID: {}, - PROCESSED: false, - processAll: processAllNamespaces, - byName: function(name) { - if (!Ember.BOOTED) { - processAllNamespaces(); - } - - return NAMESPACES_BY_ID[name]; - } -}); - -var NAMESPACES_BY_ID = Namespace.NAMESPACES_BY_ID; - -var hasOwnProp = ({}).hasOwnProperty, - guidFor = Ember.guidFor; - -function processNamespace(paths, root, seen) { - var idx = paths.length; - - NAMESPACES_BY_ID[paths.join('.')] = root; - - // Loop over all of the keys in the namespace, looking for classes - for(var key in root) { - if (!hasOwnProp.call(root, key)) { continue; } - var obj = root[key]; - - // If we are processing the `Ember` namespace, for example, the - // `paths` will start with `["Ember"]`. Every iteration through - // the loop will update the **second** element of this list with - // the key, so processing `Ember.View` will make the Array - // `['Ember', 'View']`. - paths[idx] = key; - - // If we have found an unprocessed class - if (obj && obj.toString === classToString) { - // Replace the class' `toString` with the dot-separated path - // and set its `NAME_KEY` - obj.toString = makeToString(paths.join('.')); - obj[NAME_KEY] = paths.join('.'); - - // Support nested namespaces - } else if (obj && obj.isNamespace) { - // Skip aliased namespaces - if (seen[guidFor(obj)]) { continue; } - seen[guidFor(obj)] = true; - - // Process the child namespace - processNamespace(paths, obj, seen); - } - } - - paths.length = idx; // cut out last item -} - -function findNamespaces() { - var Namespace = Ember.Namespace, lookup = Ember.lookup, obj, isNamespace; - - if (Namespace.PROCESSED) { return; } - - for (var prop in lookup) { - // These don't raise exceptions but can cause warnings - if (prop === "parent" || prop === "top" || prop === "frameElement" || prop === "webkitStorageInfo") { continue; } - - // get(window.globalStorage, 'isNamespace') would try to read the storage for domain isNamespace and cause exception in Firefox. - // globalStorage is a storage obsoleted by the WhatWG storage specification. See https://developer.mozilla.org/en/DOM/Storage#globalStorage - if (prop === "globalStorage" && lookup.StorageList && lookup.globalStorage instanceof lookup.StorageList) { continue; } - // Unfortunately, some versions of IE don't support window.hasOwnProperty - if (lookup.hasOwnProperty && !lookup.hasOwnProperty(prop)) { continue; } - - // At times we are not allowed to access certain properties for security reasons. - // There are also times where even if we can access them, we are not allowed to access their properties. - try { - obj = Ember.lookup[prop]; - isNamespace = obj && obj.isNamespace; - } catch (e) { - continue; - } - - if (isNamespace) { - Ember.deprecate("Namespaces should not begin with lowercase.", /^[A-Z]/.test(prop)); - obj[NAME_KEY] = prop; - } - } -} - -var NAME_KEY = Ember.NAME_KEY = Ember.GUID_KEY + '_name'; - -function superClassString(mixin) { - var superclass = mixin.superclass; - if (superclass) { - if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; } - else { return superClassString(superclass); } - } else { - return; - } -} - -function classToString() { - if (!Ember.BOOTED && !this[NAME_KEY]) { - processAllNamespaces(); - } - - var ret; - - if (this[NAME_KEY]) { - ret = this[NAME_KEY]; - } else if (this._toString) { - ret = this._toString; - } else { - var str = superClassString(this); - if (str) { - ret = "(subclass of " + str + ")"; - } else { - ret = "(unknown mixin)"; - } - this.toString = makeToString(ret); - } - - return ret; -} - -function processAllNamespaces() { - var unprocessedNamespaces = !Namespace.PROCESSED, - unprocessedMixins = Ember.anyUnprocessedMixins; - - if (unprocessedNamespaces) { - findNamespaces(); - Namespace.PROCESSED = true; - } - - if (unprocessedNamespaces || unprocessedMixins) { - var namespaces = Namespace.NAMESPACES, namespace; - for (var i=0, l=namespaces.length; i1) args = a_slice.call(arguments, 1); - - this.forEach(function(x, idx) { - var method = x && x[methodName]; - if ('function' === typeof method) { - ret[idx] = args ? method.apply(x, args) : x[methodName](); - } - }, this); - - return ret; - }, - - /** - Simply converts the enumerable into a genuine array. The order is not - guaranteed. Corresponds to the method implemented by Prototype. - - @method toArray - @return {Array} the enumerable as an array. - */ - toArray: function() { - var ret = Ember.A(); - this.forEach(function(o, idx) { ret[idx] = o; }); - return ret ; - }, - - /** - Returns a copy of the array with all null and undefined elements removed. - - ```javascript - var arr = ["a", null, "c", undefined]; - arr.compact(); // ["a", "c"] - ``` - - @method compact - @return {Array} the array without null and undefined elements. - */ - compact: function() { - return this.filter(function(value) { return value != null; }); - }, - - /** - Returns a new enumerable that excludes the passed value. The default - implementation returns an array regardless of the receiver type unless - the receiver does not contain the value. - - ```javascript - var arr = ["a", "b", "a", "c"]; - arr.without("a"); // ["b", "c"] - ``` - - @method without - @param {Object} value - @return {Ember.Enumerable} - */ - without: function(value) { - if (!this.contains(value)) return this; // nothing to do - var ret = Ember.A(); - this.forEach(function(k) { - if (k !== value) ret[ret.length] = k; - }) ; - return ret ; - }, - - /** - Returns a new enumerable that contains only unique values. The default - implementation returns an array regardless of the receiver type. - - ```javascript - var arr = ["a", "a", "b", "b"]; - arr.uniq(); // ["a", "b"] - ``` - - @method uniq - @return {Ember.Enumerable} - */ - uniq: function() { - var ret = Ember.A(); - this.forEach(function(k) { - if (a_indexOf(ret, k)<0) ret.push(k); - }); - return ret; - }, - - /** - This property will trigger anytime the enumerable's content changes. - You can observe this property to be notified of changes to the enumerables - content. - - For plain enumerables, this property is read only. `Ember.Array` overrides - this method. - - @property [] - @type Ember.Array - @return this - */ - '[]': Ember.computed(function(key, value) { - return this; - }), - - // .......................................................... - // ENUMERABLE OBSERVERS - // - - /** - Registers an enumerable observer. Must implement `Ember.EnumerableObserver` - mixin. - - @method addEnumerableObserver - @param {Object} target - @param {Hash} [opts] - @return this - */ - addEnumerableObserver: function(target, opts) { - var willChange = (opts && opts.willChange) || 'enumerableWillChange', - didChange = (opts && opts.didChange) || 'enumerableDidChange'; - - var hasObservers = get(this, 'hasEnumerableObservers'); - if (!hasObservers) Ember.propertyWillChange(this, 'hasEnumerableObservers'); - Ember.addListener(this, '@enumerable:before', target, willChange); - Ember.addListener(this, '@enumerable:change', target, didChange); - if (!hasObservers) Ember.propertyDidChange(this, 'hasEnumerableObservers'); - return this; - }, - - /** - Removes a registered enumerable observer. - - @method removeEnumerableObserver - @param {Object} target - @param {Hash} [opts] - @return this - */ - removeEnumerableObserver: function(target, opts) { - var willChange = (opts && opts.willChange) || 'enumerableWillChange', - didChange = (opts && opts.didChange) || 'enumerableDidChange'; - - var hasObservers = get(this, 'hasEnumerableObservers'); - if (hasObservers) Ember.propertyWillChange(this, 'hasEnumerableObservers'); - Ember.removeListener(this, '@enumerable:before', target, willChange); - Ember.removeListener(this, '@enumerable:change', target, didChange); - if (hasObservers) Ember.propertyDidChange(this, 'hasEnumerableObservers'); - return this; - }, - - /** - Becomes true whenever the array currently has observers watching changes - on the array. - - @property hasEnumerableObservers - @type Boolean - */ - hasEnumerableObservers: Ember.computed(function() { - return Ember.hasListeners(this, '@enumerable:change') || Ember.hasListeners(this, '@enumerable:before'); - }), - - - /** - Invoke this method just before the contents of your enumerable will - change. You can either omit the parameters completely or pass the objects - to be removed or added if available or just a count. - - @method enumerableContentWillChange - @param {Ember.Enumerable|Number} removing An enumerable of the objects to - be removed or the number of items to be removed. - @param {Ember.Enumerable|Number} adding An enumerable of the objects to be - added or the number of items to be added. - @chainable - */ - enumerableContentWillChange: function(removing, adding) { - - var removeCnt, addCnt, hasDelta; - - if ('number' === typeof removing) removeCnt = removing; - else if (removing) removeCnt = get(removing, 'length'); - else removeCnt = removing = -1; - - if ('number' === typeof adding) addCnt = adding; - else if (adding) addCnt = get(adding,'length'); - else addCnt = adding = -1; - - hasDelta = addCnt<0 || removeCnt<0 || addCnt-removeCnt!==0; - - if (removing === -1) removing = null; - if (adding === -1) adding = null; - - Ember.propertyWillChange(this, '[]'); - if (hasDelta) Ember.propertyWillChange(this, 'length'); - Ember.sendEvent(this, '@enumerable:before', [this, removing, adding]); - - return this; - }, - - /** - Invoke this method when the contents of your enumerable has changed. - This will notify any observers watching for content changes. If your are - implementing an ordered enumerable (such as an array), also pass the - start and end values where the content changed so that it can be used to - notify range observers. - - @method enumerableContentDidChange - @param {Ember.Enumerable|Number} removing An enumerable of the objects to - be removed or the number of items to be removed. - @param {Ember.Enumerable|Number} adding An enumerable of the objects to - be added or the number of items to be added. - @chainable - */ - enumerableContentDidChange: function(removing, adding) { - var removeCnt, addCnt, hasDelta; - - if ('number' === typeof removing) removeCnt = removing; - else if (removing) removeCnt = get(removing, 'length'); - else removeCnt = removing = -1; - - if ('number' === typeof adding) addCnt = adding; - else if (adding) addCnt = get(adding, 'length'); - else addCnt = adding = -1; - - hasDelta = addCnt<0 || removeCnt<0 || addCnt-removeCnt!==0; - - if (removing === -1) removing = null; - if (adding === -1) adding = null; - - Ember.sendEvent(this, '@enumerable:change', [this, removing, adding]); - if (hasDelta) Ember.propertyDidChange(this, 'length'); - Ember.propertyDidChange(this, '[]'); - - return this ; - }, - - /** - Converts the enumerable into an array and sorts by the keys - specified in the argument. - - You may provide multiple arguments to sort by multiple properties. - - @method sortBy - @param {String} property name(s) to sort on - @return {Array} The sorted array. - */ - sortBy: function() { - var sortKeys = arguments; - return this.toArray().sort(function(a, b){ - for(var i = 0; i < sortKeys.length; i++) { - var key = sortKeys[i], - propA = get(a, key), - propB = get(b, key); - // return 1 or -1 else continue to the next sortKey - var compareValue = Ember.compare(propA, propB); - if (compareValue) { return compareValue; } - } - return 0; - }); - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -// .......................................................... -// HELPERS -// - -var get = Ember.get, set = Ember.set, isNone = Ember.isNone, map = Ember.EnumerableUtils.map, cacheFor = Ember.cacheFor; - -// .......................................................... -// ARRAY -// -/** - This mixin implements Observer-friendly Array-like behavior. It is not a - concrete implementation, but it can be used up by other classes that want - to appear like arrays. - - For example, ArrayProxy and ArrayController are both concrete classes that can - be instantiated to implement array-like behavior. Both of these classes use - the Array Mixin by way of the MutableArray mixin, which allows observable - changes to be made to the underlying array. - - Unlike `Ember.Enumerable,` this mixin defines methods specifically for - collections that provide index-ordered access to their contents. When you - are designing code that needs to accept any kind of Array-like object, you - should use these methods instead of Array primitives because these will - properly notify observers of changes to the array. - - Although these methods are efficient, they do add a layer of indirection to - your application so it is a good idea to use them only when you need the - flexibility of using both true JavaScript arrays and "virtual" arrays such - as controllers and collections. - - You can use the methods defined in this module to access and modify array - contents in a KVO-friendly way. You can also be notified whenever the - membership of an array changes by changing the syntax of the property to - `.observes('*myProperty.[]')`. - - To support `Ember.Array` in your own class, you must override two - primitives to use it: `replace()` and `objectAt()`. - - Note that the Ember.Array mixin also incorporates the `Ember.Enumerable` - mixin. All `Ember.Array`-like objects are also enumerable. - - @class Array - @namespace Ember - @uses Ember.Enumerable - @since Ember 0.9.0 -*/ -Ember.Array = Ember.Mixin.create(Ember.Enumerable, { - - /** - Your array must support the `length` property. Your replace methods should - set this property whenever it changes. - - @property {Number} length - */ - length: Ember.required(), - - /** - Returns the object at the given `index`. If the given `index` is negative - or is greater or equal than the array length, returns `undefined`. - - This is one of the primitives you must implement to support `Ember.Array`. - If your object supports retrieving the value of an array item using `get()` - (i.e. `myArray.get(0)`), then you do not need to implement this method - yourself. - - ```javascript - var arr = ['a', 'b', 'c', 'd']; - arr.objectAt(0); // "a" - arr.objectAt(3); // "d" - arr.objectAt(-1); // undefined - arr.objectAt(4); // undefined - arr.objectAt(5); // undefined - ``` - - @method objectAt - @param {Number} idx The index of the item to return. - @return {*} item at index or undefined - */ - objectAt: function(idx) { - if ((idx < 0) || (idx>=get(this, 'length'))) return undefined ; - return get(this, idx); - }, - - /** - This returns the objects at the specified indexes, using `objectAt`. - - ```javascript - var arr = ['a', 'b', 'c', 'd']; - arr.objectsAt([0, 1, 2]); // ["a", "b", "c"] - arr.objectsAt([2, 3, 4]); // ["c", "d", undefined] - ``` - - @method objectsAt - @param {Array} indexes An array of indexes of items to return. - @return {Array} - */ - objectsAt: function(indexes) { - var self = this; - return map(indexes, function(idx) { return self.objectAt(idx); }); - }, - - // overrides Ember.Enumerable version - nextObject: function(idx) { - return this.objectAt(idx); - }, - - /** - This is the handler for the special array content property. If you get - this property, it will return this. If you set this property it a new - array, it will replace the current content. - - This property overrides the default property defined in `Ember.Enumerable`. - - @property [] - @return this - */ - '[]': Ember.computed(function(key, value) { - if (value !== undefined) this.replace(0, get(this, 'length'), value) ; - return this ; - }), - - firstObject: Ember.computed(function() { - return this.objectAt(0); - }), - - lastObject: Ember.computed(function() { - return this.objectAt(get(this, 'length')-1); - }), - - // optimized version from Enumerable - contains: function(obj) { - return this.indexOf(obj) >= 0; - }, - - // Add any extra methods to Ember.Array that are native to the built-in Array. - /** - Returns a new array that is a slice of the receiver. This implementation - uses the observable array methods to retrieve the objects for the new - slice. - - ```javascript - var arr = ['red', 'green', 'blue']; - arr.slice(0); // ['red', 'green', 'blue'] - arr.slice(0, 2); // ['red', 'green'] - arr.slice(1, 100); // ['green', 'blue'] - ``` - - @method slice - @param {Integer} beginIndex (Optional) index to begin slicing from. - @param {Integer} endIndex (Optional) index to end the slice at (but not included). - @return {Array} New array with specified slice - */ - slice: function(beginIndex, endIndex) { - var ret = Ember.A(); - var length = get(this, 'length') ; - if (isNone(beginIndex)) beginIndex = 0 ; - if (isNone(endIndex) || (endIndex > length)) endIndex = length ; - - if (beginIndex < 0) beginIndex = length + beginIndex; - if (endIndex < 0) endIndex = length + endIndex; - - while(beginIndex < endIndex) { - ret[ret.length] = this.objectAt(beginIndex++) ; - } - return ret ; - }, - - /** - Returns the index of the given object's first occurrence. - If no `startAt` argument is given, the starting location to - search is 0. If it's negative, will count backward from - the end of the array. Returns -1 if no match is found. - - ```javascript - var arr = ["a", "b", "c", "d", "a"]; - arr.indexOf("a"); // 0 - arr.indexOf("z"); // -1 - arr.indexOf("a", 2); // 4 - arr.indexOf("a", -1); // 4 - arr.indexOf("b", 3); // -1 - arr.indexOf("a", 100); // -1 - ``` - - @method indexOf - @param {Object} object the item to search for - @param {Number} startAt optional starting location to search, default 0 - @return {Number} index or -1 if not found - */ - indexOf: function(object, startAt) { - var idx, len = get(this, 'length'); - - if (startAt === undefined) startAt = 0; - if (startAt < 0) startAt += len; - - for(idx=startAt;idx= len) startAt = len-1; - if (startAt < 0) startAt += len; - - for(idx=startAt;idx>=0;idx--) { - if (this.objectAt(idx) === object) return idx ; - } - return -1; - }, - - // .......................................................... - // ARRAY OBSERVERS - // - - /** - Adds an array observer to the receiving array. The array observer object - normally must implement two methods: - - * `arrayWillChange(observedObj, start, removeCount, addCount)` - This method will be - called just before the array is modified. - * `arrayDidChange(observedObj, start, removeCount, addCount)` - This method will be - called just after the array is modified. - - Both callbacks will be passed the observed object, starting index of the - change as well a a count of the items to be removed and added. You can use - these callbacks to optionally inspect the array during the change, clear - caches, or do any other bookkeeping necessary. - - In addition to passing a target, you can also include an options hash - which you can use to override the method names that will be invoked on the - target. - - @method addArrayObserver - @param {Object} target The observer object. - @param {Hash} opts Optional hash of configuration options including - `willChange` and `didChange` option. - @return {Ember.Array} receiver - */ - addArrayObserver: function(target, opts) { - var willChange = (opts && opts.willChange) || 'arrayWillChange', - didChange = (opts && opts.didChange) || 'arrayDidChange'; - - var hasObservers = get(this, 'hasArrayObservers'); - if (!hasObservers) Ember.propertyWillChange(this, 'hasArrayObservers'); - Ember.addListener(this, '@array:before', target, willChange); - Ember.addListener(this, '@array:change', target, didChange); - if (!hasObservers) Ember.propertyDidChange(this, 'hasArrayObservers'); - return this; - }, - - /** - Removes an array observer from the object if the observer is current - registered. Calling this method multiple times with the same object will - have no effect. - - @method removeArrayObserver - @param {Object} target The object observing the array. - @param {Hash} opts Optional hash of configuration options including - `willChange` and `didChange` option. - @return {Ember.Array} receiver - */ - removeArrayObserver: function(target, opts) { - var willChange = (opts && opts.willChange) || 'arrayWillChange', - didChange = (opts && opts.didChange) || 'arrayDidChange'; - - var hasObservers = get(this, 'hasArrayObservers'); - if (hasObservers) Ember.propertyWillChange(this, 'hasArrayObservers'); - Ember.removeListener(this, '@array:before', target, willChange); - Ember.removeListener(this, '@array:change', target, didChange); - if (hasObservers) Ember.propertyDidChange(this, 'hasArrayObservers'); - return this; - }, - - /** - Becomes true whenever the array currently has observers watching changes - on the array. - - @property {Boolean} hasArrayObservers - */ - hasArrayObservers: Ember.computed(function() { - return Ember.hasListeners(this, '@array:change') || Ember.hasListeners(this, '@array:before'); - }), - - /** - If you are implementing an object that supports `Ember.Array`, call this - method just before the array content changes to notify any observers and - invalidate any related properties. Pass the starting index of the change - as well as a delta of the amounts to change. - - @method arrayContentWillChange - @param {Number} startIdx The starting index in the array that will change. - @param {Number} removeAmt The number of items that will be removed. If you - pass `null` assumes 0 - @param {Number} addAmt The number of items that will be added. If you - pass `null` assumes 0. - @return {Ember.Array} receiver - */ - arrayContentWillChange: function(startIdx, removeAmt, addAmt) { - - // if no args are passed assume everything changes - if (startIdx===undefined) { - startIdx = 0; - removeAmt = addAmt = -1; - } else { - if (removeAmt === undefined) removeAmt=-1; - if (addAmt === undefined) addAmt=-1; - } - - // Make sure the @each proxy is set up if anyone is observing @each - if (Ember.isWatching(this, '@each')) { get(this, '@each'); } - - Ember.sendEvent(this, '@array:before', [this, startIdx, removeAmt, addAmt]); - - var removing, lim; - if (startIdx>=0 && removeAmt>=0 && get(this, 'hasEnumerableObservers')) { - removing = []; - lim = startIdx+removeAmt; - for(var idx=startIdx;idx=0 && addAmt>=0 && get(this, 'hasEnumerableObservers')) { - adding = []; - lim = startIdx+addAmt; - for(var idx=startIdx;idx Ember.TrackedArray instances. We use - // this to lazily recompute indexes for item property observers. - this.trackedArraysByGuid = {}; - - // We suspend observers to ignore replacements from `reset` when totally - // recomputing. Unfortunately we cannot properly suspend the observers - // because we only have the key; instead we make the observers no-ops - this.suspended = false; - - // This is used to coalesce item changes from property observers. - this.changedItems = {}; -} - -function ItemPropertyObserverContext (dependentArray, index, trackedArray) { - Ember.assert("Internal error: trackedArray is null or undefined", trackedArray); - - this.dependentArray = dependentArray; - this.index = index; - this.item = dependentArray.objectAt(index); - this.trackedArray = trackedArray; - this.beforeObserver = null; - this.observer = null; - - this.destroyed = false; -} - -DependentArraysObserver.prototype = { - setValue: function (newValue) { - this.instanceMeta.setValue(newValue, true); - }, - getValue: function () { - return this.instanceMeta.getValue(); - }, - - setupObservers: function (dependentArray, dependentKey) { - this.dependentKeysByGuid[guidFor(dependentArray)] = dependentKey; - - dependentArray.addArrayObserver(this, { - willChange: 'dependentArrayWillChange', - didChange: 'dependentArrayDidChange' - }); - - if (this.cp._itemPropertyKeys[dependentKey]) { - this.setupPropertyObservers(dependentKey, this.cp._itemPropertyKeys[dependentKey]); - } - }, - - teardownObservers: function (dependentArray, dependentKey) { - var itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || []; - - delete this.dependentKeysByGuid[guidFor(dependentArray)]; - - this.teardownPropertyObservers(dependentKey, itemPropertyKeys); - - dependentArray.removeArrayObserver(this, { - willChange: 'dependentArrayWillChange', - didChange: 'dependentArrayDidChange' - }); - }, - - suspendArrayObservers: function (callback, binding) { - var oldSuspended = this.suspended; - this.suspended = true; - callback.call(binding); - this.suspended = oldSuspended; - }, - - setupPropertyObservers: function (dependentKey, itemPropertyKeys) { - var dependentArray = get(this.instanceMeta.context, dependentKey), - length = get(dependentArray, 'length'), - observerContexts = new Array(length); - - this.resetTransformations(dependentKey, observerContexts); - - forEach(dependentArray, function (item, index) { - var observerContext = this.createPropertyObserverContext(dependentArray, index, this.trackedArraysByGuid[dependentKey]); - observerContexts[index] = observerContext; - - forEach(itemPropertyKeys, function (propertyKey) { - addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver); - addObserver(item, propertyKey, this, observerContext.observer); - }, this); - }, this); - }, - - teardownPropertyObservers: function (dependentKey, itemPropertyKeys) { - var dependentArrayObserver = this, - trackedArray = this.trackedArraysByGuid[dependentKey], - beforeObserver, - observer, - item; - - if (!trackedArray) { return; } - - trackedArray.apply(function (observerContexts, offset, operation) { - if (operation === Ember.TrackedArray.DELETE) { return; } - - forEach(observerContexts, function (observerContext) { - observerContext.destroyed = true; - beforeObserver = observerContext.beforeObserver; - observer = observerContext.observer; - item = observerContext.item; - - forEach(itemPropertyKeys, function (propertyKey) { - removeBeforeObserver(item, propertyKey, dependentArrayObserver, beforeObserver); - removeObserver(item, propertyKey, dependentArrayObserver, observer); - }); - }); - }); - }, - - createPropertyObserverContext: function (dependentArray, index, trackedArray) { - var observerContext = new ItemPropertyObserverContext(dependentArray, index, trackedArray); - - this.createPropertyObserver(observerContext); - - return observerContext; - }, - - createPropertyObserver: function (observerContext) { - var dependentArrayObserver = this; - - observerContext.beforeObserver = function (obj, keyName) { - return dependentArrayObserver.itemPropertyWillChange(obj, keyName, observerContext.dependentArray, observerContext); - }; - observerContext.observer = function (obj, keyName) { - return dependentArrayObserver.itemPropertyDidChange(obj, keyName, observerContext.dependentArray, observerContext); - }; - }, - - resetTransformations: function (dependentKey, observerContexts) { - this.trackedArraysByGuid[dependentKey] = new Ember.TrackedArray(observerContexts); - }, - - trackAdd: function (dependentKey, index, newItems) { - var trackedArray = this.trackedArraysByGuid[dependentKey]; - if (trackedArray) { - trackedArray.addItems(index, newItems); - } - }, - - trackRemove: function (dependentKey, index, removedCount) { - var trackedArray = this.trackedArraysByGuid[dependentKey]; - - if (trackedArray) { - return trackedArray.removeItems(index, removedCount); - } - - return []; - }, - - updateIndexes: function (trackedArray, array) { - var length = get(array, 'length'); - // OPTIMIZE: we could stop updating once we hit the object whose observer - // fired; ie partially apply the transformations - trackedArray.apply(function (observerContexts, offset, operation) { - // we don't even have observer contexts for removed items, even if we did, - // they no longer have any index in the array - if (operation === Ember.TrackedArray.DELETE) { return; } - if (operation === Ember.TrackedArray.RETAIN && observerContexts.length === length && offset === 0) { - // If we update many items we don't want to walk the array each time: we - // only need to update the indexes at most once per run loop. - return; - } - - forEach(observerContexts, function (context, index) { - context.index = index + offset; - }); - }); - }, - - dependentArrayWillChange: function (dependentArray, index, removedCount, addedCount) { - if (this.suspended) { return; } - - var removedItem = this.callbacks.removedItem, - changeMeta, - guid = guidFor(dependentArray), - dependentKey = this.dependentKeysByGuid[guid], - itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || [], - length = get(dependentArray, 'length'), - normalizedIndex = normalizeIndex(index, length, 0), - normalizedRemoveCount = normalizeRemoveCount(normalizedIndex, length, removedCount), - item, - itemIndex, - sliceIndex, - observerContexts; - - observerContexts = this.trackRemove(dependentKey, normalizedIndex, normalizedRemoveCount); - - function removeObservers(propertyKey) { - observerContexts[sliceIndex].destroyed = true; - removeBeforeObserver(item, propertyKey, this, observerContexts[sliceIndex].beforeObserver); - removeObserver(item, propertyKey, this, observerContexts[sliceIndex].observer); - } - - for (sliceIndex = normalizedRemoveCount - 1; sliceIndex >= 0; --sliceIndex) { - itemIndex = normalizedIndex + sliceIndex; - if (itemIndex >= length) { break; } - - item = dependentArray.objectAt(itemIndex); - - forEach(itemPropertyKeys, removeObservers, this); - - changeMeta = createChangeMeta(dependentArray, item, itemIndex, this.instanceMeta.propertyName, this.cp); - this.setValue( removedItem.call( - this.instanceMeta.context, this.getValue(), item, changeMeta, this.instanceMeta.sugarMeta)); - } - }, - - dependentArrayDidChange: function (dependentArray, index, removedCount, addedCount) { - if (this.suspended) { return; } - - var addedItem = this.callbacks.addedItem, - guid = guidFor(dependentArray), - dependentKey = this.dependentKeysByGuid[guid], - observerContexts = new Array(addedCount), - itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey], - length = get(dependentArray, 'length'), - normalizedIndex = normalizeIndex(index, length, addedCount), - changeMeta, - observerContext; - - forEach(dependentArray.slice(normalizedIndex, normalizedIndex + addedCount), function (item, sliceIndex) { - if (itemPropertyKeys) { - observerContext = - observerContexts[sliceIndex] = - this.createPropertyObserverContext(dependentArray, normalizedIndex + sliceIndex, this.trackedArraysByGuid[dependentKey]); - forEach(itemPropertyKeys, function (propertyKey) { - addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver); - addObserver(item, propertyKey, this, observerContext.observer); - }, this); - } - - changeMeta = createChangeMeta(dependentArray, item, normalizedIndex + sliceIndex, this.instanceMeta.propertyName, this.cp); - this.setValue( addedItem.call( - this.instanceMeta.context, this.getValue(), item, changeMeta, this.instanceMeta.sugarMeta)); - }, this); - - this.trackAdd(dependentKey, normalizedIndex, observerContexts); - }, - - itemPropertyWillChange: function (obj, keyName, array, observerContext) { - var guid = guidFor(obj); - - if (!this.changedItems[guid]) { - this.changedItems[guid] = { - array: array, - observerContext: observerContext, - obj: obj, - previousValues: {} - }; - } - - this.changedItems[guid].previousValues[keyName] = get(obj, keyName); - }, - - itemPropertyDidChange: function(obj, keyName, array, observerContext) { - this.flushChanges(); - }, - - flushChanges: function() { - var changedItems = this.changedItems, key, c, changeMeta; - - for (key in changedItems) { - c = changedItems[key]; - if (c.observerContext.destroyed) { continue; } - - this.updateIndexes(c.observerContext.trackedArray, c.observerContext.dependentArray); - - changeMeta = createChangeMeta(c.array, c.obj, c.observerContext.index, this.instanceMeta.propertyName, this.cp, c.previousValues); - this.setValue( - this.callbacks.removedItem.call(this.instanceMeta.context, this.getValue(), c.obj, changeMeta, this.instanceMeta.sugarMeta)); - this.setValue( - this.callbacks.addedItem.call(this.instanceMeta.context, this.getValue(), c.obj, changeMeta, this.instanceMeta.sugarMeta)); - } - this.changedItems = {}; - } -}; - -function normalizeIndex(index, length, newItemsOffset) { - if (index < 0) { - return Math.max(0, length + index); - } else if (index < length) { - return index; - } else /* index > length */ { - return Math.min(length - newItemsOffset, index); - } -} - -function normalizeRemoveCount(index, length, removedCount) { - return Math.min(removedCount, length - index); -} - -function createChangeMeta(dependentArray, item, index, propertyName, property, previousValues) { - var meta = { - arrayChanged: dependentArray, - index: index, - item: item, - propertyName: propertyName, - property: property - }; - - if (previousValues) { - // previous values only available for item property changes - meta.previousValues = previousValues; - } - - return meta; -} - -function addItems (dependentArray, callbacks, cp, propertyName, meta) { - forEach(dependentArray, function (item, index) { - meta.setValue( callbacks.addedItem.call( - this, meta.getValue(), item, createChangeMeta(dependentArray, item, index, propertyName, cp), meta.sugarMeta)); - }, this); -} - -function reset(cp, propertyName) { - var callbacks = cp._callbacks(), - meta; - - if (cp._hasInstanceMeta(this, propertyName)) { - meta = cp._instanceMeta(this, propertyName); - meta.setValue(cp.resetValue(meta.getValue())); - } else { - meta = cp._instanceMeta(this, propertyName); - } - - if (cp.options.initialize) { - cp.options.initialize.call(this, meta.getValue(), { property: cp, propertyName: propertyName }, meta.sugarMeta); - } -} - -function partiallyRecomputeFor(obj, dependentKey) { - if (arrayBracketPattern.test(dependentKey)) { - return false; - } - - var value = get(obj, dependentKey); - return Ember.Array.detect(value); -} - -function ReduceComputedPropertyInstanceMeta(context, propertyName, initialValue) { - this.context = context; - this.propertyName = propertyName; - this.cache = metaFor(context).cache; - - this.dependentArrays = {}; - this.sugarMeta = {}; - - this.initialValue = initialValue; -} - -ReduceComputedPropertyInstanceMeta.prototype = { - getValue: function () { - if (this.propertyName in this.cache) { - return this.cache[this.propertyName]; - } else { - return this.initialValue; - } - }, - - setValue: function(newValue, triggerObservers) { - // This lets sugars force a recomputation, handy for very simple - // implementations of eg max. - if (newValue === this.cache[this.propertyName]) { - return; - } - - if (triggerObservers) { - propertyWillChange(this.context, this.propertyName); - } - - if (newValue === undefined) { - delete this.cache[this.propertyName]; - } else { - this.cache[this.propertyName] = newValue; - } - - if (triggerObservers) { - propertyDidChange(this.context, this.propertyName); - } - } -}; - -/** - A computed property whose dependent keys are arrays and which is updated with - "one at a time" semantics. - - @class ReduceComputedProperty - @namespace Ember - @extends Ember.ComputedProperty - @constructor -*/ -function ReduceComputedProperty(options) { - var cp = this; - - this.options = options; - - this._dependentKeys = null; - // A map of dependentKey -> [itemProperty, ...] that tracks what properties of - // items in the array we must track to update this property. - this._itemPropertyKeys = {}; - this._previousItemPropertyKeys = {}; - - this.readOnly(); - this.cacheable(); - - this.recomputeOnce = function(propertyName) { - // What we really want to do is coalesce by . - // We need a form of `scheduleOnce` that accepts an arbitrary token to - // coalesce by, in addition to the target and method. - Ember.run.once(this, recompute, propertyName); - }; - var recompute = function(propertyName) { - var dependentKeys = cp._dependentKeys, - meta = cp._instanceMeta(this, propertyName), - callbacks = cp._callbacks(); - - reset.call(this, cp, propertyName); - - meta.dependentArraysObserver.suspendArrayObservers(function () { - forEach(cp._dependentKeys, function (dependentKey) { - Ember.assert( - "dependent array " + dependentKey + " must be an `Ember.Array`. " + - "If you are not extending arrays, you will need to wrap native arrays with `Ember.A`", - !(Ember.isArray(get(this, dependentKey)) && !Ember.Array.detect(get(this, dependentKey)))); - - if (!partiallyRecomputeFor(this, dependentKey)) { return; } - - var dependentArray = get(this, dependentKey), - previousDependentArray = meta.dependentArrays[dependentKey]; - - if (dependentArray === previousDependentArray) { - // The array may be the same, but our item property keys may have - // changed, so we set them up again. We can't easily tell if they've - // changed: the array may be the same object, but with different - // contents. - if (cp._previousItemPropertyKeys[dependentKey]) { - delete cp._previousItemPropertyKeys[dependentKey]; - meta.dependentArraysObserver.setupPropertyObservers(dependentKey, cp._itemPropertyKeys[dependentKey]); - } - } else { - meta.dependentArrays[dependentKey] = dependentArray; - - if (previousDependentArray) { - meta.dependentArraysObserver.teardownObservers(previousDependentArray, dependentKey); - } - - if (dependentArray) { - meta.dependentArraysObserver.setupObservers(dependentArray, dependentKey); - } - } - }, this); - }, this); - - forEach(cp._dependentKeys, function(dependentKey) { - if (!partiallyRecomputeFor(this, dependentKey)) { return; } - - var dependentArray = get(this, dependentKey); - if (dependentArray) { - addItems.call(this, dependentArray, callbacks, cp, propertyName, meta); - } - }, this); - }; - - - this.func = function (propertyName) { - Ember.assert("Computed reduce values require at least one dependent key", cp._dependentKeys); - - recompute.call(this, propertyName); - - return cp._instanceMeta(this, propertyName).getValue(); - }; -} - -Ember.ReduceComputedProperty = ReduceComputedProperty; -ReduceComputedProperty.prototype = o_create(ComputedProperty.prototype); - -function defaultCallback(computedValue) { - return computedValue; -} - -ReduceComputedProperty.prototype._callbacks = function () { - if (!this.callbacks) { - var options = this.options; - this.callbacks = { - removedItem: options.removedItem || defaultCallback, - addedItem: options.addedItem || defaultCallback - }; - } - return this.callbacks; -}; - -ReduceComputedProperty.prototype._hasInstanceMeta = function (context, propertyName) { - return !!metaFor(context).cacheMeta[propertyName]; -}; - -ReduceComputedProperty.prototype._instanceMeta = function (context, propertyName) { - var cacheMeta = metaFor(context).cacheMeta, - meta = cacheMeta[propertyName]; - - if (!meta) { - meta = cacheMeta[propertyName] = new ReduceComputedPropertyInstanceMeta(context, propertyName, this.initialValue()); - meta.dependentArraysObserver = new DependentArraysObserver(this._callbacks(), this, meta, context, propertyName, meta.sugarMeta); - } - - return meta; -}; - -ReduceComputedProperty.prototype.initialValue = function () { - if (typeof this.options.initialValue === 'function') { - return this.options.initialValue(); - } - else { - return this.options.initialValue; - } -}; - -ReduceComputedProperty.prototype.resetValue = function (value) { - return this.initialValue(); -}; - -ReduceComputedProperty.prototype.itemPropertyKey = function (dependentArrayKey, itemPropertyKey) { - this._itemPropertyKeys[dependentArrayKey] = this._itemPropertyKeys[dependentArrayKey] || []; - this._itemPropertyKeys[dependentArrayKey].push(itemPropertyKey); -}; - -ReduceComputedProperty.prototype.clearItemPropertyKeys = function (dependentArrayKey) { - if (this._itemPropertyKeys[dependentArrayKey]) { - this._previousItemPropertyKeys[dependentArrayKey] = this._itemPropertyKeys[dependentArrayKey]; - this._itemPropertyKeys[dependentArrayKey] = []; - } -}; - -ReduceComputedProperty.prototype.property = function () { - var cp = this, - args = a_slice.call(arguments), - propertyArgs = new Ember.Set(), - match, - dependentArrayKey, - itemPropertyKey; - - forEach(args, function (dependentKey) { - if (doubleEachPropertyPattern.test(dependentKey)) { - throw new Ember.Error("Nested @each properties not supported: " + dependentKey); - } else if (match = eachPropertyPattern.exec(dependentKey)) { - dependentArrayKey = match[1]; - - var itemPropertyKeyPattern = match[2], - addItemPropertyKey = function (itemPropertyKey) { - cp.itemPropertyKey(dependentArrayKey, itemPropertyKey); - }; - - expandProperties(itemPropertyKeyPattern, addItemPropertyKey); - propertyArgs.add(dependentArrayKey); - } else { - propertyArgs.add(dependentKey); - } - }); - - return ComputedProperty.prototype.property.apply(this, propertyArgs.toArray()); - -}; - -/** - Creates a computed property which operates on dependent arrays and - is updated with "one at a time" semantics. When items are added or - removed from the dependent array(s) a reduce computed only operates - on the change instead of re-evaluating the entire array. - - If there are more than one arguments the first arguments are - considered to be dependent property keys. The last argument is - required to be an options object. The options object can have the - following four properties: - - `initialValue` - A value or function that will be used as the initial - value for the computed. If this property is a function the result of calling - the function will be used as the initial value. This property is required. - - `initialize` - An optional initialize function. Typically this will be used - to set up state on the instanceMeta object. - - `removedItem` - A function that is called each time an element is removed - from the array. - - `addedItem` - A function that is called each time an element is added to - the array. - - - The `initialize` function has the following signature: - - ```javascript - function (initialValue, changeMeta, instanceMeta) - ``` - - `initialValue` - The value of the `initialValue` property from the - options object. - - `changeMeta` - An object which contains meta information about the - computed. It contains the following properties: - - - `property` the computed property - - `propertyName` the name of the property on the object - - `instanceMeta` - An object that can be used to store meta - information needed for calculating your computed. For example a - unique computed might use this to store the number of times a given - element is found in the dependent array. - - - The `removedItem` and `addedItem` functions both have the following signature: - - ```javascript - function (accumulatedValue, item, changeMeta, instanceMeta) - ``` - - `accumulatedValue` - The value returned from the last time - `removedItem` or `addedItem` was called or `initialValue`. - - `item` - the element added or removed from the array - - `changeMeta` - An object which contains meta information about the - change. It contains the following properties: - - - `property` the computed property - - `propertyName` the name of the property on the object - - `index` the index of the added or removed item - - `item` the added or removed item: this is exactly the same as - the second arg - - `arrayChanged` the array that triggered the change. Can be - useful when depending on multiple arrays. - - For property changes triggered on an item property change (when - depKey is something like `someArray.@each.someProperty`), - `changeMeta` will also contain the following property: - - - `previousValues` an object whose keys are the properties that changed on - the item, and whose values are the item's previous values. - - `previousValues` is important Ember coalesces item property changes via - Ember.run.once. This means that by the time removedItem gets called, item has - the new values, but you may need the previous value (eg for sorting & - filtering). - - `instanceMeta` - An object that can be used to store meta - information needed for calculating your computed. For example a - unique computed might use this to store the number of times a given - element is found in the dependent array. - - The `removedItem` and `addedItem` functions should return the accumulated - value. It is acceptable to not return anything (ie return undefined) - to invalidate the computation. This is generally not a good idea for - arrayComputed but it's used in eg max and min. - - Note that observers will be fired if either of these functions return a value - that differs from the accumulated value. When returning an object that - mutates in response to array changes, for example an array that maps - everything from some other array (see `Ember.computed.map`), it is usually - important that the *same* array be returned to avoid accidentally triggering observers. - - Example - - ```javascript - Ember.computed.max = function (dependentKey) { - return Ember.reduceComputed(dependentKey, { - initialValue: -Infinity, - - addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - return Math.max(accumulatedValue, item); - }, - - removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - if (item < accumulatedValue) { - return accumulatedValue; - } - } - }); - }; - ``` - - Dependent keys may refer to `@this` to observe changes to the object itself, - which must be array-like, rather than a property of the object. This is - mostly useful for array proxies, to ensure objects are retrieved via - `objectAtContent`. This is how you could sort items by properties defined on an item controller. - - Example - - ```javascript - App.PeopleController = Ember.ArrayController.extend({ - itemController: 'person', - - sortedPeople: Ember.computed.sort('@this.@each.reversedName', function(personA, personB) { - // `reversedName` isn't defined on Person, but we have access to it via - // the item controller App.PersonController. If we'd used - // `content.@each.reversedName` above, we would be getting the objects - // directly and not have access to `reversedName`. - // - var reversedNameA = get(personA, 'reversedName'), - reversedNameB = get(personB, 'reversedName'); - - return Ember.compare(reversedNameA, reversedNameB); - }) - }); - - App.PersonController = Ember.ObjectController.extend({ - reversedName: function () { - return reverse(get(this, 'name')); - }.property('name') - }) - ``` - - Dependent keys whose values are not arrays are treated as regular - dependencies: when they change, the computed property is completely - recalculated. It is sometimes useful to have dependent arrays with similar - semantics. Dependent keys which end in `.[]` do not use "one at a time" - semantics. When an item is added or removed from such a dependency, the - computed property is completely recomputed. - - Example - - ```javascript - Ember.Object.extend({ - // When `string` is changed, `computed` is completely recomputed. - string: 'a string', - - // When an item is added to `array`, `addedItem` is called. - array: [], - - // When an item is added to `anotherArray`, `computed` is completely - // recomputed. - anotherArray: [], - - computed: Ember.reduceComputed('string', 'array', 'anotherArray.[]', { - addedItem: addedItemCallback, - removedItem: removedItemCallback - }) - }); - ``` - - @method reduceComputed - @for Ember - @param {String} [dependentKeys*] - @param {Object} options - @return {Ember.ComputedProperty} -*/ -Ember.reduceComputed = function (options) { - var args; - - if (arguments.length > 1) { - args = a_slice.call(arguments, 0, -1); - options = a_slice.call(arguments, -1)[0]; - } - - if (typeof options !== "object") { - throw new Ember.Error("Reduce Computed Property declared without an options hash"); - } - - if (!('initialValue' in options)) { - throw new Ember.Error("Reduce Computed Property declared without an initial value"); - } - - var cp = new ReduceComputedProperty(options); - - if (args) { - cp.property.apply(cp, args); - } - - return cp; -}; - -})(); - - - -(function() { -var ReduceComputedProperty = Ember.ReduceComputedProperty, - a_slice = [].slice, - o_create = Ember.create, - forEach = Ember.EnumerableUtils.forEach; - -function ArrayComputedProperty() { - var cp = this; - - ReduceComputedProperty.apply(this, arguments); - - this.func = (function(reduceFunc) { - return function (propertyName) { - if (!cp._hasInstanceMeta(this, propertyName)) { - // When we recompute an array computed property, we need already - // retrieved arrays to be updated; we can't simply empty the cache and - // hope the array is re-retrieved. - forEach(cp._dependentKeys, function(dependentKey) { - Ember.addObserver(this, dependentKey, function() { - cp.recomputeOnce.call(this, propertyName); - }); - }, this); - } - - return reduceFunc.apply(this, arguments); - }; - })(this.func); - - return this; -} -Ember.ArrayComputedProperty = ArrayComputedProperty; -ArrayComputedProperty.prototype = o_create(ReduceComputedProperty.prototype); -ArrayComputedProperty.prototype.initialValue = function () { - return Ember.A(); -}; -ArrayComputedProperty.prototype.resetValue = function (array) { - array.clear(); - return array; -}; - -// This is a stopgap to keep the reference counts correct with lazy CPs. -ArrayComputedProperty.prototype.didChange = function (obj, keyName) { - return; -}; - -/** - Creates a computed property which operates on dependent arrays and - is updated with "one at a time" semantics. When items are added or - removed from the dependent array(s) an array computed only operates - on the change instead of re-evaluating the entire array. This should - return an array, if you'd like to use "one at a time" semantics and - compute some value other then an array look at - `Ember.reduceComputed`. - - If there are more than one arguments the first arguments are - considered to be dependent property keys. The last argument is - required to be an options object. The options object can have the - following three properties. - - `initialize` - An optional initialize function. Typically this will be used - to set up state on the instanceMeta object. - - `removedItem` - A function that is called each time an element is - removed from the array. - - `addedItem` - A function that is called each time an element is - added to the array. - - - The `initialize` function has the following signature: - - ```javascript - function (array, changeMeta, instanceMeta) - ``` - - `array` - The initial value of the arrayComputed, an empty array. - - `changeMeta` - An object which contains meta information about the - computed. It contains the following properties: - - - `property` the computed property - - `propertyName` the name of the property on the object - - `instanceMeta` - An object that can be used to store meta - information needed for calculating your computed. For example a - unique computed might use this to store the number of times a given - element is found in the dependent array. - - - The `removedItem` and `addedItem` functions both have the following signature: - - ```javascript - function (accumulatedValue, item, changeMeta, instanceMeta) - ``` - - `accumulatedValue` - The value returned from the last time - `removedItem` or `addedItem` was called or an empty array. - - `item` - the element added or removed from the array - - `changeMeta` - An object which contains meta information about the - change. It contains the following properties: - - - `property` the computed property - - `propertyName` the name of the property on the object - - `index` the index of the added or removed item - - `item` the added or removed item: this is exactly the same as - the second arg - - `arrayChanged` the array that triggered the change. Can be - useful when depending on multiple arrays. - - For property changes triggered on an item property change (when - depKey is something like `someArray.@each.someProperty`), - `changeMeta` will also contain the following property: - - - `previousValues` an object whose keys are the properties that changed on - the item, and whose values are the item's previous values. - - `previousValues` is important Ember coalesces item property changes via - Ember.run.once. This means that by the time removedItem gets called, item has - the new values, but you may need the previous value (eg for sorting & - filtering). - - `instanceMeta` - An object that can be used to store meta - information needed for calculating your computed. For example a - unique computed might use this to store the number of times a given - element is found in the dependent array. - - The `removedItem` and `addedItem` functions should return the accumulated - value. It is acceptable to not return anything (ie return undefined) - to invalidate the computation. This is generally not a good idea for - arrayComputed but it's used in eg max and min. - - Example - - ```javascript - Ember.computed.map = function(dependentKey, callback) { - var options = { - addedItem: function(array, item, changeMeta, instanceMeta) { - var mapped = callback(item); - array.insertAt(changeMeta.index, mapped); - return array; - }, - removedItem: function(array, item, changeMeta, instanceMeta) { - array.removeAt(changeMeta.index, 1); - return array; - } - }; - - return Ember.arrayComputed(dependentKey, options); - }; - ``` - - @method arrayComputed - @for Ember - @param {String} [dependentKeys*] - @param {Object} options - @return {Ember.ComputedProperty} -*/ -Ember.arrayComputed = function (options) { - var args; - - if (arguments.length > 1) { - args = a_slice.call(arguments, 0, -1); - options = a_slice.call(arguments, -1)[0]; - } - - if (typeof options !== "object") { - throw new Ember.Error("Array Computed Property declared without an options hash"); - } - - var cp = new ArrayComputedProperty(options); - - if (args) { - cp.property.apply(cp, args); - } - - return cp; -}; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var get = Ember.get, - set = Ember.set, - guidFor = Ember.guidFor, - merge = Ember.merge, - a_slice = [].slice, - forEach = Ember.EnumerableUtils.forEach, - map = Ember.EnumerableUtils.map, - SearchProxy; - -/** - A computed property that returns the sum of the value - in the dependent array. - - @method computed.sum - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computes the sum of all values in the dependentKey's array -*/ - -Ember.computed.sum = function(dependentKey){ - return Ember.reduceComputed(dependentKey, { - initialValue: 0, - - addedItem: function(accumulatedValue, item, changeMeta, instanceMeta){ - return accumulatedValue + item; - }, - - removedItem: function(accumulatedValue, item, changeMeta, instanceMeta){ - return accumulatedValue - item; - } - }); -}; - -/** - A computed property that calculates the maximum value in the - dependent array. This will return `-Infinity` when the dependent - array is empty. - - ```javascript - App.Person = Ember.Object.extend({ - childAges: Ember.computed.mapBy('children', 'age'), - maxChildAge: Ember.computed.max('childAges') - }); - - var lordByron = App.Person.create({children: []}); - lordByron.get('maxChildAge'); // -Infinity - lordByron.get('children').pushObject({ - name: 'Augusta Ada Byron', age: 7 - }); - lordByron.get('maxChildAge'); // 7 - lordByron.get('children').pushObjects([{ - name: 'Allegra Byron', - age: 5 - }, { - name: 'Elizabeth Medora Leigh', - age: 8 - }]); - lordByron.get('maxChildAge'); // 8 - ``` - - @method computed.max - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computes the largest value in the dependentKey's array -*/ -Ember.computed.max = function (dependentKey) { - return Ember.reduceComputed(dependentKey, { - initialValue: -Infinity, - - addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - return Math.max(accumulatedValue, item); - }, - - removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - if (item < accumulatedValue) { - return accumulatedValue; - } - } - }); -}; - -/** - A computed property that calculates the minimum value in the - dependent array. This will return `Infinity` when the dependent - array is empty. - - ```javascript - App.Person = Ember.Object.extend({ - childAges: Ember.computed.mapBy('children', 'age'), - minChildAge: Ember.computed.min('childAges') - }); - - var lordByron = App.Person.create({children: []}); - lordByron.get('minChildAge'); // Infinity - lordByron.get('children').pushObject({ - name: 'Augusta Ada Byron', age: 7 - }); - lordByron.get('minChildAge'); // 7 - lordByron.get('children').pushObjects([{ - name: 'Allegra Byron', - age: 5 - }, { - name: 'Elizabeth Medora Leigh', - age: 8 - }]); - lordByron.get('minChildAge'); // 5 - ``` - - @method computed.min - @for Ember - @param {String} dependentKey - @return {Ember.ComputedProperty} computes the smallest value in the dependentKey's array -*/ -Ember.computed.min = function (dependentKey) { - return Ember.reduceComputed(dependentKey, { - initialValue: Infinity, - - addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - return Math.min(accumulatedValue, item); - }, - - removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { - if (item > accumulatedValue) { - return accumulatedValue; - } - } - }); -}; - -/** - Returns an array mapped via the callback - - The callback method you provide should have the following signature. - `item` is the current item in the iteration. - - ```javascript - function(item); - ``` - - Example - - ```javascript - App.Hamster = Ember.Object.extend({ - excitingChores: Ember.computed.map('chores', function(chore) { - return chore.toUpperCase() + '!'; - }) - }); - - var hamster = App.Hamster.create({ - chores: ['clean', 'write more unit tests'] - }); - hamster.get('excitingChores'); // ['CLEAN!', 'WRITE MORE UNIT TESTS!'] - ``` - - @method computed.map - @for Ember - @param {String} dependentKey - @param {Function} callback - @return {Ember.ComputedProperty} an array mapped via the callback -*/ -Ember.computed.map = function(dependentKey, callback) { - var options = { - addedItem: function(array, item, changeMeta, instanceMeta) { - var mapped = callback.call(this, item); - array.insertAt(changeMeta.index, mapped); - return array; - }, - removedItem: function(array, item, changeMeta, instanceMeta) { - array.removeAt(changeMeta.index, 1); - return array; - } - }; - - return Ember.arrayComputed(dependentKey, options); -}; - -/** - Returns an array mapped to the specified key. - - ```javascript - App.Person = Ember.Object.extend({ - childAges: Ember.computed.mapBy('children', 'age') - }); - - var lordByron = App.Person.create({children: []}); - lordByron.get('childAges'); // [] - lordByron.get('children').pushObject({name: 'Augusta Ada Byron', age: 7}); - lordByron.get('childAges'); // [7] - lordByron.get('children').pushObjects([{ - name: 'Allegra Byron', - age: 5 - }, { - name: 'Elizabeth Medora Leigh', - age: 8 - }]); - lordByron.get('childAges'); // [7, 5, 8] - ``` - - @method computed.mapBy - @for Ember - @param {String} dependentKey - @param {String} propertyKey - @return {Ember.ComputedProperty} an array mapped to the specified key -*/ -Ember.computed.mapBy = function(dependentKey, propertyKey) { - var callback = function(item) { return get(item, propertyKey); }; - return Ember.computed.map(dependentKey + '.@each.' + propertyKey, callback); -}; - -/** - @method computed.mapProperty - @for Ember - @deprecated Use `Ember.computed.mapBy` instead - @param dependentKey - @param propertyKey -*/ -Ember.computed.mapProperty = Ember.computed.mapBy; - -/** - Filters the array by the callback. - - The callback method you provide should have the following signature. - `item` is the current item in the iteration. - - ```javascript - function(item); - ``` - - ```javascript - App.Hamster = Ember.Object.extend({ - remainingChores: Ember.computed.filter('chores', function(chore) { - return !chore.done; - }) - }); - - var hamster = App.Hamster.create({chores: [ - {name: 'cook', done: true}, - {name: 'clean', done: true}, - {name: 'write more unit tests', done: false} - ]}); - hamster.get('remainingChores'); // [{name: 'write more unit tests', done: false}] - ``` - - @method computed.filter - @for Ember - @param {String} dependentKey - @param {Function} callback - @return {Ember.ComputedProperty} the filtered array -*/ -Ember.computed.filter = function(dependentKey, callback) { - var options = { - initialize: function (array, changeMeta, instanceMeta) { - instanceMeta.filteredArrayIndexes = new Ember.SubArray(); - }, - - addedItem: function(array, item, changeMeta, instanceMeta) { - var match = !!callback.call(this, item), - filterIndex = instanceMeta.filteredArrayIndexes.addItem(changeMeta.index, match); - - if (match) { - array.insertAt(filterIndex, item); - } - - return array; - }, - - removedItem: function(array, item, changeMeta, instanceMeta) { - var filterIndex = instanceMeta.filteredArrayIndexes.removeItem(changeMeta.index); - - if (filterIndex > -1) { - array.removeAt(filterIndex); - } - - return array; - } - }; - - return Ember.arrayComputed(dependentKey, options); -}; - -/** - Filters the array by the property and value - - ```javascript - App.Hamster = Ember.Object.extend({ - remainingChores: Ember.computed.filterBy('chores', 'done', false) - }); - - var hamster = App.Hamster.create({chores: [ - {name: 'cook', done: true}, - {name: 'clean', done: true}, - {name: 'write more unit tests', done: false} - ]}); - hamster.get('remainingChores'); // [{name: 'write more unit tests', done: false}] - ``` - - @method computed.filterBy - @for Ember - @param {String} dependentKey - @param {String} propertyKey - @param {*} value - @return {Ember.ComputedProperty} the filtered array -*/ -Ember.computed.filterBy = function(dependentKey, propertyKey, value) { - var callback; - - if (arguments.length === 2) { - callback = function(item) { - return get(item, propertyKey); - }; - } else { - callback = function(item) { - return get(item, propertyKey) === value; - }; - } - - return Ember.computed.filter(dependentKey + '.@each.' + propertyKey, callback); -}; - -/** - @method computed.filterProperty - @for Ember - @param dependentKey - @param propertyKey - @param value - @deprecated Use `Ember.computed.filterBy` instead -*/ -Ember.computed.filterProperty = Ember.computed.filterBy; - -/** - A computed property which returns a new array with all the unique - elements from one or more dependent arrays. - - Example - - ```javascript - App.Hamster = Ember.Object.extend({ - uniqueFruits: Ember.computed.uniq('fruits') - }); - - var hamster = App.Hamster.create({fruits: [ - 'banana', - 'grape', - 'kale', - 'banana' - ]}); - hamster.get('uniqueFruits'); // ['banana', 'grape', 'kale'] - ``` - - @method computed.uniq - @for Ember - @param {String} propertyKey* - @return {Ember.ComputedProperty} computes a new array with all the - unique elements from the dependent array -*/ -Ember.computed.uniq = function() { - var args = a_slice.call(arguments); - args.push({ - initialize: function(array, changeMeta, instanceMeta) { - instanceMeta.itemCounts = {}; - }, - - addedItem: function(array, item, changeMeta, instanceMeta) { - var guid = guidFor(item); - - if (!instanceMeta.itemCounts[guid]) { - instanceMeta.itemCounts[guid] = 1; - } else { - ++instanceMeta.itemCounts[guid]; - } - array.addObject(item); - return array; - }, - removedItem: function(array, item, _, instanceMeta) { - var guid = guidFor(item), - itemCounts = instanceMeta.itemCounts; - - if (--itemCounts[guid] === 0) { - array.removeObject(item); - } - return array; - } - }); - return Ember.arrayComputed.apply(null, args); -}; - -/** - Alias for [Ember.computed.uniq](/api/#method_computed_uniq). - - @method computed.union - @for Ember - @param {String} propertyKey* - @return {Ember.ComputedProperty} computes a new array with all the - unique elements from the dependent array -*/ -Ember.computed.union = Ember.computed.uniq; - -/** - A computed property which returns a new array with all the duplicated - elements from two or more dependent arrays. - - Example - - ```javascript - var obj = Ember.Object.createWithMixins({ - adaFriends: ['Charles Babbage', 'John Hobhouse', 'William King', 'Mary Somerville'], - charlesFriends: ['William King', 'Mary Somerville', 'Ada Lovelace', 'George Peacock'], - friendsInCommon: Ember.computed.intersect('adaFriends', 'charlesFriends') - }); - - obj.get('friendsInCommon'); // ['William King', 'Mary Somerville'] - ``` - - @method computed.intersect - @for Ember - @param {String} propertyKey* - @return {Ember.ComputedProperty} computes a new array with all the - duplicated elements from the dependent arrays -*/ -Ember.computed.intersect = function () { - var getDependentKeyGuids = function (changeMeta) { - return map(changeMeta.property._dependentKeys, function (dependentKey) { - return guidFor(dependentKey); - }); - }; - - var args = a_slice.call(arguments); - args.push({ - initialize: function (array, changeMeta, instanceMeta) { - instanceMeta.itemCounts = {}; - }, - - addedItem: function(array, item, changeMeta, instanceMeta) { - var itemGuid = guidFor(item), - dependentGuids = getDependentKeyGuids(changeMeta), - dependentGuid = guidFor(changeMeta.arrayChanged), - numberOfDependentArrays = changeMeta.property._dependentKeys.length, - itemCounts = instanceMeta.itemCounts; - - if (!itemCounts[itemGuid]) { itemCounts[itemGuid] = {}; } - if (itemCounts[itemGuid][dependentGuid] === undefined) { itemCounts[itemGuid][dependentGuid] = 0; } - - if (++itemCounts[itemGuid][dependentGuid] === 1 && - numberOfDependentArrays === Ember.keys(itemCounts[itemGuid]).length) { - - array.addObject(item); - } - return array; - }, - removedItem: function(array, item, changeMeta, instanceMeta) { - var itemGuid = guidFor(item), - dependentGuids = getDependentKeyGuids(changeMeta), - dependentGuid = guidFor(changeMeta.arrayChanged), - numberOfDependentArrays = changeMeta.property._dependentKeys.length, - numberOfArraysItemAppearsIn, - itemCounts = instanceMeta.itemCounts; - - if (itemCounts[itemGuid][dependentGuid] === undefined) { itemCounts[itemGuid][dependentGuid] = 0; } - if (--itemCounts[itemGuid][dependentGuid] === 0) { - delete itemCounts[itemGuid][dependentGuid]; - numberOfArraysItemAppearsIn = Ember.keys(itemCounts[itemGuid]).length; - - if (numberOfArraysItemAppearsIn === 0) { - delete itemCounts[itemGuid]; - } - array.removeObject(item); - } - return array; - } - }); - return Ember.arrayComputed.apply(null, args); -}; - -/** - A computed property which returns a new array with all the - properties from the first dependent array that are not in the second - dependent array. - - Example - - ```javascript - App.Hamster = Ember.Object.extend({ - likes: ['banana', 'grape', 'kale'], - wants: Ember.computed.setDiff('likes', 'fruits') - }); - - var hamster = App.Hamster.create({fruits: [ - 'grape', - 'kale', - ]}); - hamster.get('wants'); // ['banana'] - ``` - - @method computed.setDiff - @for Ember - @param {String} setAProperty - @param {String} setBProperty - @return {Ember.ComputedProperty} computes a new array with all the - items from the first dependent array that are not in the second - dependent array -*/ -Ember.computed.setDiff = function (setAProperty, setBProperty) { - if (arguments.length !== 2) { - throw new Ember.Error("setDiff requires exactly two dependent arrays."); - } - return Ember.arrayComputed(setAProperty, setBProperty, { - addedItem: function (array, item, changeMeta, instanceMeta) { - var setA = get(this, setAProperty), - setB = get(this, setBProperty); - - if (changeMeta.arrayChanged === setA) { - if (!setB.contains(item)) { - array.addObject(item); - } - } else { - array.removeObject(item); - } - return array; - }, - - removedItem: function (array, item, changeMeta, instanceMeta) { - var setA = get(this, setAProperty), - setB = get(this, setBProperty); - - if (changeMeta.arrayChanged === setB) { - if (setA.contains(item)) { - array.addObject(item); - } - } else { - array.removeObject(item); - } - return array; - } - }); -}; - -function binarySearch(array, item, low, high) { - var mid, midItem, res, guidMid, guidItem; - - if (arguments.length < 4) { high = get(array, 'length'); } - if (arguments.length < 3) { low = 0; } - - if (low === high) { - return low; - } - - mid = low + Math.floor((high - low) / 2); - midItem = array.objectAt(mid); - - guidMid = _guidFor(midItem); - guidItem = _guidFor(item); - - if (guidMid === guidItem) { - return mid; - } - - res = this.order(midItem, item); - if (res === 0) { - res = guidMid < guidItem ? -1 : 1; - } - - - if (res < 0) { - return this.binarySearch(array, item, mid+1, high); - } else if (res > 0) { - return this.binarySearch(array, item, low, mid); - } - - return mid; - - function _guidFor(item) { - if (SearchProxy.detectInstance(item)) { - return guidFor(get(item, 'content')); - } - return guidFor(item); - } -} - - -SearchProxy = Ember.ObjectProxy.extend(); - -/** - A computed property which returns a new array with all the - properties from the first dependent array sorted based on a property - or sort function. - - The callback method you provide should have the following signature: - - ```javascript - function(itemA, itemB); - ``` - - - `itemA` the first item to compare. - - `itemB` the second item to compare. - - This function should return negative number (e.g. `-1`) when `itemA` should come before - `itemB`. It should return positive number (e.g. `1`) when `itemA` should come after - `itemB`. If the `itemA` and `itemB` are equal this function should return `0`. - - Therefore, if this function is comparing some numeric values, simple `itemA - itemB` or - `itemA.get( 'foo' ) - itemB.get( 'foo' )` can be used instead of series of `if`. - - Example - - ```javascript - var ToDoList = Ember.Object.extend({ - todosSorting: ['name'], - sortedTodos: Ember.computed.sort('todos', 'todosSorting'), - priorityTodos: Ember.computed.sort('todos', function(a, b){ - if (a.priority > b.priority) { - return 1; - } else if (a.priority < b.priority) { - return -1; - } - return 0; - }), - }); - var todoList = ToDoList.create({todos: [ - {name: 'Unit Test', priority: 2}, - {name: 'Documentation', priority: 3}, - {name: 'Release', priority: 1} - ]}); - - todoList.get('sortedTodos'); // [{name:'Documentation', priority:3}, {name:'Release', priority:1}, {name:'Unit Test', priority:2}] - todoList.get('priorityTodos'); // [{name:'Release', priority:1}, {name:'Unit Test', priority:2}, {name:'Documentation', priority:3}] - ``` - - @method computed.sort - @for Ember - @param {String} dependentKey - @param {String or Function} sortDefinition a dependent key to an - array of sort properties or a function to use when sorting - @return {Ember.ComputedProperty} computes a new sorted array based - on the sort property array or callback function -*/ -Ember.computed.sort = function (itemsKey, sortDefinition) { - Ember.assert("Ember.computed.sort requires two arguments: an array key to sort and either a sort properties key or sort function", arguments.length === 2); - - var initFn, sortPropertiesKey; - - if (typeof sortDefinition === 'function') { - initFn = function (array, changeMeta, instanceMeta) { - instanceMeta.order = sortDefinition; - instanceMeta.binarySearch = binarySearch; - }; - } else { - sortPropertiesKey = sortDefinition; - initFn = function (array, changeMeta, instanceMeta) { - function setupSortProperties() { - var sortPropertyDefinitions = get(this, sortPropertiesKey), - sortProperty, - sortProperties = instanceMeta.sortProperties = [], - sortPropertyAscending = instanceMeta.sortPropertyAscending = {}, - idx, - asc; - - Ember.assert("Cannot sort: '" + sortPropertiesKey + "' is not an array.", Ember.isArray(sortPropertyDefinitions)); - - changeMeta.property.clearItemPropertyKeys(itemsKey); - - forEach(sortPropertyDefinitions, function (sortPropertyDefinition) { - if ((idx = sortPropertyDefinition.indexOf(':')) !== -1) { - sortProperty = sortPropertyDefinition.substring(0, idx); - asc = sortPropertyDefinition.substring(idx+1).toLowerCase() !== 'desc'; - } else { - sortProperty = sortPropertyDefinition; - asc = true; - } - - sortProperties.push(sortProperty); - sortPropertyAscending[sortProperty] = asc; - changeMeta.property.itemPropertyKey(itemsKey, sortProperty); - }); - - sortPropertyDefinitions.addObserver('@each', this, updateSortPropertiesOnce); - } - - function updateSortPropertiesOnce() { - Ember.run.once(this, updateSortProperties, changeMeta.propertyName); - } - - function updateSortProperties(propertyName) { - setupSortProperties.call(this); - changeMeta.property.recomputeOnce.call(this, propertyName); - } - - Ember.addObserver(this, sortPropertiesKey, updateSortPropertiesOnce); - - setupSortProperties.call(this); - - - instanceMeta.order = function (itemA, itemB) { - var isProxy = itemB instanceof SearchProxy, - sortProperty, result, asc; - - for (var i = 0; i < this.sortProperties.length; ++i) { - sortProperty = this.sortProperties[i]; - result = Ember.compare(get(itemA, sortProperty), isProxy ? itemB[sortProperty] : get(itemB, sortProperty)); - - if (result !== 0) { - asc = this.sortPropertyAscending[sortProperty]; - return asc ? result : (-1 * result); - } - } - - return 0; - }; - - instanceMeta.binarySearch = binarySearch; - }; - } - - return Ember.arrayComputed(itemsKey, { - initialize: initFn, - - addedItem: function (array, item, changeMeta, instanceMeta) { - var index = instanceMeta.binarySearch(array, item); - array.insertAt(index, item); - return array; - }, - - removedItem: function (array, item, changeMeta, instanceMeta) { - var proxyProperties, index, searchItem; - - if (changeMeta.previousValues) { - proxyProperties = merge({ content: item }, changeMeta.previousValues); - - searchItem = SearchProxy.create(proxyProperties); - } else { - searchItem = item; - } - - index = instanceMeta.binarySearch(array, searchItem); - array.removeAt(index); - return array; - } - }); -}; - -})(); - - - -(function() { -Ember.RSVP = requireModule('rsvp'); - -Ember.RSVP.onerrorDefault = function(error) { - if (error instanceof Error) { - if (Ember.testing) { - if (Ember.Test && Ember.Test.adapter) { - Ember.Test.adapter.exception(error); - } else { - throw error; - } - } else { - Ember.Logger.error(error.stack); - Ember.assert(error, false); - } - } -}; - -Ember.RSVP.on('error', Ember.RSVP.onerrorDefault); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var a_slice = Array.prototype.slice; - -var expandProperties = Ember.expandProperties; - -if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { - - /** - The `property` extension of Javascript's Function prototype is available - when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is - `true`, which is the default. - - Computed properties allow you to treat a function like a property: - - ```javascript - MyApp.President = Ember.Object.extend({ - firstName: '', - lastName: '', - - fullName: function() { - return this.get('firstName') + ' ' + this.get('lastName'); - - // Call this flag to mark the function as a property - }.property() - }); - - var president = MyApp.President.create({ - firstName: "Barack", - lastName: "Obama" - }); - - president.get('fullName'); // "Barack Obama" - ``` - - Treating a function like a property is useful because they can work with - bindings, just like any other property. - - Many computed properties have dependencies on other properties. For - example, in the above example, the `fullName` property depends on - `firstName` and `lastName` to determine its value. You can tell Ember - about these dependencies like this: - - ```javascript - MyApp.President = Ember.Object.extend({ - firstName: '', - lastName: '', - - fullName: function() { - return this.get('firstName') + ' ' + this.get('lastName'); - - // Tell Ember.js that this computed property depends on firstName - // and lastName - }.property('firstName', 'lastName') - }); - ``` - - Make sure you list these dependencies so Ember knows when to update - bindings that connect to a computed property. Changing a dependency - will not immediately trigger an update of the computed property, but - will instead clear the cache so that it is updated when the next `get` - is called on the property. - - See [Ember.ComputedProperty](/api/classes/Ember.ComputedProperty.html), [Ember.computed](/api/#method_computed). - - @method property - @for Function - */ - Function.prototype.property = function() { - var ret = Ember.computed(this); - // ComputedProperty.prototype.property expands properties; no need for us to - // do so here. - return ret.property.apply(ret, arguments); - }; - - /** - The `observes` extension of Javascript's Function prototype is available - when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is - true, which is the default. - - You can observe property changes simply by adding the `observes` - call to the end of your method declarations in classes that you write. - For example: - - ```javascript - Ember.Object.extend({ - valueObserver: function() { - // Executes whenever the "value" property changes - }.observes('value') - }); - ``` - - In the future this method may become asynchronous. If you want to ensure - synchronous behavior, use `observesImmediately`. - - See `Ember.observer`. - - @method observes - @for Function - */ - Function.prototype.observes = function() { - var addWatchedProperty = function (obs) { watched.push(obs); }; - var watched = []; - - for (var i=0; i b` - - Default implementation raises an exception. - - @method compare - @param a {Object} the first object to compare - @param b {Object} the second object to compare - @return {Integer} the result of the comparison - */ - compare: Ember.required(Function) - -}); - - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - - - -var get = Ember.get, set = Ember.set; - -/** - Implements some standard methods for copying an object. Add this mixin to - any object you create that can create a copy of itself. This mixin is - added automatically to the built-in array. - - You should generally implement the `copy()` method to return a copy of the - receiver. - - Note that `frozenCopy()` will only work if you also implement - `Ember.Freezable`. - - @class Copyable - @namespace Ember - @since Ember 0.9 -*/ -Ember.Copyable = Ember.Mixin.create({ - - /** - Override to return a copy of the receiver. Default implementation raises - an exception. - - @method copy - @param {Boolean} deep if `true`, a deep copy of the object should be made - @return {Object} copy of receiver - */ - copy: Ember.required(Function), - - /** - If the object implements `Ember.Freezable`, then this will return a new - copy if the object is not frozen and the receiver if the object is frozen. - - Raises an exception if you try to call this method on a object that does - not support freezing. - - You should use this method whenever you want a copy of a freezable object - since a freezable object can simply return itself without actually - consuming more memory. - - @method frozenCopy - @return {Object} copy of receiver or receiver - */ - frozenCopy: function() { - if (Ember.Freezable && Ember.Freezable.detect(this)) { - return get(this, 'isFrozen') ? this : this.copy().freeze(); - } else { - throw new Ember.Error(Ember.String.fmt("%@ does not support freezing", [this])); - } - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - - -var get = Ember.get, set = Ember.set; - -/** - The `Ember.Freezable` mixin implements some basic methods for marking an - object as frozen. Once an object is frozen it should be read only. No changes - may be made the internal state of the object. - - ## Enforcement - - To fully support freezing in your subclass, you must include this mixin and - override any method that might alter any property on the object to instead - raise an exception. You can check the state of an object by checking the - `isFrozen` property. - - Although future versions of JavaScript may support language-level freezing - object objects, that is not the case today. Even if an object is freezable, - it is still technically possible to modify the object, even though it could - break other parts of your application that do not expect a frozen object to - change. It is, therefore, very important that you always respect the - `isFrozen` property on all freezable objects. - - ## Example Usage - - The example below shows a simple object that implement the `Ember.Freezable` - protocol. - - ```javascript - Contact = Ember.Object.extend(Ember.Freezable, { - firstName: null, - lastName: null, - - // swaps the names - swapNames: function() { - if (this.get('isFrozen')) throw Ember.FROZEN_ERROR; - var tmp = this.get('firstName'); - this.set('firstName', this.get('lastName')); - this.set('lastName', tmp); - return this; - } - - }); - - c = Contact.create({ firstName: "John", lastName: "Doe" }); - c.swapNames(); // returns c - c.freeze(); - c.swapNames(); // EXCEPTION - ``` - - ## Copying - - Usually the `Ember.Freezable` protocol is implemented in cooperation with the - `Ember.Copyable` protocol, which defines a `frozenCopy()` method that will - return a frozen object, if the object implements this method as well. - - @class Freezable - @namespace Ember - @since Ember 0.9 -*/ -Ember.Freezable = Ember.Mixin.create({ - - /** - Set to `true` when the object is frozen. Use this property to detect - whether your object is frozen or not. - - @property isFrozen - @type Boolean - */ - isFrozen: false, - - /** - Freezes the object. Once this method has been called the object should - no longer allow any properties to be edited. - - @method freeze - @return {Object} receiver - */ - freeze: function() { - if (get(this, 'isFrozen')) return this; - set(this, 'isFrozen', true); - return this; - } - -}); - -Ember.FROZEN_ERROR = "Frozen object cannot be modified."; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var forEach = Ember.EnumerableUtils.forEach; - -/** - This mixin defines the API for modifying generic enumerables. These methods - can be applied to an object regardless of whether it is ordered or - unordered. - - Note that an Enumerable can change even if it does not implement this mixin. - For example, a MappedEnumerable cannot be directly modified but if its - underlying enumerable changes, it will change also. - - ## Adding Objects - - To add an object to an enumerable, use the `addObject()` method. This - method will only add the object to the enumerable if the object is not - already present and is of a type supported by the enumerable. - - ```javascript - set.addObject(contact); - ``` - - ## Removing Objects - - To remove an object from an enumerable, use the `removeObject()` method. This - will only remove the object if it is present in the enumerable, otherwise - this method has no effect. - - ```javascript - set.removeObject(contact); - ``` - - ## Implementing In Your Own Code - - If you are implementing an object and want to support this API, just include - this mixin in your class and implement the required methods. In your unit - tests, be sure to apply the Ember.MutableEnumerableTests to your object. - - @class MutableEnumerable - @namespace Ember - @uses Ember.Enumerable -*/ -Ember.MutableEnumerable = Ember.Mixin.create(Ember.Enumerable, { - - /** - __Required.__ You must implement this method to apply this mixin. - - Attempts to add the passed object to the receiver if the object is not - already present in the collection. If the object is present, this method - has no effect. - - If the passed object is of a type not supported by the receiver, - then this method should raise an exception. - - @method addObject - @param {Object} object The object to add to the enumerable. - @return {Object} the passed object - */ - addObject: Ember.required(Function), - - /** - Adds each object in the passed enumerable to the receiver. - - @method addObjects - @param {Ember.Enumerable} objects the objects to add. - @return {Object} receiver - */ - addObjects: function(objects) { - Ember.beginPropertyChanges(this); - forEach(objects, function(obj) { this.addObject(obj); }, this); - Ember.endPropertyChanges(this); - return this; - }, - - /** - __Required.__ You must implement this method to apply this mixin. - - Attempts to remove the passed object from the receiver collection if the - object is present in the collection. If the object is not present, - this method has no effect. - - If the passed object is of a type not supported by the receiver, - then this method should raise an exception. - - @method removeObject - @param {Object} object The object to remove from the enumerable. - @return {Object} the passed object - */ - removeObject: Ember.required(Function), - - - /** - Removes each object in the passed enumerable from the receiver. - - @method removeObjects - @param {Ember.Enumerable} objects the objects to remove - @return {Object} receiver - */ - removeObjects: function(objects) { - Ember.beginPropertyChanges(this); - forEach(objects, function(obj) { this.removeObject(obj); }, this); - Ember.endPropertyChanges(this); - return this; - } - -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ -// .......................................................... -// CONSTANTS -// - -var OUT_OF_RANGE_EXCEPTION = "Index out of range" ; -var EMPTY = []; - -// .......................................................... -// HELPERS -// - -var get = Ember.get, set = Ember.set; - -/** - This mixin defines the API for modifying array-like objects. These methods - can be applied only to a collection that keeps its items in an ordered set. - It builds upon the Array mixin and adds methods to modify the array. - Concrete implementations of this class include ArrayProxy and ArrayController. - - It is important to use the methods in this class to modify arrays so that - changes are observable. This allows the binding system in Ember to function - correctly. - - Note that an Array can change even if it does not implement this mixin. - For example, one might implement a SparseArray that cannot be directly - modified, but if its underlying enumerable changes, it will change also. - - @class MutableArray - @namespace Ember - @uses Ember.Array - @uses Ember.MutableEnumerable -*/ -Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable, { - - /** - __Required.__ You must implement this method to apply this mixin. - - This is one of the primitives you must implement to support `Ember.Array`. - You should replace amt objects started at idx with the objects in the - passed array. You should also call `this.enumerableContentDidChange()` - - @method replace - @param {Number} idx Starting index in the array to replace. If - idx >= length, then append to the end of the array. - @param {Number} amt Number of elements that should be removed from - the array, starting at *idx*. - @param {Array} objects An array of zero or more objects that should be - inserted into the array at *idx* - */ - replace: Ember.required(), - - /** - Remove all elements from self. This is useful if you - want to reuse an existing array without having to recreate it. - - ```javascript - var colors = ["red", "green", "blue"]; - color.length(); // 3 - colors.clear(); // [] - colors.length(); // 0 - ``` - - @method clear - @return {Ember.Array} An empty Array. - */ - clear: function () { - var len = get(this, 'length'); - if (len === 0) return this; - this.replace(0, len, EMPTY); - return this; - }, - - /** - This will use the primitive `replace()` method to insert an object at the - specified index. - - ```javascript - var colors = ["red", "green", "blue"]; - colors.insertAt(2, "yellow"); // ["red", "green", "yellow", "blue"] - colors.insertAt(5, "orange"); // Error: Index out of range - ``` - - @method insertAt - @param {Number} idx index of insert the object at. - @param {Object} object object to insert - @return this - */ - insertAt: function(idx, object) { - if (idx > get(this, 'length')) throw new Ember.Error(OUT_OF_RANGE_EXCEPTION) ; - this.replace(idx, 0, [object]) ; - return this ; - }, - - /** - Remove an object at the specified index using the `replace()` primitive - method. You can pass either a single index, or a start and a length. - - If you pass a start and length that is beyond the - length this method will throw an `OUT_OF_RANGE_EXCEPTION`. - - ```javascript - var colors = ["red", "green", "blue", "yellow", "orange"]; - colors.removeAt(0); // ["green", "blue", "yellow", "orange"] - colors.removeAt(2, 2); // ["green", "blue"] - colors.removeAt(4, 2); // Error: Index out of range - ``` - - @method removeAt - @param {Number} start index, start of range - @param {Number} len length of passing range - @return {Object} receiver - */ - removeAt: function(start, len) { - if ('number' === typeof start) { - - if ((start < 0) || (start >= get(this, 'length'))) { - throw new Ember.Error(OUT_OF_RANGE_EXCEPTION); - } - - // fast case - if (len === undefined) len = 1; - this.replace(start, len, EMPTY); - } - - return this ; - }, - - /** - Push the object onto the end of the array. Works just like `push()` but it - is KVO-compliant. - - ```javascript - var colors = ["red", "green"]; - colors.pushObject("black"); // ["red", "green", "black"] - colors.pushObject(["yellow"]); // ["red", "green", ["yellow"]] - ``` - - @method pushObject - @param {*} obj object to push - @return The same obj passed as param - */ - pushObject: function(obj) { - this.insertAt(get(this, 'length'), obj) ; - return obj; - }, - - /** - Add the objects in the passed numerable to the end of the array. Defers - notifying observers of the change until all objects are added. - - ```javascript - var colors = ["red"]; - colors.pushObjects(["yellow", "orange"]); // ["red", "yellow", "orange"] - ``` - - @method pushObjects - @param {Ember.Enumerable} objects the objects to add - @return {Ember.Array} receiver - */ - pushObjects: function(objects) { - if (!(Ember.Enumerable.detect(objects) || Ember.isArray(objects))) { - throw new TypeError("Must pass Ember.Enumerable to Ember.MutableArray#pushObjects"); - } - this.replace(get(this, 'length'), 0, objects); - return this; - }, - - /** - Pop object from array or nil if none are left. Works just like `pop()` but - it is KVO-compliant. - - ```javascript - var colors = ["red", "green", "blue"]; - colors.popObject(); // "blue" - console.log(colors); // ["red", "green"] - ``` - - @method popObject - @return object - */ - popObject: function() { - var len = get(this, 'length') ; - if (len === 0) return null ; - - var ret = this.objectAt(len-1) ; - this.removeAt(len-1, 1) ; - return ret ; - }, - - /** - Shift an object from start of array or nil if none are left. Works just - like `shift()` but it is KVO-compliant. - - ```javascript - var colors = ["red", "green", "blue"]; - colors.shiftObject(); // "red" - console.log(colors); // ["green", "blue"] - ``` - - @method shiftObject - @return object - */ - shiftObject: function() { - if (get(this, 'length') === 0) return null ; - var ret = this.objectAt(0) ; - this.removeAt(0) ; - return ret ; - }, - - /** - Unshift an object to start of array. Works just like `unshift()` but it is - KVO-compliant. - - ```javascript - var colors = ["red"]; - colors.unshiftObject("yellow"); // ["yellow", "red"] - colors.unshiftObject(["black"]); // [["black"], "yellow", "red"] - ``` - - @method unshiftObject - @param {*} obj object to unshift - @return The same obj passed as param - */ - unshiftObject: function(obj) { - this.insertAt(0, obj) ; - return obj ; - }, - - /** - Adds the named objects to the beginning of the array. Defers notifying - observers until all objects have been added. - - ```javascript - var colors = ["red"]; - colors.unshiftObjects(["black", "white"]); // ["black", "white", "red"] - colors.unshiftObjects("yellow"); // Type Error: 'undefined' is not a function - ``` - - @method unshiftObjects - @param {Ember.Enumerable} objects the objects to add - @return {Ember.Array} receiver - */ - unshiftObjects: function(objects) { - this.replace(0, 0, objects); - return this; - }, - - /** - Reverse objects in the array. Works just like `reverse()` but it is - KVO-compliant. - - @method reverseObjects - @return {Ember.Array} receiver - */ - reverseObjects: function() { - var len = get(this, 'length'); - if (len === 0) return this; - var objects = this.toArray().reverse(); - this.replace(0, len, objects); - return this; - }, - - /** - Replace all the the receiver's content with content of the argument. - If argument is an empty array receiver will be cleared. - - ```javascript - var colors = ["red", "green", "blue"]; - colors.setObjects(["black", "white"]); // ["black", "white"] - colors.setObjects([]); // [] - ``` - - @method setObjects - @param {Ember.Array} objects array whose content will be used for replacing - the content of the receiver - @return {Ember.Array} receiver with the new content - */ - setObjects: function(objects) { - if (objects.length === 0) return this.clear(); - - var len = get(this, 'length'); - this.replace(0, len, objects); - return this; - }, - - // .......................................................... - // IMPLEMENT Ember.MutableEnumerable - // - - removeObject: function(obj) { - var loc = get(this, 'length') || 0; - while(--loc >= 0) { - var curObject = this.objectAt(loc) ; - if (curObject === obj) this.removeAt(loc) ; - } - return this ; - }, - - addObject: function(obj) { - if (!this.contains(obj)) this.pushObject(obj); - return this ; - } - -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var get = Ember.get, set = Ember.set; - -/** -`Ember.TargetActionSupport` is a mixin that can be included in a class -to add a `triggerAction` method with semantics similar to the Handlebars -`{{action}}` helper. In normal Ember usage, the `{{action}}` helper is -usually the best choice. This mixin is most often useful when you are -doing more complex event handling in View objects. - -See also `Ember.ViewTargetActionSupport`, which has -view-aware defaults for target and actionContext. - -@class TargetActionSupport -@namespace Ember -@extends Ember.Mixin -*/ -Ember.TargetActionSupport = Ember.Mixin.create({ - target: null, - action: null, - actionContext: null, - - targetObject: Ember.computed(function() { - var target = get(this, 'target'); - - if (Ember.typeOf(target) === "string") { - var value = get(this, target); - if (value === undefined) { value = get(Ember.lookup, target); } - return value; - } else { - return target; - } - }).property('target'), - - actionContextObject: Ember.computed(function() { - var actionContext = get(this, 'actionContext'); - - if (Ember.typeOf(actionContext) === "string") { - var value = get(this, actionContext); - if (value === undefined) { value = get(Ember.lookup, actionContext); } - return value; - } else { - return actionContext; - } - }).property('actionContext'), - - /** - Send an `action` with an `actionContext` to a `target`. The action, actionContext - and target will be retrieved from properties of the object. For example: - - ```javascript - App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, { - target: Ember.computed.alias('controller'), - action: 'save', - actionContext: Ember.computed.alias('context'), - click: function() { - this.triggerAction(); // Sends the `save` action, along with the current context - // to the current controller - } - }); - ``` - - The `target`, `action`, and `actionContext` can be provided as properties of - an optional object argument to `triggerAction` as well. - - ```javascript - App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, { - click: function() { - this.triggerAction({ - action: 'save', - target: this.get('controller'), - actionContext: this.get('context'), - }); // Sends the `save` action, along with the current context - // to the current controller - } - }); - ``` - - The `actionContext` defaults to the object you mixing `TargetActionSupport` into. - But `target` and `action` must be specified either as properties or with the argument - to `triggerAction`, or a combination: - - ```javascript - App.SaveButtonView = Ember.View.extend(Ember.TargetActionSupport, { - target: Ember.computed.alias('controller'), - click: function() { - this.triggerAction({ - action: 'save' - }); // Sends the `save` action, along with a reference to `this`, - // to the current controller - } - }); - ``` - - @method triggerAction - @param opts {Hash} (optional, with the optional keys action, target and/or actionContext) - @return {Boolean} true if the action was sent successfully and did not return false - */ - triggerAction: function(opts) { - opts = opts || {}; - var action = opts.action || get(this, 'action'), - target = opts.target || get(this, 'targetObject'), - actionContext = opts.actionContext; - - function args(options, actionName) { - var ret = []; - if (actionName) { ret.push(actionName); } - - return ret.concat(options); - } - - if (typeof actionContext === 'undefined') { - actionContext = get(this, 'actionContextObject') || this; - } - - if (target && action) { - var ret; - - if (target.send) { - ret = target.send.apply(target, args(actionContext, action)); - } else { - Ember.assert("The action '" + action + "' did not exist on " + target, typeof target[action] === 'function'); - ret = target[action].apply(target, args(actionContext)); - } - - if (ret !== false) ret = true; - - return ret; - } else { - return false; - } - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -/** - This mixin allows for Ember objects to subscribe to and emit events. - - ```javascript - App.Person = Ember.Object.extend(Ember.Evented, { - greet: function() { - // ... - this.trigger('greet'); - } - }); - - var person = App.Person.create(); - - person.on('greet', function() { - console.log('Our person has greeted'); - }); - - person.greet(); - - // outputs: 'Our person has greeted' - ``` - - You can also chain multiple event subscriptions: - - ```javascript - person.on('greet', function() { - console.log('Our person has greeted'); - }).one('greet', function() { - console.log('Offer one-time special'); - }).off('event', this, forgetThis); - ``` - - @class Evented - @namespace Ember - */ -Ember.Evented = Ember.Mixin.create({ - - /** - Subscribes to a named event with given function. - - ```javascript - person.on('didLoad', function() { - // fired once the person has loaded - }); - ``` - - An optional target can be passed in as the 2nd argument that will - be set as the "this" for the callback. This is a good way to give your - function access to the object triggering the event. When the target - parameter is used the callback becomes the third argument. - - @method on - @param {String} name The name of the event - @param {Object} [target] The "this" binding for the callback - @param {Function} method The callback to execute - @return this - */ - on: function(name, target, method) { - Ember.addListener(this, name, target, method); - return this; - }, - - /** - Subscribes a function to a named event and then cancels the subscription - after the first time the event is triggered. It is good to use ``one`` when - you only care about the first time an event has taken place. - - This function takes an optional 2nd argument that will become the "this" - value for the callback. If this argument is passed then the 3rd argument - becomes the function. - - @method one - @param {String} name The name of the event - @param {Object} [target] The "this" binding for the callback - @param {Function} method The callback to execute - @return this - */ - one: function(name, target, method) { - if (!method) { - method = target; - target = null; - } - - Ember.addListener(this, name, target, method, true); - return this; - }, - - /** - Triggers a named event for the object. Any additional arguments - will be passed as parameters to the functions that are subscribed to the - event. - - ```javascript - person.on('didEat', function(food) { - console.log('person ate some ' + food); - }); - - person.trigger('didEat', 'broccoli'); - - // outputs: person ate some broccoli - ``` - @method trigger - @param {String} name The name of the event - @param {Object...} args Optional arguments to pass on - */ - trigger: function(name) { - var args = [], i, l; - for (i = 1, l = arguments.length; i < l; i++) { - args.push(arguments[i]); - } - Ember.sendEvent(this, name, args); - }, - - /** - Cancels subscription for given name, target, and method. - - @method off - @param {String} name The name of the event - @param {Object} target The target of the subscription - @param {Function} method The function of the subscription - @return this - */ - off: function(name, target, method) { - Ember.removeListener(this, name, target, method); - return this; - }, - - /** - Checks to see if object has any subscriptions for named event. - - @method has - @param {String} name The name of the event - @return {Boolean} does the object have a subscription for event - */ - has: function(name) { - return Ember.hasListeners(this, name); - } -}); - -})(); - - - -(function() { -var RSVP = requireModule("rsvp"); - -if (Ember.FEATURES['ember-runtime-test-friendly-promises']) { - - var asyncStart = function() { - if (Ember.Test && Ember.Test.adapter) { - Ember.Test.adapter.asyncStart(); - } - }; - - var asyncEnd = function() { - if (Ember.Test && Ember.Test.adapter) { - Ember.Test.adapter.asyncEnd(); - } - }; - - RSVP.configure('async', function(callback, promise) { - var async = !Ember.run.currentRunLoop; - - if (Ember.testing && async) { asyncStart(); } - - Ember.run.backburner.schedule('actions', function(){ - if (Ember.testing && async) { asyncEnd(); } - callback(promise); - }); - }); -} else { - RSVP.configure('async', function(callback, promise) { - Ember.run.backburner.schedule('actions', function(){ - callback(promise); - }); - }); -} - -RSVP.Promise.prototype.fail = function(callback, label){ - Ember.deprecate('RSVP.Promise.fail has been renamed as RSVP.Promise.catch'); - return this['catch'](callback, label); -}; - -/** -@module ember -@submodule ember-runtime -*/ - -var get = Ember.get; - -/** - @class Deferred - @namespace Ember - */ -Ember.DeferredMixin = Ember.Mixin.create({ - /** - Add handlers to be called when the Deferred object is resolved or rejected. - - @method then - @param {Function} resolve a callback function to be called when done - @param {Function} reject a callback function to be called when failed - */ - then: function(resolve, reject, label) { - var deferred, promise, entity; - - entity = this; - deferred = get(this, '_deferred'); - promise = deferred.promise; - - function fulfillmentHandler(fulfillment) { - if (fulfillment === promise) { - return resolve(entity); - } else { - return resolve(fulfillment); - } - } - - return promise.then(resolve && fulfillmentHandler, reject, label); - }, - - /** - Resolve a Deferred object and call any `doneCallbacks` with the given args. - - @method resolve - */ - resolve: function(value) { - var deferred, promise; - - deferred = get(this, '_deferred'); - promise = deferred.promise; - - if (value === this) { - deferred.resolve(promise); - } else { - deferred.resolve(value); - } - }, - - /** - Reject a Deferred object and call any `failCallbacks` with the given args. - - @method reject - */ - reject: function(value) { - get(this, '_deferred').reject(value); - }, - - _deferred: Ember.computed(function() { - return RSVP.defer('Ember: DeferredMixin - ' + this); - }) -}); - - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var get = Ember.get, typeOf = Ember.typeOf; - -/** - The `Ember.ActionHandler` mixin implements support for moving an `actions` - property to an `_actions` property at extend time, and adding `_actions` - to the object's mergedProperties list. - - `Ember.ActionHandler` is available on some familiar classes including - `Ember.Route`, `Ember.View`, `Ember.Component`, and controllers such as - `Ember.Controller` and `Ember.ObjectController`. - (Internally the mixin is used by `Ember.CoreView`, `Ember.ControllerMixin`, - and `Ember.Route` and available to the above classes through - inheritance.) - - @class ActionHandler - @namespace Ember -*/ -Ember.ActionHandler = Ember.Mixin.create({ - mergedProperties: ['_actions'], - - /** - The collection of functions, keyed by name, available on this - `ActionHandler` as action targets. - - These functions will be invoked when a matching `{{action}}` is triggered - from within a template and the application's current route is this route. - - Actions can also be invoked from other parts of your application - via `ActionHandler#send`. - - The `actions` hash will inherit action handlers from - the `actions` hash defined on extended parent classes - or mixins rather than just replace the entire hash, e.g.: - - ```js - App.CanDisplayBanner = Ember.Mixin.create({ - actions: { - displayBanner: function(msg) { - // ... - } - } - }); - - App.WelcomeRoute = Ember.Route.extend(App.CanDisplayBanner, { - actions: { - playMusic: function() { - // ... - } - } - }); - - // `WelcomeRoute`, when active, will be able to respond - // to both actions, since the actions hash is merged rather - // then replaced when extending mixins / parent classes. - this.send('displayBanner'); - this.send('playMusic'); - ``` - - Within a Controller, Route, View or Component's action handler, - the value of the `this` context is the Controller, Route, View or - Component object: - - ```js - App.SongRoute = Ember.Route.extend({ - actions: { - myAction: function() { - this.controllerFor("song"); - this.transitionTo("other.route"); - ... - } - } - }); - ``` - - It is also possible to call `this._super()` from within an - action handler if it overrides a handler defined on a parent - class or mixin: - - Take for example the following routes: - - ```js - App.DebugRoute = Ember.Mixin.create({ - actions: { - debugRouteInformation: function() { - console.debug("trololo"); - } - } - }); - - App.AnnoyingDebugRoute = Ember.Route.extend(App.DebugRoute, { - actions: { - debugRouteInformation: function() { - // also call the debugRouteInformation of mixed in App.DebugRoute - this._super(); - - // show additional annoyance - window.alert(...); - } - } - }); - ``` - - ## Bubbling - - By default, an action will stop bubbling once a handler defined - on the `actions` hash handles it. To continue bubbling the action, - you must return `true` from the handler: - - ```js - App.Router.map(function() { - this.resource("album", function() { - this.route("song"); - }); - }); - - App.AlbumRoute = Ember.Route.extend({ - actions: { - startPlaying: function() { - } - } - }); - - App.AlbumSongRoute = Ember.Route.extend({ - actions: { - startPlaying: function() { - // ... - - if (actionShouldAlsoBeTriggeredOnParentRoute) { - return true; - } - } - } - }); - ``` - - @property actions - @type Hash - @default null - */ - - /** - Moves `actions` to `_actions` at extend time. Note that this currently - modifies the mixin themselves, which is technically dubious but - is practically of little consequence. This may change in the future. - - @private - @method willMergeMixin - */ - willMergeMixin: function(props) { - var hashName; - - if (!props._actions) { - Ember.assert("'actions' should not be a function", typeof(props.actions) !== 'function'); - - if (typeOf(props.actions) === 'object') { - hashName = 'actions'; - } else if (typeOf(props.events) === 'object') { - Ember.deprecate('Action handlers contained in an `events` object are deprecated in favor of putting them in an `actions` object', false); - hashName = 'events'; - } - - if (hashName) { - props._actions = Ember.merge(props._actions || {}, props[hashName]); - } - - delete props[hashName]; - } - }, - - /** - Triggers a named action on the `ActionHandler`. Any parameters - supplied after the `actionName` string will be passed as arguments - to the action target function. - - If the `ActionHandler` has its `target` property set, actions may - bubble to the `target`. Bubbling happens when an `actionName` can - not be found in the `ActionHandler`'s `actions` hash or if the - action target function returns `true`. - - Example - - ```js - App.WelcomeRoute = Ember.Route.extend({ - actions: { - playTheme: function() { - this.send('playMusic', 'theme.mp3'); - }, - playMusic: function(track) { - // ... - } - } - }); - ``` - - @method send - @param {String} actionName The action to trigger - @param {*} context a context to send with the action - */ - send: function(actionName) { - var args = [].slice.call(arguments, 1), target; - - if (this._actions && this._actions[actionName]) { - if (this._actions[actionName].apply(this, args) === true) { - // handler returned true, so this action will bubble - } else { - return; - } - } else if (!Ember.FEATURES.isEnabled('ember-routing-drop-deprecated-action-style') && this.deprecatedSend && this.deprecatedSendHandles && this.deprecatedSendHandles(actionName)) { - Ember.warn("The current default is deprecated but will prefer to handle actions directly on the controller instead of a similarly named action in the actions hash. To turn off this deprecated feature set: Ember.FEATURES['ember-routing-drop-deprecated-action-style'] = true"); - if (this.deprecatedSend.apply(this, [].slice.call(arguments)) === true) { - // handler return true, so this action will bubble - } else { - return; - } - } - - if (target = get(this, 'target')) { - Ember.assert("The `target` for " + this + " (" + target + ") does not have a `send` method", typeof target.send === 'function'); - target.send.apply(target, arguments); - } - } - -}); - -})(); - - - -(function() { -var set = Ember.set, get = Ember.get, - not = Ember.computed.not, - or = Ember.computed.or; - -/** - @module ember - @submodule ember-runtime - */ - -function tap(proxy, promise) { - set(proxy, 'isFulfilled', false); - set(proxy, 'isRejected', false); - - return promise.then(function(value) { - set(proxy, 'isFulfilled', true); - set(proxy, 'content', value); - return value; - }, function(reason) { - set(proxy, 'isRejected', true); - set(proxy, 'reason', reason); - throw reason; - }, "Ember: PromiseProxy"); -} - -/** - A low level mixin making ObjectProxy, ObjectController or ArrayController's promise aware. - - ```javascript - var ObjectPromiseController = Ember.ObjectController.extend(Ember.PromiseProxyMixin); - - var controller = ObjectPromiseController.create({ - promise: $.getJSON('/some/remote/data.json') - }); - - controller.then(function(json){ - // the json - }, function(reason) { - // the reason why you have no json - }); - ``` - - the controller has bindable attributes which - track the promises life cycle - - ```javascript - controller.get('isPending') //=> true - controller.get('isSettled') //=> false - controller.get('isRejected') //=> false - controller.get('isFulfilled') //=> false - ``` - - When the the $.getJSON completes, and the promise is fulfilled - with json, the life cycle attributes will update accordingly. - - ```javascript - controller.get('isPending') //=> false - controller.get('isSettled') //=> true - controller.get('isRejected') //=> false - controller.get('isFulfilled') //=> true - ``` - - As the controller is an ObjectController, and the json now its content, - all the json properties will be available directly from the controller. - - ```javascript - // Assuming the following json: - { - firstName: 'Stefan', - lastName: 'Penner' - } - - // both properties will accessible on the controller - controller.get('firstName') //=> 'Stefan' - controller.get('lastName') //=> 'Penner' - ``` - - If the controller is backing a template, the attributes are - bindable from within that template - - ```handlebars - {{#if isPending}} - loading... - {{else}} - firstName: {{firstName}} - lastName: {{lastName}} - {{/if}} - ``` - @class Ember.PromiseProxyMixin -*/ -Ember.PromiseProxyMixin = Ember.Mixin.create({ - /** - If the proxied promise is rejected this will contain the reason - provided. - - @property reason - @default null - */ - reason: null, - - /** - Once the proxied promise has settled this will become `false`. - - @property isPending - @default true - */ - isPending: not('isSettled').readOnly(), - - /** - Once the proxied promise has settled this will become `true`. - - @property isSettled - @default false - */ - isSettled: or('isRejected', 'isFulfilled').readOnly(), - - /** - Will become `true` if the proxied promise is rejected. - - @property isRejected - @default false - */ - isRejected: false, - - /** - Will become `true` if the proxied promise is fulfilled. - - @property isFullfilled - @default false - */ - isFulfilled: false, - - /** - The promise whose fulfillment value is being proxied by this object. - - This property must be specified upon creation, and should not be - changed once created. - - Example: - - ```javascript - Ember.ObjectController.extend(Ember.PromiseProxyMixin).create({ - promise: - }); - ``` - - @property promise - */ - promise: Ember.computed(function(key, promise) { - if (arguments.length === 2) { - return tap(this, promise); - } else { - throw new Ember.Error("PromiseProxy's promise must be set"); - } - }), - - /** - An alias to the proxied promise's `then`. - - See RSVP.Promise.then. - - @method then - @param {Function} callback - @return {RSVP.Promise} - */ - then: promiseAlias('then'), - - /** - An alias to the proxied promise's `catch`. - - See RSVP.Promise.catch. - - @method catch - @param {Function} callback - @return {RSVP.Promise} - */ - 'catch': promiseAlias('catch'), - - /** - An alias to the proxied promise's `finally`. - - See RSVP.Promise.finally. - - @method finally - @param {Function} callback - @return {RSVP.Promise} - */ - 'finally': promiseAlias('finally') - -}); - -function promiseAlias(name) { - return function () { - var promise = get(this, 'promise'); - return promise[name].apply(promise, arguments); - }; -} - -})(); - - - -(function() { - -})(); - - - -(function() { -var get = Ember.get, - forEach = Ember.EnumerableUtils.forEach, - RETAIN = 'r', - INSERT = 'i', - DELETE = 'd'; - -/** - An `Ember.TrackedArray` tracks array operations. It's useful when you want to - lazily compute the indexes of items in an array after they've been shifted by - subsequent operations. - - @class TrackedArray - @namespace Ember - @param {array} [items=[]] The array to be tracked. This is used just to get - the initial items for the starting state of retain:n. -*/ -Ember.TrackedArray = function (items) { - if (arguments.length < 1) { items = []; } - - var length = get(items, 'length'); - - if (length) { - this._operations = [new ArrayOperation(RETAIN, length, items)]; - } else { - this._operations = []; - } -}; - -Ember.TrackedArray.RETAIN = RETAIN; -Ember.TrackedArray.INSERT = INSERT; -Ember.TrackedArray.DELETE = DELETE; - -Ember.TrackedArray.prototype = { - - /** - Track that `newItems` were added to the tracked array at `index`. - - @method addItems - @param index - @param newItems - */ - addItems: function (index, newItems) { - var count = get(newItems, 'length'); - if (count < 1) { return; } - - var match = this._findArrayOperation(index), - arrayOperation = match.operation, - arrayOperationIndex = match.index, - arrayOperationRangeStart = match.rangeStart, - composeIndex, - splitIndex, - splitItems, - splitArrayOperation, - newArrayOperation; - - newArrayOperation = new ArrayOperation(INSERT, count, newItems); - - if (arrayOperation) { - if (!match.split) { - // insert left of arrayOperation - this._operations.splice(arrayOperationIndex, 0, newArrayOperation); - composeIndex = arrayOperationIndex; - } else { - this._split(arrayOperationIndex, index - arrayOperationRangeStart, newArrayOperation); - composeIndex = arrayOperationIndex + 1; - } - } else { - // insert at end - this._operations.push(newArrayOperation); - composeIndex = arrayOperationIndex; - } - - this._composeInsert(composeIndex); - }, - - /** - Track that `count` items were removed at `index`. - - @method removeItems - @param index - @param count - */ - removeItems: function (index, count) { - if (count < 1) { return; } - - var match = this._findArrayOperation(index), - arrayOperation = match.operation, - arrayOperationIndex = match.index, - arrayOperationRangeStart = match.rangeStart, - newArrayOperation, - composeIndex; - - newArrayOperation = new ArrayOperation(DELETE, count); - if (!match.split) { - // insert left of arrayOperation - this._operations.splice(arrayOperationIndex, 0, newArrayOperation); - composeIndex = arrayOperationIndex; - } else { - this._split(arrayOperationIndex, index - arrayOperationRangeStart, newArrayOperation); - composeIndex = arrayOperationIndex + 1; - } - - return this._composeDelete(composeIndex); - }, - - /** - Apply all operations, reducing them to retain:n, for `n`, the number of - items in the array. - - `callback` will be called for each operation and will be passed the following arguments: - - * {array} items The items for the given operation - * {number} offset The computed offset of the items, ie the index in the - array of the first item for this operation. - * {string} operation The type of the operation. One of - `Ember.TrackedArray.{RETAIN, DELETE, INSERT}` - - @method apply - @param {function} callback - */ - apply: function (callback) { - var items = [], - offset = 0; - - forEach(this._operations, function (arrayOperation) { - callback(arrayOperation.items, offset, arrayOperation.type); - - if (arrayOperation.type !== DELETE) { - offset += arrayOperation.count; - items = items.concat(arrayOperation.items); - } - }); - - this._operations = [new ArrayOperation(RETAIN, items.length, items)]; - }, - - /** - Return an `ArrayOperationMatch` for the operation that contains the item at `index`. - - @method _findArrayOperation - - @param {number} index the index of the item whose operation information - should be returned. - @private - */ - _findArrayOperation: function (index) { - var arrayOperationIndex, - len, - split = false, - arrayOperation, - arrayOperationRangeStart, - arrayOperationRangeEnd; - - // OPTIMIZE: we could search these faster if we kept a balanced tree. - // find leftmost arrayOperation to the right of `index` - for (arrayOperationIndex = arrayOperationRangeStart = 0, len = this._operations.length; arrayOperationIndex < len; ++arrayOperationIndex) { - arrayOperation = this._operations[arrayOperationIndex]; - - if (arrayOperation.type === DELETE) { continue; } - - arrayOperationRangeEnd = arrayOperationRangeStart + arrayOperation.count - 1; - - if (index === arrayOperationRangeStart) { - break; - } else if (index > arrayOperationRangeStart && index <= arrayOperationRangeEnd) { - split = true; - break; - } else { - arrayOperationRangeStart = arrayOperationRangeEnd + 1; - } - } - - return new ArrayOperationMatch(arrayOperation, arrayOperationIndex, split, arrayOperationRangeStart); - }, - - _split: function (arrayOperationIndex, splitIndex, newArrayOperation) { - var arrayOperation = this._operations[arrayOperationIndex], - splitItems = arrayOperation.items.slice(splitIndex), - splitArrayOperation = new ArrayOperation(arrayOperation.type, splitItems.length, splitItems); - - // truncate LHS - arrayOperation.count = splitIndex; - arrayOperation.items = arrayOperation.items.slice(0, splitIndex); - - this._operations.splice(arrayOperationIndex + 1, 0, newArrayOperation, splitArrayOperation); - }, - - // see SubArray for a better implementation. - _composeInsert: function (index) { - var newArrayOperation = this._operations[index], - leftArrayOperation = this._operations[index-1], // may be undefined - rightArrayOperation = this._operations[index+1], // may be undefined - leftOp = leftArrayOperation && leftArrayOperation.type, - rightOp = rightArrayOperation && rightArrayOperation.type; - - if (leftOp === INSERT) { - // merge left - leftArrayOperation.count += newArrayOperation.count; - leftArrayOperation.items = leftArrayOperation.items.concat(newArrayOperation.items); - - if (rightOp === INSERT) { - // also merge right (we have split an insert with an insert) - leftArrayOperation.count += rightArrayOperation.count; - leftArrayOperation.items = leftArrayOperation.items.concat(rightArrayOperation.items); - this._operations.splice(index, 2); - } else { - // only merge left - this._operations.splice(index, 1); - } - } else if (rightOp === INSERT) { - // merge right - newArrayOperation.count += rightArrayOperation.count; - newArrayOperation.items = newArrayOperation.items.concat(rightArrayOperation.items); - this._operations.splice(index + 1, 1); - } - }, - - _composeDelete: function (index) { - var arrayOperation = this._operations[index], - deletesToGo = arrayOperation.count, - leftArrayOperation = this._operations[index-1], // may be undefined - leftOp = leftArrayOperation && leftArrayOperation.type, - nextArrayOperation, - nextOp, - nextCount, - removeNewAndNextOp = false, - removedItems = []; - - if (leftOp === DELETE) { - arrayOperation = leftArrayOperation; - index -= 1; - } - - for (var i = index + 1; deletesToGo > 0; ++i) { - nextArrayOperation = this._operations[i]; - nextOp = nextArrayOperation.type; - nextCount = nextArrayOperation.count; - - if (nextOp === DELETE) { - arrayOperation.count += nextCount; - continue; - } - - if (nextCount > deletesToGo) { - // d:2 {r,i}:5 we reduce the retain or insert, but it stays - removedItems = removedItems.concat(nextArrayOperation.items.splice(0, deletesToGo)); - nextArrayOperation.count -= deletesToGo; - - // In the case where we truncate the last arrayOperation, we don't need to - // remove it; also the deletesToGo reduction is not the entirety of - // nextCount - i -= 1; - nextCount = deletesToGo; - - deletesToGo = 0; - } else { - if (nextCount === deletesToGo) { - // Handle edge case of d:2 i:2 in which case both operations go away - // during composition. - removeNewAndNextOp = true; - } - removedItems = removedItems.concat(nextArrayOperation.items); - deletesToGo -= nextCount; - } - - if (nextOp === INSERT) { - // d:2 i:3 will result in delete going away - arrayOperation.count -= nextCount; - } - } - - if (arrayOperation.count > 0) { - // compose our new delete with possibly several operations to the right of - // disparate types - this._operations.splice(index+1, i-1-index); - } else { - // The delete operation can go away; it has merely reduced some other - // operation, as in d:3 i:4; it may also have eliminated that operation, - // as in d:3 i:3. - this._operations.splice(index, removeNewAndNextOp ? 2 : 1); - } - - return removedItems; - }, - - toString: function () { - var str = ""; - forEach(this._operations, function (operation) { - str += " " + operation.type + ":" + operation.count; - }); - return str.substring(1); - } -}; - -/** - Internal data structure to represent an array operation. - - @method ArrayOperation - @private - @param {string} type The type of the operation. One of - `Ember.TrackedArray.{RETAIN, INSERT, DELETE}` - @param {number} count The number of items in this operation. - @param {array} items The items of the operation, if included. RETAIN and - INSERT include their items, DELETE does not. -*/ -function ArrayOperation (operation, count, items) { - this.type = operation; // RETAIN | INSERT | DELETE - this.count = count; - this.items = items; -} - -/** - Internal data structure used to include information when looking up operations - by item index. - - @method ArrayOperationMatch - @private - @param {ArrayOperation} operation - @param {number} index The index of `operation` in the array of operations. - @param {boolean} split Whether or not the item index searched for would - require a split for a new operation type. - @param {number} rangeStart The index of the first item in the operation, - with respect to the tracked array. The index of the last item can be computed - from `rangeStart` and `operation.count`. -*/ -function ArrayOperationMatch(operation, index, split, rangeStart) { - this.operation = operation; - this.index = index; - this.split = split; - this.rangeStart = rangeStart; -} - -})(); - - - -(function() { -var get = Ember.get, - forEach = Ember.EnumerableUtils.forEach, - RETAIN = 'r', - FILTER = 'f'; - -function Operation (type, count) { - this.type = type; - this.count = count; -} - -/** - An `Ember.SubArray` tracks an array in a way similar to, but more specialized - than, `Ember.TrackedArray`. It is useful for keeping track of the indexes of - items within a filtered array. - - @class SubArray - @namespace Ember -*/ -Ember.SubArray = function (length) { - if (arguments.length < 1) { length = 0; } - - if (length > 0) { - this._operations = [new Operation(RETAIN, length)]; - } else { - this._operations = []; - } -}; - -Ember.SubArray.prototype = { - /** - Track that an item was added to the tracked array. - - @method addItem - - @param {number} index The index of the item in the tracked array. - @param {boolean} match `true` iff the item is included in the subarray. - - @return {number} The index of the item in the subarray. - */ - addItem: function(index, match) { - var returnValue = -1, - itemType = match ? RETAIN : FILTER, - self = this; - - this._findOperation(index, function(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) { - var newOperation, splitOperation; - - if (itemType === operation.type) { - ++operation.count; - } else if (index === rangeStart) { - // insert to the left of `operation` - self._operations.splice(operationIndex, 0, new Operation(itemType, 1)); - } else { - newOperation = new Operation(itemType, 1); - splitOperation = new Operation(operation.type, rangeEnd - index + 1); - operation.count = index - rangeStart; - - self._operations.splice(operationIndex + 1, 0, newOperation, splitOperation); - } - - if (match) { - if (operation.type === RETAIN) { - returnValue = seenInSubArray + (index - rangeStart); - } else { - returnValue = seenInSubArray; - } - } - - self._composeAt(operationIndex); - }, function(seenInSubArray) { - self._operations.push(new Operation(itemType, 1)); - - if (match) { - returnValue = seenInSubArray; - } - - self._composeAt(self._operations.length-1); - }); - - return returnValue; - }, - - /** - Track that an item was removed from the tracked array. - - @method removeItem - - @param {number} index The index of the item in the tracked array. - - @return {number} The index of the item in the subarray, or `-1` if the item - was not in the subarray. - */ - removeItem: function(index) { - var returnValue = -1, - self = this; - - this._findOperation(index, function (operation, operationIndex, rangeStart, rangeEnd, seenInSubArray) { - if (operation.type === RETAIN) { - returnValue = seenInSubArray + (index - rangeStart); - } - - if (operation.count > 1) { - --operation.count; - } else { - self._operations.splice(operationIndex, 1); - self._composeAt(operationIndex); - } - }, function() { - throw new Ember.Error("Can't remove an item that has never been added."); - }); - - return returnValue; - }, - - - _findOperation: function (index, foundCallback, notFoundCallback) { - var operationIndex, - len, - operation, - rangeStart, - rangeEnd, - seenInSubArray = 0; - - // OPTIMIZE: change to balanced tree - // find leftmost operation to the right of `index` - for (operationIndex = rangeStart = 0, len = this._operations.length; operationIndex < len; rangeStart = rangeEnd + 1, ++operationIndex) { - operation = this._operations[operationIndex]; - rangeEnd = rangeStart + operation.count - 1; - - if (index >= rangeStart && index <= rangeEnd) { - foundCallback(operation, operationIndex, rangeStart, rangeEnd, seenInSubArray); - return; - } else if (operation.type === RETAIN) { - seenInSubArray += operation.count; - } - } - - notFoundCallback(seenInSubArray); - }, - - _composeAt: function(index) { - var op = this._operations[index], - otherOp; - - if (!op) { - // Composing out of bounds is a no-op, as when removing the last operation - // in the list. - return; - } - - if (index > 0) { - otherOp = this._operations[index-1]; - if (otherOp.type === op.type) { - op.count += otherOp.count; - this._operations.splice(index-1, 1); - --index; - } - } - - if (index < this._operations.length-1) { - otherOp = this._operations[index+1]; - if (otherOp.type === op.type) { - op.count += otherOp.count; - this._operations.splice(index+1, 1); - } - } - }, - - toString: function () { - var str = ""; - forEach(this._operations, function (operation) { - str += " " + operation.type + ":" + operation.count; - }); - return str.substring(1); - } -}; - -})(); - - - -(function() { -Ember.Container = requireModule('container')['default']; -Ember.Container.set = Ember.set; - -})(); - - - -(function() { -Ember.Application = Ember.Namespace.extend(); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var OUT_OF_RANGE_EXCEPTION = "Index out of range"; -var EMPTY = []; - -var get = Ember.get, set = Ember.set; - -/** - An ArrayProxy wraps any other object that implements `Ember.Array` and/or - `Ember.MutableArray,` forwarding all requests. This makes it very useful for - a number of binding use cases or other cases where being able to swap - out the underlying array is useful. - - A simple example of usage: - - ```javascript - var pets = ['dog', 'cat', 'fish']; - var ap = Ember.ArrayProxy.create({ content: Ember.A(pets) }); - - ap.get('firstObject'); // 'dog' - ap.set('content', ['amoeba', 'paramecium']); - ap.get('firstObject'); // 'amoeba' - ``` - - This class can also be useful as a layer to transform the contents of - an array, as they are accessed. This can be done by overriding - `objectAtContent`: - - ```javascript - var pets = ['dog', 'cat', 'fish']; - var ap = Ember.ArrayProxy.create({ - content: Ember.A(pets), - objectAtContent: function(idx) { - return this.get('content').objectAt(idx).toUpperCase(); - } - }); - - ap.get('firstObject'); // . 'DOG' - ``` - - @class ArrayProxy - @namespace Ember - @extends Ember.Object - @uses Ember.MutableArray -*/ -Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray, { - - /** - The content array. Must be an object that implements `Ember.Array` and/or - `Ember.MutableArray.` - - @property content - @type Ember.Array - */ - content: null, - - /** - The array that the proxy pretends to be. In the default `ArrayProxy` - implementation, this and `content` are the same. Subclasses of `ArrayProxy` - can override this property to provide things like sorting and filtering. - - @property arrangedContent - */ - arrangedContent: Ember.computed.alias('content'), - - /** - Should actually retrieve the object at the specified index from the - content. You can override this method in subclasses to transform the - content item to something new. - - This method will only be called if content is non-`null`. - - @method objectAtContent - @param {Number} idx The index to retrieve. - @return {Object} the value or undefined if none found - */ - objectAtContent: function(idx) { - return get(this, 'arrangedContent').objectAt(idx); - }, - - /** - Should actually replace the specified objects on the content array. - You can override this method in subclasses to transform the content item - into something new. - - This method will only be called if content is non-`null`. - - @method replaceContent - @param {Number} idx The starting index - @param {Number} amt The number of items to remove from the content. - @param {Array} objects Optional array of objects to insert or null if no - objects. - @return {void} - */ - replaceContent: function(idx, amt, objects) { - get(this, 'content').replace(idx, amt, objects); - }, - - /** - Invoked when the content property is about to change. Notifies observers that the - entire array content will change. - - @private - @method _contentWillChange - */ - _contentWillChange: Ember.beforeObserver('content', function() { - this._teardownContent(); - }), - - _teardownContent: function() { - var content = get(this, 'content'); - - if (content) { - content.removeArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - } - }, - - contentArrayWillChange: Ember.K, - contentArrayDidChange: Ember.K, - - /** - Invoked when the content property changes. Notifies observers that the - entire array content has changed. - - @private - @method _contentDidChange - */ - _contentDidChange: Ember.observer('content', function() { - var content = get(this, 'content'); - - Ember.assert("Can't set ArrayProxy's content to itself", content !== this); - - this._setupContent(); - }), - - _setupContent: function() { - var content = get(this, 'content'); - - if (content) { - Ember.assert(Ember.String.fmt('ArrayProxy expects an Array or ' + - 'Ember.ArrayProxy, but you passed %@', [typeof content]), - Ember.isArray(content) || content.isDestroyed); - - content.addArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - } - }, - - _arrangedContentWillChange: Ember.beforeObserver('arrangedContent', function() { - var arrangedContent = get(this, 'arrangedContent'), - len = arrangedContent ? get(arrangedContent, 'length') : 0; - - this.arrangedContentArrayWillChange(this, 0, len, undefined); - this.arrangedContentWillChange(this); - - this._teardownArrangedContent(arrangedContent); - }), - - _arrangedContentDidChange: Ember.observer('arrangedContent', function() { - var arrangedContent = get(this, 'arrangedContent'), - len = arrangedContent ? get(arrangedContent, 'length') : 0; - - Ember.assert("Can't set ArrayProxy's content to itself", arrangedContent !== this); - - this._setupArrangedContent(); - - this.arrangedContentDidChange(this); - this.arrangedContentArrayDidChange(this, 0, undefined, len); - }), - - _setupArrangedContent: function() { - var arrangedContent = get(this, 'arrangedContent'); - - if (arrangedContent) { - Ember.assert(Ember.String.fmt('ArrayProxy expects an Array or ' + - 'Ember.ArrayProxy, but you passed %@', [typeof arrangedContent]), - Ember.isArray(arrangedContent) || arrangedContent.isDestroyed); - - arrangedContent.addArrayObserver(this, { - willChange: 'arrangedContentArrayWillChange', - didChange: 'arrangedContentArrayDidChange' - }); - } - }, - - _teardownArrangedContent: function() { - var arrangedContent = get(this, 'arrangedContent'); - - if (arrangedContent) { - arrangedContent.removeArrayObserver(this, { - willChange: 'arrangedContentArrayWillChange', - didChange: 'arrangedContentArrayDidChange' - }); - } - }, - - arrangedContentWillChange: Ember.K, - arrangedContentDidChange: Ember.K, - - objectAt: function(idx) { - return get(this, 'content') && this.objectAtContent(idx); - }, - - length: Ember.computed(function() { - var arrangedContent = get(this, 'arrangedContent'); - return arrangedContent ? get(arrangedContent, 'length') : 0; - // No dependencies since Enumerable notifies length of change - }), - - _replace: function(idx, amt, objects) { - var content = get(this, 'content'); - Ember.assert('The content property of '+ this.constructor + ' should be set before modifying it', content); - if (content) this.replaceContent(idx, amt, objects); - return this; - }, - - replace: function() { - if (get(this, 'arrangedContent') === get(this, 'content')) { - this._replace.apply(this, arguments); - } else { - throw new Ember.Error("Using replace on an arranged ArrayProxy is not allowed."); - } - }, - - _insertAt: function(idx, object) { - if (idx > get(this, 'content.length')) throw new Ember.Error(OUT_OF_RANGE_EXCEPTION); - this._replace(idx, 0, [object]); - return this; - }, - - insertAt: function(idx, object) { - if (get(this, 'arrangedContent') === get(this, 'content')) { - return this._insertAt(idx, object); - } else { - throw new Ember.Error("Using insertAt on an arranged ArrayProxy is not allowed."); - } - }, - - removeAt: function(start, len) { - if ('number' === typeof start) { - var content = get(this, 'content'), - arrangedContent = get(this, 'arrangedContent'), - indices = [], i; - - if ((start < 0) || (start >= get(this, 'length'))) { - throw new Ember.Error(OUT_OF_RANGE_EXCEPTION); - } - - if (len === undefined) len = 1; - - // Get a list of indices in original content to remove - for (i=start; i=idx) { - var item = content.objectAt(loc); - if (item) { - Ember.assert('When using @each to observe the array ' + content + ', the array must return an object', Ember.typeOf(item) === 'instance' || Ember.typeOf(item) === 'object'); - Ember.addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); - Ember.addObserver(item, keyName, proxy, 'contentKeyDidChange'); - - // keep track of the index each item was found at so we can map - // it back when the obj changes. - guid = guidFor(item); - if (!objects[guid]) objects[guid] = []; - objects[guid].push(loc); - } - } -} - -function removeObserverForContentKey(content, keyName, proxy, idx, loc) { - var objects = proxy._objects; - if (!objects) objects = proxy._objects = {}; - var indicies, guid; - - while(--loc>=idx) { - var item = content.objectAt(loc); - if (item) { - Ember.removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); - Ember.removeObserver(item, keyName, proxy, 'contentKeyDidChange'); - - guid = guidFor(item); - indicies = objects[guid]; - indicies[indexOf.call(indicies, loc)] = null; - } - } -} - -/** - This is the object instance returned when you get the `@each` property on an - array. It uses the unknownProperty handler to automatically create - EachArray instances for property names. - - @private - @class EachProxy - @namespace Ember - @extends Ember.Object -*/ -Ember.EachProxy = Ember.Object.extend({ - - init: function(content) { - this._super(); - this._content = content; - content.addArrayObserver(this); - - // in case someone is already observing some keys make sure they are - // added - forEach(Ember.watchedEvents(this), function(eventName) { - this.didAddListener(eventName); - }, this); - }, - - /** - You can directly access mapped properties by simply requesting them. - The `unknownProperty` handler will generate an EachArray of each item. - - @method unknownProperty - @param keyName {String} - @param value {*} - */ - unknownProperty: function(keyName, value) { - var ret; - ret = new EachArray(this._content, keyName, this); - Ember.defineProperty(this, keyName, null, ret); - this.beginObservingContentKey(keyName); - return ret; - }, - - // .......................................................... - // ARRAY CHANGES - // Invokes whenever the content array itself changes. - - arrayWillChange: function(content, idx, removedCnt, addedCnt) { - var keys = this._keys, key, lim; - - lim = removedCnt>0 ? idx+removedCnt : -1; - Ember.beginPropertyChanges(this); - - for(key in keys) { - if (!keys.hasOwnProperty(key)) { continue; } - - if (lim>0) { removeObserverForContentKey(content, key, this, idx, lim); } - - Ember.propertyWillChange(this, key); - } - - Ember.propertyWillChange(this._content, '@each'); - Ember.endPropertyChanges(this); - }, - - arrayDidChange: function(content, idx, removedCnt, addedCnt) { - var keys = this._keys, lim; - - lim = addedCnt>0 ? idx+addedCnt : -1; - Ember.changeProperties(function() { - for(var key in keys) { - if (!keys.hasOwnProperty(key)) { continue; } - - if (lim>0) { addObserverForContentKey(content, key, this, idx, lim); } - - Ember.propertyDidChange(this, key); - } - - Ember.propertyDidChange(this._content, '@each'); - }, this); - }, - - // .......................................................... - // LISTEN FOR NEW OBSERVERS AND OTHER EVENT LISTENERS - // Start monitoring keys based on who is listening... - - didAddListener: function(eventName) { - if (IS_OBSERVER.test(eventName)) { - this.beginObservingContentKey(eventName.slice(0, -7)); - } - }, - - didRemoveListener: function(eventName) { - if (IS_OBSERVER.test(eventName)) { - this.stopObservingContentKey(eventName.slice(0, -7)); - } - }, - - // .......................................................... - // CONTENT KEY OBSERVING - // Actual watch keys on the source content. - - beginObservingContentKey: function(keyName) { - var keys = this._keys; - if (!keys) keys = this._keys = {}; - if (!keys[keyName]) { - keys[keyName] = 1; - var content = this._content, - len = get(content, 'length'); - addObserverForContentKey(content, keyName, this, 0, len); - } else { - keys[keyName]++; - } - }, - - stopObservingContentKey: function(keyName) { - var keys = this._keys; - if (keys && (keys[keyName]>0) && (--keys[keyName]<=0)) { - var content = this._content, - len = get(content, 'length'); - removeObserverForContentKey(content, keyName, this, 0, len); - } - }, - - contentKeyWillChange: function(obj, keyName) { - Ember.propertyWillChange(this, keyName); - }, - - contentKeyDidChange: function(obj, keyName) { - Ember.propertyDidChange(this, keyName); - } - -}); - - - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - - -var get = Ember.get, set = Ember.set, replace = Ember.EnumerableUtils._replace; - -// Add Ember.Array to Array.prototype. Remove methods with native -// implementations and supply some more optimized versions of generic methods -// because they are so common. -var NativeArray = Ember.Mixin.create(Ember.MutableArray, Ember.Observable, Ember.Copyable, { - - // because length is a built-in property we need to know to just get the - // original property. - get: function(key) { - if (key==='length') return this.length; - else if ('number' === typeof key) return this[key]; - else return this._super(key); - }, - - objectAt: function(idx) { - return this[idx]; - }, - - // primitive for array support. - replace: function(idx, amt, objects) { - - if (this.isFrozen) throw Ember.FROZEN_ERROR; - - // if we replaced exactly the same number of items, then pass only the - // replaced range. Otherwise, pass the full remaining array length - // since everything has shifted - var len = objects ? get(objects, 'length') : 0; - this.arrayContentWillChange(idx, amt, len); - - if (len === 0) { - this.splice(idx, amt); - } else { - replace(this, idx, amt, objects); - } - - this.arrayContentDidChange(idx, amt, len); - return this; - }, - - // If you ask for an unknown property, then try to collect the value - // from member items. - unknownProperty: function(key, value) { - var ret;// = this.reducedProperty(key, value) ; - if ((value !== undefined) && ret === undefined) { - ret = this[key] = value; - } - return ret ; - }, - - // If browser did not implement indexOf natively, then override with - // specialized version - indexOf: function(object, startAt) { - var idx, len = this.length; - - if (startAt === undefined) startAt = 0; - else startAt = (startAt < 0) ? Math.ceil(startAt) : Math.floor(startAt); - if (startAt < 0) startAt += len; - - for(idx=startAt;idx=0;idx--) { - if (this[idx] === object) return idx ; - } - return -1; - }, - - copy: function(deep) { - if (deep) { - return this.map(function(item) { return Ember.copy(item, true); }); - } - - return this.slice(); - } -}); - -// Remove any methods implemented natively so we don't override them -var ignore = ['length']; -Ember.EnumerableUtils.forEach(NativeArray.keys(), function(methodName) { - if (Array.prototype[methodName]) ignore.push(methodName); -}); - -if (ignore.length>0) { - NativeArray = NativeArray.without.apply(NativeArray, ignore); -} - -/** - The NativeArray mixin contains the properties needed to to make the native - Array support Ember.MutableArray and all of its dependent APIs. Unless you - have `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Array` set to - false, this will be applied automatically. Otherwise you can apply the mixin - at anytime by calling `Ember.NativeArray.activate`. - - @class NativeArray - @namespace Ember - @uses Ember.MutableArray - @uses Ember.Observable - @uses Ember.Copyable -*/ -Ember.NativeArray = NativeArray; - -/** - Creates an `Ember.NativeArray` from an Array like object. - Does not modify the original object. Ember.A is not needed if - `Ember.EXTEND_PROTOTYPES` is `true` (the default value). However, - it is recommended that you use Ember.A when creating addons for - ember or when you can not guarantee that `Ember.EXTEND_PROTOTYPES` - will be `true`. - - Example - - ```js - var Pagination = Ember.CollectionView.extend({ - tagName: 'ul', - classNames: ['pagination'], - init: function() { - this._super(); - if (!this.get('content')) { - this.set('content', Ember.A([])); - } - } - }); - ``` - - @method A - @for Ember - @return {Ember.NativeArray} -*/ -Ember.A = function(arr) { - if (arr === undefined) { arr = []; } - return Ember.Array.detect(arr) ? arr : Ember.NativeArray.apply(arr); -}; - -/** - Activates the mixin on the Array.prototype if not already applied. Calling - this method more than once is safe. This will be called when ember is loaded - unless you have `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Array` - set to `false`. - - Example - - ```js - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { - Ember.NativeArray.activate(); - } - ``` - - @method activate - @for Ember.NativeArray - @static - @return {void} -*/ -Ember.NativeArray.activate = function() { - NativeArray.apply(Array.prototype); - - Ember.A = function(arr) { return arr || []; }; -}; - -if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Array) { - Ember.NativeArray.activate(); -} - - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor, isNone = Ember.isNone, fmt = Ember.String.fmt; - -/** - An unordered collection of objects. - - A Set works a bit like an array except that its items are not ordered. You - can create a set to efficiently test for membership for an object. You can - also iterate through a set just like an array, even accessing objects by - index, however there is no guarantee as to their order. - - All Sets are observable via the Enumerable Observer API - which works - on any enumerable object including both Sets and Arrays. - - ## Creating a Set - - You can create a set like you would most objects using - `new Ember.Set()`. Most new sets you create will be empty, but you can - also initialize the set with some content by passing an array or other - enumerable of objects to the constructor. - - Finally, you can pass in an existing set and the set will be copied. You - can also create a copy of a set by calling `Ember.Set#copy()`. - - ```javascript - // creates a new empty set - var foundNames = new Ember.Set(); - - // creates a set with four names in it. - var names = new Ember.Set(["Charles", "Tom", "Juan", "Alex"]); // :P - - // creates a copy of the names set. - var namesCopy = new Ember.Set(names); - - // same as above. - var anotherNamesCopy = names.copy(); - ``` - - ## Adding/Removing Objects - - You generally add or remove objects from a set using `add()` or - `remove()`. You can add any type of object including primitives such as - numbers, strings, and booleans. - - Unlike arrays, objects can only exist one time in a set. If you call `add()` - on a set with the same object multiple times, the object will only be added - once. Likewise, calling `remove()` with the same object multiple times will - remove the object the first time and have no effect on future calls until - you add the object to the set again. - - NOTE: You cannot add/remove `null` or `undefined` to a set. Any attempt to do - so will be ignored. - - In addition to add/remove you can also call `push()`/`pop()`. Push behaves - just like `add()` but `pop()`, unlike `remove()` will pick an arbitrary - object, remove it and return it. This is a good way to use a set as a job - queue when you don't care which order the jobs are executed in. - - ## Testing for an Object - - To test for an object's presence in a set you simply call - `Ember.Set#contains()`. - - ## Observing changes - - When using `Ember.Set`, you can observe the `"[]"` property to be - alerted whenever the content changes. You can also add an enumerable - observer to the set to be notified of specific objects that are added and - removed from the set. See [Ember.Enumerable](/api/classes/Ember.Enumerable.html) - for more information on enumerables. - - This is often unhelpful. If you are filtering sets of objects, for instance, - it is very inefficient to re-filter all of the items each time the set - changes. It would be better if you could just adjust the filtered set based - on what was changed on the original set. The same issue applies to merging - sets, as well. - - ## Other Methods - - `Ember.Set` primary implements other mixin APIs. For a complete reference - on the methods you will use with `Ember.Set`, please consult these mixins. - The most useful ones will be `Ember.Enumerable` and - `Ember.MutableEnumerable` which implement most of the common iterator - methods you are used to on Array. - - Note that you can also use the `Ember.Copyable` and `Ember.Freezable` - APIs on `Ember.Set` as well. Once a set is frozen it can no longer be - modified. The benefit of this is that when you call `frozenCopy()` on it, - Ember will avoid making copies of the set. This allows you to write - code that can know with certainty when the underlying set data will or - will not be modified. - - @class Set - @namespace Ember - @extends Ember.CoreObject - @uses Ember.MutableEnumerable - @uses Ember.Copyable - @uses Ember.Freezable - @since Ember 0.9 -*/ -Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Ember.Freezable, - { - - // .......................................................... - // IMPLEMENT ENUMERABLE APIS - // - - /** - This property will change as the number of objects in the set changes. - - @property length - @type number - @default 0 - */ - length: 0, - - /** - Clears the set. This is useful if you want to reuse an existing set - without having to recreate it. - - ```javascript - var colors = new Ember.Set(["red", "green", "blue"]); - colors.length; // 3 - colors.clear(); - colors.length; // 0 - ``` - - @method clear - @return {Ember.Set} An empty Set - */ - clear: function() { - if (this.isFrozen) { throw new Ember.Error(Ember.FROZEN_ERROR); } - - var len = get(this, 'length'); - if (len === 0) { return this; } - - var guid; - - this.enumerableContentWillChange(len, 0); - Ember.propertyWillChange(this, 'firstObject'); - Ember.propertyWillChange(this, 'lastObject'); - - for (var i=0; i < len; i++) { - guid = guidFor(this[i]); - delete this[guid]; - delete this[i]; - } - - set(this, 'length', 0); - - Ember.propertyDidChange(this, 'firstObject'); - Ember.propertyDidChange(this, 'lastObject'); - this.enumerableContentDidChange(len, 0); - - return this; - }, - - /** - Returns true if the passed object is also an enumerable that contains the - same objects as the receiver. - - ```javascript - var colors = ["red", "green", "blue"], - same_colors = new Ember.Set(colors); - - same_colors.isEqual(colors); // true - same_colors.isEqual(["purple", "brown"]); // false - ``` - - @method isEqual - @param {Ember.Set} obj the other object. - @return {Boolean} - */ - isEqual: function(obj) { - // fail fast - if (!Ember.Enumerable.detect(obj)) return false; - - var loc = get(this, 'length'); - if (get(obj, 'length') !== loc) return false; - - while(--loc >= 0) { - if (!obj.contains(this[loc])) return false; - } - - return true; - }, - - /** - Adds an object to the set. Only non-`null` objects can be added to a set - and those can only be added once. If the object is already in the set or - the passed value is null this method will have no effect. - - This is an alias for `Ember.MutableEnumerable.addObject()`. - - ```javascript - var colors = new Ember.Set(); - colors.add("blue"); // ["blue"] - colors.add("blue"); // ["blue"] - colors.add("red"); // ["blue", "red"] - colors.add(null); // ["blue", "red"] - colors.add(undefined); // ["blue", "red"] - ``` - - @method add - @param {Object} obj The object to add. - @return {Ember.Set} The set itself. - */ - add: Ember.aliasMethod('addObject'), - - /** - Removes the object from the set if it is found. If you pass a `null` value - or an object that is already not in the set, this method will have no - effect. This is an alias for `Ember.MutableEnumerable.removeObject()`. - - ```javascript - var colors = new Ember.Set(["red", "green", "blue"]); - colors.remove("red"); // ["blue", "green"] - colors.remove("purple"); // ["blue", "green"] - colors.remove(null); // ["blue", "green"] - ``` - - @method remove - @param {Object} obj The object to remove - @return {Ember.Set} The set itself. - */ - remove: Ember.aliasMethod('removeObject'), - - /** - Removes the last element from the set and returns it, or `null` if it's empty. - - ```javascript - var colors = new Ember.Set(["green", "blue"]); - colors.pop(); // "blue" - colors.pop(); // "green" - colors.pop(); // null - ``` - - @method pop - @return {Object} The removed object from the set or null. - */ - pop: function() { - if (get(this, 'isFrozen')) throw new Ember.Error(Ember.FROZEN_ERROR); - var obj = this.length > 0 ? this[this.length-1] : null; - this.remove(obj); - return obj; - }, - - /** - Inserts the given object on to the end of the set. It returns - the set itself. - - This is an alias for `Ember.MutableEnumerable.addObject()`. - - ```javascript - var colors = new Ember.Set(); - colors.push("red"); // ["red"] - colors.push("green"); // ["red", "green"] - colors.push("blue"); // ["red", "green", "blue"] - ``` - - @method push - @return {Ember.Set} The set itself. - */ - push: Ember.aliasMethod('addObject'), - - /** - Removes the last element from the set and returns it, or `null` if it's empty. - - This is an alias for `Ember.Set.pop()`. - - ```javascript - var colors = new Ember.Set(["green", "blue"]); - colors.shift(); // "blue" - colors.shift(); // "green" - colors.shift(); // null - ``` - - @method shift - @return {Object} The removed object from the set or null. - */ - shift: Ember.aliasMethod('pop'), - - /** - Inserts the given object on to the end of the set. It returns - the set itself. - - This is an alias of `Ember.Set.push()` - - ```javascript - var colors = new Ember.Set(); - colors.unshift("red"); // ["red"] - colors.unshift("green"); // ["red", "green"] - colors.unshift("blue"); // ["red", "green", "blue"] - ``` - - @method unshift - @return {Ember.Set} The set itself. - */ - unshift: Ember.aliasMethod('push'), - - /** - Adds each object in the passed enumerable to the set. - - This is an alias of `Ember.MutableEnumerable.addObjects()` - - ```javascript - var colors = new Ember.Set(); - colors.addEach(["red", "green", "blue"]); // ["red", "green", "blue"] - ``` - - @method addEach - @param {Ember.Enumerable} objects the objects to add. - @return {Ember.Set} The set itself. - */ - addEach: Ember.aliasMethod('addObjects'), - - /** - Removes each object in the passed enumerable to the set. - - This is an alias of `Ember.MutableEnumerable.removeObjects()` - - ```javascript - var colors = new Ember.Set(["red", "green", "blue"]); - colors.removeEach(["red", "blue"]); // ["green"] - ``` - - @method removeEach - @param {Ember.Enumerable} objects the objects to remove. - @return {Ember.Set} The set itself. - */ - removeEach: Ember.aliasMethod('removeObjects'), - - // .......................................................... - // PRIVATE ENUMERABLE SUPPORT - // - - init: function(items) { - this._super(); - if (items) this.addObjects(items); - }, - - // implement Ember.Enumerable - nextObject: function(idx) { - return this[idx]; - }, - - // more optimized version - firstObject: Ember.computed(function() { - return this.length > 0 ? this[0] : undefined; - }), - - // more optimized version - lastObject: Ember.computed(function() { - return this.length > 0 ? this[this.length-1] : undefined; - }), - - // implements Ember.MutableEnumerable - addObject: function(obj) { - if (get(this, 'isFrozen')) throw new Ember.Error(Ember.FROZEN_ERROR); - if (isNone(obj)) return this; // nothing to do - - var guid = guidFor(obj), - idx = this[guid], - len = get(this, 'length'), - added ; - - if (idx>=0 && idx=0 && idx=0; - }, - - copy: function() { - var C = this.constructor, ret = new C(), loc = get(this, 'length'); - set(ret, 'length', loc); - while(--loc>=0) { - ret[loc] = this[loc]; - ret[guidFor(this[loc])] = loc; - } - return ret; - }, - - toString: function() { - var len = this.length, idx, array = []; - for(idx = 0; idx < len; idx++) { - array[idx] = this[idx]; - } - return fmt("Ember.Set<%@>", [array.join(',')]); - } - -}); - -})(); - - - -(function() { -var DeferredMixin = Ember.DeferredMixin, // mixins/deferred - get = Ember.get; - -var Deferred = Ember.Object.extend(DeferredMixin); - -Deferred.reopenClass({ - promise: function(callback, binding) { - var deferred = Deferred.create(); - callback.call(binding, deferred); - return deferred; - } -}); - -Ember.Deferred = Deferred; - -})(); - - - -(function() { -/*globals CustomEvent */ - -var forEach = Ember.ArrayPolyfills.forEach; - -/** - @module ember - @submodule ember-runtime -*/ - -var loadHooks = Ember.ENV.EMBER_LOAD_HOOKS || {}; -var loaded = {}; - -/** - Detects when a specific package of Ember (e.g. 'Ember.Handlebars') - has fully loaded and is available for extension. - - The provided `callback` will be called with the `name` passed - resolved from a string into the object: - - ``` javascript - Ember.onLoad('Ember.Handlebars' function(hbars){ - hbars.registerHelper(...); - }); - ``` - - @method onLoad - @for Ember - @param name {String} name of hook - @param callback {Function} callback to be called -*/ -Ember.onLoad = function(name, callback) { - var object; - - loadHooks[name] = loadHooks[name] || Ember.A(); - loadHooks[name].pushObject(callback); - - if (object = loaded[name]) { - callback(object); - } -}; - -/** - Called when an Ember.js package (e.g Ember.Handlebars) has finished - loading. Triggers any callbacks registered for this event. - - @method runLoadHooks - @for Ember - @param name {String} name of hook - @param object {Object} object to pass to callbacks -*/ -Ember.runLoadHooks = function(name, object) { - loaded[name] = object; - - if (typeof window === 'object' && typeof window.dispatchEvent === 'function' && typeof CustomEvent === "function") { - var event = new CustomEvent(name, {detail: object, name: name}); - window.dispatchEvent(event); - } - - if (loadHooks[name]) { - forEach.call(loadHooks[name], function(callback) { - callback(object); - }); - } -}; - -})(); - - - -(function() { - -})(); - - - -(function() { -var get = Ember.get; - -/** -@module ember -@submodule ember-runtime -*/ - -/** - `Ember.ControllerMixin` provides a standard interface for all classes that - compose Ember's controller layer: `Ember.Controller`, - `Ember.ArrayController`, and `Ember.ObjectController`. - - @class ControllerMixin - @namespace Ember - @uses Ember.ActionHandler -*/ -Ember.ControllerMixin = Ember.Mixin.create(Ember.ActionHandler, { - /* ducktype as a controller */ - isController: true, - - /** - The object to which actions from the view should be sent. - - For example, when a Handlebars template uses the `{{action}}` helper, - it will attempt to send the action to the view's controller's `target`. - - By default, a controller's `target` is set to the router after it is - instantiated by `Ember.Application#initialize`. - - @property target - @default null - */ - target: null, - - container: null, - - parentController: null, - - store: null, - - model: Ember.computed.alias('content'), - - deprecatedSendHandles: function(actionName) { - return !!this[actionName]; - }, - - deprecatedSend: function(actionName) { - var args = [].slice.call(arguments, 1); - Ember.assert('' + this + " has the action " + actionName + " but it is not a function", typeof this[actionName] === 'function'); - Ember.deprecate('Action handlers implemented directly on controllers are deprecated in favor of action handlers on an `actions` object ( action: `' + actionName + '` on ' + this + ')', false); - this[actionName].apply(this, args); - return; - } -}); - -/** - @class Controller - @namespace Ember - @extends Ember.Object - @uses Ember.ControllerMixin -*/ -Ember.Controller = Ember.Object.extend(Ember.ControllerMixin); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var get = Ember.get, set = Ember.set, forEach = Ember.EnumerableUtils.forEach; - -/** - `Ember.SortableMixin` provides a standard interface for array proxies - to specify a sort order and maintain this sorting when objects are added, - removed, or updated without changing the implicit order of their underlying - content array: - - ```javascript - songs = [ - {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'}, - {trackNumber: 2, title: 'Back in the U.S.S.R.'}, - {trackNumber: 3, title: 'Glass Onion'}, - ]; - - songsController = Ember.ArrayController.create({ - content: songs, - sortProperties: ['trackNumber'], - sortAscending: true - }); - - songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} - - songsController.addObject({trackNumber: 1, title: 'Dear Prudence'}); - songsController.get('firstObject'); // {trackNumber: 1, title: 'Dear Prudence'} - ``` - - If you add or remove the properties to sort by or change the sort direction the content - sort order will be automatically updated. - - ```javascript - songsController.set('sortProperties', ['title']); - songsController.get('firstObject'); // {trackNumber: 2, title: 'Back in the U.S.S.R.'} - - songsController.toggleProperty('sortAscending'); - songsController.get('firstObject'); // {trackNumber: 4, title: 'Ob-La-Di, Ob-La-Da'} - ``` - - SortableMixin works by sorting the arrangedContent array, which is the array that - arrayProxy displays. Due to the fact that the underlying 'content' array is not changed, that - array will not display the sorted list: - - ```javascript - songsController.get('content').get('firstObject'); // Returns the unsorted original content - songsController.get('firstObject'); // Returns the sorted content. - ``` - - Although the sorted content can also be accessed through the arrangedContent property, - it is preferable to use the proxied class and not the arrangedContent array directly. - - @class SortableMixin - @namespace Ember - @uses Ember.MutableEnumerable -*/ -Ember.SortableMixin = Ember.Mixin.create(Ember.MutableEnumerable, { - - /** - Specifies which properties dictate the arrangedContent's sort order. - - When specifying multiple properties the sorting will use properties - from the `sortProperties` array prioritized from first to last. - - @property {Array} sortProperties - */ - sortProperties: null, - - /** - Specifies the arrangedContent's sort direction - - @property {Boolean} sortAscending - */ - sortAscending: true, - - /** - The function used to compare two values. You can override this if you - want to do custom comparisons. Functions must be of the type expected by - Array#sort, i.e. - return 0 if the two parameters are equal, - return a negative value if the first parameter is smaller than the second or - return a positive value otherwise: - - ```javascript - function(x,y) { // These are assumed to be integers - if (x === y) - return 0; - return x < y ? -1 : 1; - } - ``` - - @property sortFunction - @type {Function} - @default Ember.compare - */ - sortFunction: Ember.compare, - - orderBy: function(item1, item2) { - var result = 0, - sortProperties = get(this, 'sortProperties'), - sortAscending = get(this, 'sortAscending'), - sortFunction = get(this, 'sortFunction'); - - Ember.assert("you need to define `sortProperties`", !!sortProperties); - - forEach(sortProperties, function(propertyName) { - if (result === 0) { - result = sortFunction(get(item1, propertyName), get(item2, propertyName)); - if ((result !== 0) && !sortAscending) { - result = (-1) * result; - } - } - }); - - return result; - }, - - destroy: function() { - var content = get(this, 'content'), - sortProperties = get(this, 'sortProperties'); - - if (content && sortProperties) { - forEach(content, function(item) { - forEach(sortProperties, function(sortProperty) { - Ember.removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); - } - - return this._super(); - }, - - isSorted: Ember.computed.bool('sortProperties'), - - /** - Overrides the default arrangedContent from arrayProxy in order to sort by sortFunction. - Also sets up observers for each sortProperty on each item in the content Array. - - @property arrangedContent - */ - - arrangedContent: Ember.computed('content', 'sortProperties.@each', function(key, value) { - var content = get(this, 'content'), - isSorted = get(this, 'isSorted'), - sortProperties = get(this, 'sortProperties'), - self = this; - - if (content && isSorted) { - content = content.slice(); - content.sort(function(item1, item2) { - return self.orderBy(item1, item2); - }); - forEach(content, function(item) { - forEach(sortProperties, function(sortProperty) { - Ember.addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); - return Ember.A(content); - } - - return content; - }), - - _contentWillChange: Ember.beforeObserver('content', function() { - var content = get(this, 'content'), - sortProperties = get(this, 'sortProperties'); - - if (content && sortProperties) { - forEach(content, function(item) { - forEach(sortProperties, function(sortProperty) { - Ember.removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); - } - - this._super(); - }), - - sortAscendingWillChange: Ember.beforeObserver('sortAscending', function() { - this._lastSortAscending = get(this, 'sortAscending'); - }), - - sortAscendingDidChange: Ember.observer('sortAscending', function() { - if (get(this, 'sortAscending') !== this._lastSortAscending) { - var arrangedContent = get(this, 'arrangedContent'); - arrangedContent.reverseObjects(); - } - }), - - contentArrayWillChange: function(array, idx, removedCount, addedCount) { - var isSorted = get(this, 'isSorted'); - - if (isSorted) { - var arrangedContent = get(this, 'arrangedContent'); - var removedObjects = array.slice(idx, idx+removedCount); - var sortProperties = get(this, 'sortProperties'); - - forEach(removedObjects, function(item) { - arrangedContent.removeObject(item); - - forEach(sortProperties, function(sortProperty) { - Ember.removeObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); - } - - return this._super(array, idx, removedCount, addedCount); - }, - - contentArrayDidChange: function(array, idx, removedCount, addedCount) { - var isSorted = get(this, 'isSorted'), - sortProperties = get(this, 'sortProperties'); - - if (isSorted) { - var addedObjects = array.slice(idx, idx+addedCount); - - forEach(addedObjects, function(item) { - this.insertItemSorted(item); - - forEach(sortProperties, function(sortProperty) { - Ember.addObserver(item, sortProperty, this, 'contentItemSortPropertyDidChange'); - }, this); - }, this); - } - - return this._super(array, idx, removedCount, addedCount); - }, - - insertItemSorted: function(item) { - var arrangedContent = get(this, 'arrangedContent'); - var length = get(arrangedContent, 'length'); - - var idx = this._binarySearch(item, 0, length); - arrangedContent.insertAt(idx, item); - }, - - contentItemSortPropertyDidChange: function(item) { - var arrangedContent = get(this, 'arrangedContent'), - oldIndex = arrangedContent.indexOf(item), - leftItem = arrangedContent.objectAt(oldIndex - 1), - rightItem = arrangedContent.objectAt(oldIndex + 1), - leftResult = leftItem && this.orderBy(item, leftItem), - rightResult = rightItem && this.orderBy(item, rightItem); - - if (leftResult < 0 || rightResult > 0) { - arrangedContent.removeObject(item); - this.insertItemSorted(item); - } - }, - - _binarySearch: function(item, low, high) { - var mid, midItem, res, arrangedContent; - - if (low === high) { - return low; - } - - arrangedContent = get(this, 'arrangedContent'); - - mid = low + Math.floor((high - low) / 2); - midItem = arrangedContent.objectAt(mid); - - res = this.orderBy(midItem, item); - - if (res < 0) { - return this._binarySearch(item, mid+1, high); - } else if (res > 0) { - return this._binarySearch(item, low, mid); - } - - return mid; - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -var get = Ember.get, set = Ember.set, forEach = Ember.EnumerableUtils.forEach, - replace = Ember.EnumerableUtils.replace; - -/** - `Ember.ArrayController` provides a way for you to publish a collection of - objects so that you can easily bind to the collection from a Handlebars - `#each` helper, an `Ember.CollectionView`, or other controllers. - - The advantage of using an `ArrayController` is that you only have to set up - your view bindings once; to change what's displayed, simply swap out the - `content` property on the controller. - - For example, imagine you wanted to display a list of items fetched via an XHR - request. Create an `Ember.ArrayController` and set its `content` property: - - ```javascript - MyApp.listController = Ember.ArrayController.create(); - - $.get('people.json', function(data) { - MyApp.listController.set('content', data); - }); - ``` - - Then, create a view that binds to your new controller: - - ```handlebars - {{#each MyApp.listController}} - {{firstName}} {{lastName}} - {{/each}} - ``` - - Although you are binding to the controller, the behavior of this controller - is to pass through any methods or properties to the underlying array. This - capability comes from `Ember.ArrayProxy`, which this class inherits from. - - Sometimes you want to display computed properties within the body of an - `#each` helper that depend on the underlying items in `content`, but are not - present on those items. To do this, set `itemController` to the name of a - controller (probably an `ObjectController`) that will wrap each individual item. - - For example: - - ```handlebars - {{#each post in controller}} -
  • {{title}} ({{titleLength}} characters)
  • - {{/each}} - ``` - - ```javascript - App.PostsController = Ember.ArrayController.extend({ - itemController: 'post' - }); - - App.PostController = Ember.ObjectController.extend({ - // the `title` property will be proxied to the underlying post. - - titleLength: function() { - return this.get('title').length; - }.property('title') - }); - ``` - - In some cases it is helpful to return a different `itemController` depending - on the particular item. Subclasses can do this by overriding - `lookupItemController`. - - For example: - - ```javascript - App.MyArrayController = Ember.ArrayController.extend({ - lookupItemController: function( object ) { - if (object.get('isSpecial')) { - return "special"; // use App.SpecialController - } else { - return "regular"; // use App.RegularController - } - } - }); - ``` - - The itemController instances will have a `parentController` property set to - the `ArrayController` instance. - - @class ArrayController - @namespace Ember - @extends Ember.ArrayProxy - @uses Ember.SortableMixin - @uses Ember.ControllerMixin -*/ - -Ember.ArrayController = Ember.ArrayProxy.extend(Ember.ControllerMixin, - Ember.SortableMixin, { - - /** - The controller used to wrap items, if any. - - @property itemController - @type String - @default null - */ - itemController: null, - - /** - Return the name of the controller to wrap items, or `null` if items should - be returned directly. The default implementation simply returns the - `itemController` property, but subclasses can override this method to return - different controllers for different objects. - - For example: - - ```javascript - App.MyArrayController = Ember.ArrayController.extend({ - lookupItemController: function( object ) { - if (object.get('isSpecial')) { - return "special"; // use App.SpecialController - } else { - return "regular"; // use App.RegularController - } - } - }); - ``` - - @method lookupItemController - @param {Object} object - @return {String} - */ - lookupItemController: function(object) { - return get(this, 'itemController'); - }, - - objectAtContent: function(idx) { - var length = get(this, 'length'), - arrangedContent = get(this,'arrangedContent'), - object = arrangedContent && arrangedContent.objectAt(idx); - - if (idx >= 0 && idx < length) { - var controllerClass = this.lookupItemController(object); - if (controllerClass) { - return this.controllerAt(idx, object, controllerClass); - } - } - - // When `controllerClass` is falsy, we have not opted in to using item - // controllers, so return the object directly. - - // When the index is out of range, we want to return the "out of range" - // value, whatever that might be. Rather than make assumptions - // (e.g. guessing `null` or `undefined`) we defer this to `arrangedContent`. - return object; - }, - - arrangedContentDidChange: function() { - this._super(); - this._resetSubControllers(); - }, - - arrayContentDidChange: function(idx, removedCnt, addedCnt) { - var subControllers = get(this, '_subControllers'), - subControllersToRemove = subControllers.slice(idx, idx+removedCnt); - - forEach(subControllersToRemove, function(subController) { - if (subController) { subController.destroy(); } - }); - - replace(subControllers, idx, removedCnt, new Array(addedCnt)); - - // The shadow array of subcontrollers must be updated before we trigger - // observers, otherwise observers will get the wrong subcontainer when - // calling `objectAt` - this._super(idx, removedCnt, addedCnt); - }, - - init: function() { - this._super(); - - this.set('_subControllers', Ember.A()); - }, - - content: Ember.computed(function () { - return Ember.A(); - }), - - /** - * Flag to mark as being "virtual". Used to keep this instance - * from participating in the parentController hierarchy. - * - * @private - * @type Boolean - */ - _isVirtual: false, - - controllerAt: function(idx, object, controllerClass) { - var container = get(this, 'container'), - subControllers = get(this, '_subControllers'), - subController = subControllers[idx], - fullName; - - if (subController) { return subController; } - - fullName = "controller:" + controllerClass; - - if (!container.has(fullName)) { - throw new Ember.Error('Could not resolve itemController: "' + controllerClass + '"'); - } - var parentController; - if (this._isVirtual) { - parentController = get(this, 'parentController'); - } - parentController = parentController || this; - subController = container.lookupFactory(fullName).create({ - target: this, - parentController: parentController, - content: object - }); - - subControllers[idx] = subController; - - return subController; - }, - - _subControllers: null, - - _resetSubControllers: function() { - var subControllers = get(this, '_subControllers'); - if (subControllers) { - forEach(subControllers, function(subController) { - if (subController) { subController.destroy(); } - }); - } - - this.set('_subControllers', Ember.A()); - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-runtime -*/ - -/** - `Ember.ObjectController` is part of Ember's Controller layer. It is intended - to wrap a single object, proxying unhandled attempts to `get` and `set` to the underlying - content object, and to forward unhandled action attempts to its `target`. - - `Ember.ObjectController` derives this functionality from its superclass - `Ember.ObjectProxy` and the `Ember.ControllerMixin` mixin. - - @class ObjectController - @namespace Ember - @extends Ember.ObjectProxy - @uses Ember.ControllerMixin -**/ -Ember.ObjectController = Ember.ObjectProxy.extend(Ember.ControllerMixin); - -})(); - - - -(function() { - -})(); - - - -(function() { -/** -Ember Runtime - -@module ember -@submodule ember-runtime -@requires ember-metal -*/ - -})(); - -(function() { -/** -@module ember -@submodule ember-views -*/ - -var jQuery = (Ember.imports && Ember.imports.jQuery) || (this && this.jQuery); -if (!jQuery && typeof require === 'function') { - jQuery = require('jquery'); -} - -Ember.assert("Ember Views require jQuery between 1.7 and 2.1", jQuery && (jQuery().jquery.match(/^((1\.(7|8|9|10|11))|(2\.(0|1)))(\.\d+)?(pre|rc\d?)?/) || Ember.ENV.FORCE_JQUERY)); - -/** - Alias for jQuery - - @method $ - @for Ember -*/ -Ember.$ = jQuery; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ -if (Ember.$) { - // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#dndevents - var dragEvents = Ember.String.w('dragstart drag dragenter dragleave dragover drop dragend'); - - // Copies the `dataTransfer` property from a browser event object onto the - // jQuery event object for the specified events - Ember.EnumerableUtils.forEach(dragEvents, function(eventName) { - Ember.$.event.fixHooks[eventName] = { props: ['dataTransfer'] }; - }); -} - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -/* BEGIN METAMORPH HELPERS */ - -// Internet Explorer prior to 9 does not allow setting innerHTML if the first element -// is a "zero-scope" element. This problem can be worked around by making -// the first node an invisible text node. We, like Modernizr, use ­ - -var needsShy = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "
    "; - testEl.firstChild.innerHTML = ""; - return testEl.firstChild.innerHTML === ''; -})(); - -// IE 8 (and likely earlier) likes to move whitespace preceeding -// a script tag to appear after it. This means that we can -// accidentally remove whitespace when updating a morph. -var movesWhitespace = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; -})(); - -// Use this to find children by ID instead of using jQuery -var findChildById = function(element, id) { - if (element.getAttribute('id') === id) { return element; } - - var len = element.childNodes.length, idx, node, found; - for (idx=0; idx 0) { - var len = matches.length, idx; - for (idx=0; idxTest'); - canSet = el.options.length === 1; - } - - innerHTMLTags[tagName] = canSet; - - return canSet; -}; - -var setInnerHTML = function(element, html) { - var tagName = element.tagName; - - if (canSetInnerHTML(tagName)) { - setInnerHTMLWithoutFix(element, html); - } else { - // Firefox versions < 11 do not have support for element.outerHTML. - var outerHTML = element.outerHTML || new XMLSerializer().serializeToString(element); - Ember.assert("Can't set innerHTML on "+element.tagName+" in this browser", outerHTML); - - var startTag = outerHTML.match(new RegExp("<"+tagName+"([^>]*)>", 'i'))[0], - endTag = ''; - - var wrapper = document.createElement('div'); - setInnerHTMLWithoutFix(wrapper, startTag + html + endTag); - element = wrapper.firstChild; - while (element.tagName !== tagName) { - element = element.nextSibling; - } - } - - return element; -}; - -function isSimpleClick(event) { - var modifier = event.shiftKey || event.metaKey || event.altKey || event.ctrlKey, - secondaryClick = event.which > 1; // IE9 may return undefined - - return !modifier && !secondaryClick; -} - -Ember.ViewUtils = { - setInnerHTML: setInnerHTML, - isSimpleClick: isSimpleClick -}; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -var get = Ember.get, set = Ember.set; - -var ClassSet = function() { - this.seen = {}; - this.list = []; -}; - -ClassSet.prototype = { - add: function(string) { - if (string in this.seen) { return; } - this.seen[string] = true; - - this.list.push(string); - }, - - toDOM: function() { - return this.list.join(" "); - } -}; - -var BAD_TAG_NAME_TEST_REGEXP = /[^a-zA-Z0-9\-]/; -var BAD_TAG_NAME_REPLACE_REGEXP = /[^a-zA-Z0-9\-]/g; - -function stripTagName(tagName) { - if (!tagName) { - return tagName; - } - - if (!BAD_TAG_NAME_TEST_REGEXP.test(tagName)) { - return tagName; - } - - return tagName.replace(BAD_TAG_NAME_REPLACE_REGEXP, ''); -} - -var BAD_CHARS_REGEXP = /&(?!\w+;)|[<>"'`]/g; -var POSSIBLE_CHARS_REGEXP = /[&<>"'`]/; - -function escapeAttribute(value) { - // Stolen shamelessly from Handlebars - - var escape = { - "<": "<", - ">": ">", - '"': """, - "'": "'", - "`": "`" - }; - - var escapeChar = function(chr) { - return escape[chr] || "&"; - }; - - var string = value.toString(); - - if(!POSSIBLE_CHARS_REGEXP.test(string)) { return string; } - return string.replace(BAD_CHARS_REGEXP, escapeChar); -} - -// IE 6/7 have bugs around setting names on inputs during creation. -// From http://msdn.microsoft.com/en-us/library/ie/ms536389(v=vs.85).aspx: -// "To include the NAME attribute at run time on objects created with the createElement method, use the eTag." -var canSetNameOnInputs = (function() { - var div = document.createElement('div'), - el = document.createElement('input'); - - el.setAttribute('name', 'foo'); - div.appendChild(el); - - return !!div.innerHTML.match('foo'); -})(); - -/** - `Ember.RenderBuffer` gathers information regarding the a view and generates the - final representation. `Ember.RenderBuffer` will generate HTML which can be pushed - to the DOM. - - ```javascript - var buffer = Ember.RenderBuffer('div'); - ``` - - @class RenderBuffer - @namespace Ember - @constructor - @param {String} tagName tag name (such as 'div' or 'p') used for the buffer -*/ -Ember.RenderBuffer = function(tagName) { - return new Ember._RenderBuffer(tagName); -}; - -Ember._RenderBuffer = function(tagName) { - this.tagNames = [tagName || null]; - this.buffer = ""; -}; - -Ember._RenderBuffer.prototype = { - - // The root view's element - _element: null, - - _hasElement: true, - - /** - An internal set used to de-dupe class names when `addClass()` is - used. After each call to `addClass()`, the `classes` property - will be updated. - - @private - @property elementClasses - @type Array - @default [] - */ - elementClasses: null, - - /** - Array of class names which will be applied in the class attribute. - - You can use `setClasses()` to set this property directly. If you - use `addClass()`, it will be maintained for you. - - @property classes - @type Array - @default [] - */ - classes: null, - - /** - The id in of the element, to be applied in the id attribute. - - You should not set this property yourself, rather, you should use - the `id()` method of `Ember.RenderBuffer`. - - @property elementId - @type String - @default null - */ - elementId: null, - - /** - A hash keyed on the name of the attribute and whose value will be - applied to that attribute. For example, if you wanted to apply a - `data-view="Foo.bar"` property to an element, you would set the - elementAttributes hash to `{'data-view':'Foo.bar'}`. - - You should not maintain this hash yourself, rather, you should use - the `attr()` method of `Ember.RenderBuffer`. - - @property elementAttributes - @type Hash - @default {} - */ - elementAttributes: null, - - /** - A hash keyed on the name of the properties and whose value will be - applied to that property. For example, if you wanted to apply a - `checked=true` property to an element, you would set the - elementProperties hash to `{'checked':true}`. - - You should not maintain this hash yourself, rather, you should use - the `prop()` method of `Ember.RenderBuffer`. - - @property elementProperties - @type Hash - @default {} - */ - elementProperties: null, - - /** - The tagname of the element an instance of `Ember.RenderBuffer` represents. - - Usually, this gets set as the first parameter to `Ember.RenderBuffer`. For - example, if you wanted to create a `p` tag, then you would call - - ```javascript - Ember.RenderBuffer('p') - ``` - - @property elementTag - @type String - @default null - */ - elementTag: null, - - /** - A hash keyed on the name of the style attribute and whose value will - be applied to that attribute. For example, if you wanted to apply a - `background-color:black;` style to an element, you would set the - elementStyle hash to `{'background-color':'black'}`. - - You should not maintain this hash yourself, rather, you should use - the `style()` method of `Ember.RenderBuffer`. - - @property elementStyle - @type Hash - @default {} - */ - elementStyle: null, - - /** - Nested `RenderBuffers` will set this to their parent `RenderBuffer` - instance. - - @property parentBuffer - @type Ember._RenderBuffer - */ - parentBuffer: null, - - /** - Adds a string of HTML to the `RenderBuffer`. - - @method push - @param {String} string HTML to push into the buffer - @chainable - */ - push: function(string) { - this.buffer += string; - return this; - }, - - /** - Adds a class to the buffer, which will be rendered to the class attribute. - - @method addClass - @param {String} className Class name to add to the buffer - @chainable - */ - addClass: function(className) { - // lazily create elementClasses - this.elementClasses = (this.elementClasses || new ClassSet()); - this.elementClasses.add(className); - this.classes = this.elementClasses.list; - - return this; - }, - - setClasses: function(classNames) { - this.elementClasses = null; - var len = classNames.length, i; - for (i = 0; i < len; i++) { - this.addClass(classNames[i]); - } - }, - - /** - Sets the elementID to be used for the element. - - @method id - @param {String} id - @chainable - */ - id: function(id) { - this.elementId = id; - return this; - }, - - // duck type attribute functionality like jQuery so a render buffer - // can be used like a jQuery object in attribute binding scenarios. - - /** - Adds an attribute which will be rendered to the element. - - @method attr - @param {String} name The name of the attribute - @param {String} value The value to add to the attribute - @chainable - @return {Ember.RenderBuffer|String} this or the current attribute value - */ - attr: function(name, value) { - var attributes = this.elementAttributes = (this.elementAttributes || {}); - - if (arguments.length === 1) { - return attributes[name]; - } else { - attributes[name] = value; - } - - return this; - }, - - /** - Remove an attribute from the list of attributes to render. - - @method removeAttr - @param {String} name The name of the attribute - @chainable - */ - removeAttr: function(name) { - var attributes = this.elementAttributes; - if (attributes) { delete attributes[name]; } - - return this; - }, - - /** - Adds a property which will be rendered to the element. - - @method prop - @param {String} name The name of the property - @param {String} value The value to add to the property - @chainable - @return {Ember.RenderBuffer|String} this or the current property value - */ - prop: function(name, value) { - var properties = this.elementProperties = (this.elementProperties || {}); - - if (arguments.length === 1) { - return properties[name]; - } else { - properties[name] = value; - } - - return this; - }, - - /** - Remove an property from the list of properties to render. - - @method removeProp - @param {String} name The name of the property - @chainable - */ - removeProp: function(name) { - var properties = this.elementProperties; - if (properties) { delete properties[name]; } - - return this; - }, - - /** - Adds a style to the style attribute which will be rendered to the element. - - @method style - @param {String} name Name of the style - @param {String} value - @chainable - */ - style: function(name, value) { - this.elementStyle = (this.elementStyle || {}); - - this.elementStyle[name] = value; - return this; - }, - - begin: function(tagName) { - this.tagNames.push(tagName || null); - return this; - }, - - pushOpeningTag: function() { - var tagName = this.currentTagName(); - if (!tagName) { return; } - - if (this._hasElement && !this._element && this.buffer.length === 0) { - this._element = this.generateElement(); - return; - } - - var buffer = this.buffer, - id = this.elementId, - classes = this.classes, - attrs = this.elementAttributes, - props = this.elementProperties, - style = this.elementStyle, - attr, prop; - - buffer += '<' + stripTagName(tagName); - - if (id) { - buffer += ' id="' + escapeAttribute(id) + '"'; - this.elementId = null; - } - if (classes) { - buffer += ' class="' + escapeAttribute(classes.join(' ')) + '"'; - this.classes = null; - this.elementClasses = null; - } - - if (style) { - buffer += ' style="'; - - for (prop in style) { - if (style.hasOwnProperty(prop)) { - buffer += prop + ':' + escapeAttribute(style[prop]) + ';'; - } - } - - buffer += '"'; - - this.elementStyle = null; - } - - if (attrs) { - for (attr in attrs) { - if (attrs.hasOwnProperty(attr)) { - buffer += ' ' + attr + '="' + escapeAttribute(attrs[attr]) + '"'; - } - } - - this.elementAttributes = null; - } - - if (props) { - for (prop in props) { - if (props.hasOwnProperty(prop)) { - var value = props[prop]; - if (value || typeof(value) === 'number') { - if (value === true) { - buffer += ' ' + prop + '="' + prop + '"'; - } else { - buffer += ' ' + prop + '="' + escapeAttribute(props[prop]) + '"'; - } - } - } - } - - this.elementProperties = null; - } - - buffer += '>'; - this.buffer = buffer; - }, - - pushClosingTag: function() { - var tagName = this.tagNames.pop(); - if (tagName) { this.buffer += ''; } - }, - - currentTagName: function() { - return this.tagNames[this.tagNames.length-1]; - }, - - generateElement: function() { - var tagName = this.tagNames.pop(), // pop since we don't need to close - id = this.elementId, - classes = this.classes, - attrs = this.elementAttributes, - props = this.elementProperties, - style = this.elementStyle, - styleBuffer = '', attr, prop, tagString; - - if (attrs && attrs.name && !canSetNameOnInputs) { - // IE allows passing a tag to createElement. See note on `canSetNameOnInputs` above as well. - tagString = '<'+stripTagName(tagName)+' name="'+escapeAttribute(attrs.name)+'">'; - } else { - tagString = tagName; - } - - var element = document.createElement(tagString), - $element = Ember.$(element); - - if (id) { - $element.attr('id', id); - this.elementId = null; - } - if (classes) { - $element.attr('class', classes.join(' ')); - this.classes = null; - this.elementClasses = null; - } - - if (style) { - for (prop in style) { - if (style.hasOwnProperty(prop)) { - styleBuffer += (prop + ':' + style[prop] + ';'); - } - } - - $element.attr('style', styleBuffer); - - this.elementStyle = null; - } - - if (attrs) { - for (attr in attrs) { - if (attrs.hasOwnProperty(attr)) { - $element.attr(attr, attrs[attr]); - } - } - - this.elementAttributes = null; - } - - if (props) { - for (prop in props) { - if (props.hasOwnProperty(prop)) { - $element.prop(prop, props[prop]); - } - } - - this.elementProperties = null; - } - - return element; - }, - - /** - @method element - @return {DOMElement} The element corresponding to the generated HTML - of this buffer - */ - element: function() { - var html = this.innerString(); - - if (html) { - this._element = Ember.ViewUtils.setInnerHTML(this._element, html); - } - - return this._element; - }, - - /** - Generates the HTML content for this buffer. - - @method string - @return {String} The generated HTML - */ - string: function() { - if (this._hasElement && this._element) { - // Firefox versions < 11 do not have support for element.outerHTML. - var thisElement = this.element(), outerHTML = thisElement.outerHTML; - if (typeof outerHTML === 'undefined') { - return Ember.$('
    ').append(thisElement).html(); - } - return outerHTML; - } else { - return this.innerString(); - } - }, - - innerString: function() { - return this.buffer; - } -}; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; - -/** - `Ember.EventDispatcher` handles delegating browser events to their - corresponding `Ember.Views.` For example, when you click on a view, - `Ember.EventDispatcher` ensures that that view's `mouseDown` method gets - called. - - @class EventDispatcher - @namespace Ember - @private - @extends Ember.Object -*/ -Ember.EventDispatcher = Ember.Object.extend({ - - /** - The set of events names (and associated handler function names) to be setup - and dispatched by the `EventDispatcher`. Custom events can added to this list at setup - time, generally via the `Ember.Application.customEvents` hash. Only override this - default set to prevent the EventDispatcher from listening on some events all together. - - This set will be modified by `setup` to also include any events added at that time. - - @property events - @type Object - */ - events: { - touchstart : 'touchStart', - touchmove : 'touchMove', - touchend : 'touchEnd', - touchcancel : 'touchCancel', - keydown : 'keyDown', - keyup : 'keyUp', - keypress : 'keyPress', - mousedown : 'mouseDown', - mouseup : 'mouseUp', - contextmenu : 'contextMenu', - click : 'click', - dblclick : 'doubleClick', - mousemove : 'mouseMove', - focusin : 'focusIn', - focusout : 'focusOut', - mouseenter : 'mouseEnter', - mouseleave : 'mouseLeave', - submit : 'submit', - input : 'input', - change : 'change', - dragstart : 'dragStart', - drag : 'drag', - dragenter : 'dragEnter', - dragleave : 'dragLeave', - dragover : 'dragOver', - drop : 'drop', - dragend : 'dragEnd' - }, - - /** - The root DOM element to which event listeners should be attached. Event - listeners will be attached to the document unless this is overridden. - - Can be specified as a DOMElement or a selector string. - - The default body is a string since this may be evaluated before document.body - exists in the DOM. - - @private - @property rootElement - @type DOMElement - @default 'body' - */ - rootElement: 'body', - - /** - Sets up event listeners for standard browser events. - - This will be called after the browser sends a `DOMContentReady` event. By - default, it will set up all of the listeners on the document body. If you - would like to register the listeners on a different element, set the event - dispatcher's `root` property. - - @private - @method setup - @param addedEvents {Hash} - */ - setup: function(addedEvents, rootElement) { - var event, events = get(this, 'events'); - - Ember.$.extend(events, addedEvents || {}); - - - if (!Ember.isNone(rootElement)) { - set(this, 'rootElement', rootElement); - } - - rootElement = Ember.$(get(this, 'rootElement')); - - Ember.assert(fmt('You cannot use the same root element (%@) multiple times in an Ember.Application', [rootElement.selector || rootElement[0].tagName]), !rootElement.is('.ember-application')); - Ember.assert('You cannot make a new Ember.Application using a root element that is a descendent of an existing Ember.Application', !rootElement.closest('.ember-application').length); - Ember.assert('You cannot make a new Ember.Application using a root element that is an ancestor of an existing Ember.Application', !rootElement.find('.ember-application').length); - - rootElement.addClass('ember-application'); - - Ember.assert('Unable to add "ember-application" class to rootElement. Make sure you set rootElement to the body or an element in the body.', rootElement.is('.ember-application')); - - for (event in events) { - if (events.hasOwnProperty(event)) { - this.setupHandler(rootElement, event, events[event]); - } - } - }, - - /** - Registers an event listener on the document. If the given event is - triggered, the provided event handler will be triggered on the target view. - - If the target view does not implement the event handler, or if the handler - returns `false`, the parent view will be called. The event will continue to - bubble to each successive parent view until it reaches the top. - - For example, to have the `mouseDown` method called on the target view when - a `mousedown` event is received from the browser, do the following: - - ```javascript - setupHandler('mousedown', 'mouseDown'); - ``` - - @private - @method setupHandler - @param {Element} rootElement - @param {String} event the browser-originated event to listen to - @param {String} eventName the name of the method to call on the view - */ - setupHandler: function(rootElement, event, eventName) { - var self = this; - - rootElement.on(event + '.ember', '.ember-view', function(evt, triggeringManager) { - var view = Ember.View.views[this.id], - result = true, manager = null; - - manager = self._findNearestEventManager(view, eventName); - - if (manager && manager !== triggeringManager) { - result = self._dispatchEvent(manager, evt, eventName, view); - } else if (view) { - result = self._bubbleEvent(view, evt, eventName); - } else { - evt.stopPropagation(); - } - - return result; - }); - - rootElement.on(event + '.ember', '[data-ember-action]', function(evt) { - var actionId = Ember.$(evt.currentTarget).attr('data-ember-action'), - action = Ember.Handlebars.ActionHelper.registeredActions[actionId]; - - // We have to check for action here since in some cases, jQuery will trigger - // an event on `removeChild` (i.e. focusout) after we've already torn down the - // action handlers for the view. - if (action && action.eventName === eventName) { - return action.handler(evt); - } - }); - }, - - _findNearestEventManager: function(view, eventName) { - var manager = null; - - while (view) { - manager = get(view, 'eventManager'); - if (manager && manager[eventName]) { break; } - - view = get(view, 'parentView'); - } - - return manager; - }, - - _dispatchEvent: function(object, evt, eventName, view) { - var result = true; - - var handler = object[eventName]; - if (Ember.typeOf(handler) === 'function') { - result = Ember.run(object, handler, evt, view); - // Do not preventDefault in eventManagers. - evt.stopPropagation(); - } - else { - result = this._bubbleEvent(view, evt, eventName); - } - - return result; - }, - - _bubbleEvent: function(view, evt, eventName) { - return Ember.run(view, view.handleEvent, eventName, evt); - }, - - destroy: function() { - var rootElement = get(this, 'rootElement'); - Ember.$(rootElement).off('.ember', '**').removeClass('ember-application'); - return this._super(); - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -// Add a new named queue for rendering views that happens -// after bindings have synced, and a queue for scheduling actions -// that that should occur after view rendering. -var queues = Ember.run.queues, - indexOf = Ember.ArrayPolyfills.indexOf; -queues.splice(indexOf.call(queues, 'actions')+1, 0, 'render', 'afterRender'); - -})(); - - - -(function() { - -})(); - - - -(function() { -var states = {}; - -/** -@module ember -@submodule ember-views -*/ - -var get = Ember.get, set = Ember.set, - guidFor = Ember.guidFor, - a_forEach = Ember.EnumerableUtils.forEach, - a_addObject = Ember.EnumerableUtils.addObject, - meta = Ember.meta, - defineProperty = Ember.defineProperty; - -function nullViewsBuffer(view) { - view.buffer = null; -} - -var childViewsProperty = Ember.computed(function() { - var childViews = this._childViews, ret = Ember.A(), view = this; - - a_forEach(childViews, function(view) { - var currentChildViews; - if (view.isVirtual) { - if (currentChildViews = get(view, 'childViews')) { - ret.pushObjects(currentChildViews); - } - } else { - ret.push(view); - } - }); - - ret.replace = function (idx, removedCount, addedViews) { - if (view instanceof Ember.ContainerView) { - Ember.deprecate("Manipulating an Ember.ContainerView through its childViews property is deprecated. Please use the ContainerView instance itself as an Ember.MutableArray."); - return view.replace(idx, removedCount, addedViews); - } - throw new Ember.Error("childViews is immutable"); - }; - - return ret; -}); - -Ember.warn("The VIEW_PRESERVES_CONTEXT flag has been removed and the functionality can no longer be disabled.", Ember.ENV.VIEW_PRESERVES_CONTEXT !== false); - -/** - Global hash of shared templates. This will automatically be populated - by the build tools so that you can store your Handlebars templates in - separate files that get loaded into JavaScript at buildtime. - - @property TEMPLATES - @for Ember - @type Hash -*/ -Ember.TEMPLATES = {}; - -/** - `Ember.CoreView` is an abstract class that exists to give view-like behavior - to both Ember's main view class `Ember.View` and other classes like - `Ember._SimpleMetamorphView` that don't need the fully functionaltiy of - `Ember.View`. - - Unless you have specific needs for `CoreView`, you will use `Ember.View` - in your applications. - - @class CoreView - @namespace Ember - @extends Ember.Object - @uses Ember.Evented - @uses Ember.ActionHandler -*/ - -Ember.CoreView = Ember.Object.extend(Ember.Evented, Ember.ActionHandler, { - isView: true, - - states: states, - - init: function() { - this._super(); - this.transitionTo('preRender'); - this._isVisible = get(this, 'isVisible'); - }, - - /** - If the view is currently inserted into the DOM of a parent view, this - property will point to the parent of the view. - - @property parentView - @type Ember.View - @default null - */ - parentView: Ember.computed('_parentView', function() { - var parent = this._parentView; - - if (parent && parent.isVirtual) { - return get(parent, 'parentView'); - } else { - return parent; - } - }), - - state: null, - - _parentView: null, - - // return the current view, not including virtual views - concreteView: Ember.computed('parentView', function() { - if (!this.isVirtual) { return this; } - else { return get(this, 'parentView'); } - }), - - instrumentName: 'core_view', - - instrumentDetails: function(hash) { - hash.object = this.toString(); - }, - - /** - Invoked by the view system when this view needs to produce an HTML - representation. This method will create a new render buffer, if needed, - then apply any default attributes, such as class names and visibility. - Finally, the `render()` method is invoked, which is responsible for - doing the bulk of the rendering. - - You should not need to override this method; instead, implement the - `template` property, or if you need more control, override the `render` - method. - - @method renderToBuffer - @param {Ember.RenderBuffer} buffer the render buffer. If no buffer is - passed, a default buffer, using the current view's `tagName`, will - be used. - @private - */ - renderToBuffer: function(parentBuffer, bufferOperation) { - var name = 'render.' + this.instrumentName, - details = {}; - - this.instrumentDetails(details); - - return Ember.instrument(name, details, function instrumentRenderToBuffer() { - return this._renderToBuffer(parentBuffer, bufferOperation); - }, this); - }, - - _renderToBuffer: function(parentBuffer, bufferOperation) { - // If this is the top-most view, start a new buffer. Otherwise, - // create a new buffer relative to the original using the - // provided buffer operation (for example, `insertAfter` will - // insert a new buffer after the "parent buffer"). - var tagName = this.tagName; - - if (tagName === null || tagName === undefined) { - tagName = 'div'; - } - - var buffer = this.buffer = parentBuffer && parentBuffer.begin(tagName) || Ember.RenderBuffer(tagName); - this.transitionTo('inBuffer', false); - - this.beforeRender(buffer); - this.render(buffer); - this.afterRender(buffer); - - return buffer; - }, - - /** - Override the default event firing from `Ember.Evented` to - also call methods with the given name. - - @method trigger - @param name {String} - @private - */ - trigger: function(name) { - this._super.apply(this, arguments); - var method = this[name]; - if (method) { - var args = [], i, l; - for (i = 1, l = arguments.length; i < l; i++) { - args.push(arguments[i]); - } - return method.apply(this, args); - } - }, - - deprecatedSendHandles: function(actionName) { - return !!this[actionName]; - }, - - deprecatedSend: function(actionName) { - var args = [].slice.call(arguments, 1); - Ember.assert('' + this + " has the action " + actionName + " but it is not a function", typeof this[actionName] === 'function'); - Ember.deprecate('Action handlers implemented directly on views are deprecated in favor of action handlers on an `actions` object ( action: `' + actionName + '` on ' + this + ')', false); - this[actionName].apply(this, args); - return; - }, - - has: function(name) { - return Ember.typeOf(this[name]) === 'function' || this._super(name); - }, - - destroy: function() { - var parent = this._parentView; - - if (!this._super()) { return; } - - // destroy the element -- this will avoid each child view destroying - // the element over and over again... - if (!this.removedFromDOM) { this.destroyElement(); } - - // remove from parent if found. Don't call removeFromParent, - // as removeFromParent will try to remove the element from - // the DOM again. - if (parent) { parent.removeChild(this); } - - this.transitionTo('destroying', false); - - return this; - }, - - clearRenderedChildren: Ember.K, - triggerRecursively: Ember.K, - invokeRecursively: Ember.K, - transitionTo: Ember.K, - destroyElement: Ember.K -}); - -var ViewCollection = Ember._ViewCollection = function(initialViews) { - var views = this.views = initialViews || []; - this.length = views.length; -}; - -ViewCollection.prototype = { - length: 0, - - trigger: function(eventName) { - var views = this.views, view; - for (var i = 0, l = views.length; i < l; i++) { - view = views[i]; - if (view.trigger) { view.trigger(eventName); } - } - }, - - triggerRecursively: function(eventName) { - var views = this.views; - for (var i = 0, l = views.length; i < l; i++) { - views[i].triggerRecursively(eventName); - } - }, - - invokeRecursively: function(fn) { - var views = this.views, view; - - for (var i = 0, l = views.length; i < l; i++) { - view = views[i]; - fn(view); - } - }, - - transitionTo: function(state, children) { - var views = this.views; - for (var i = 0, l = views.length; i < l; i++) { - views[i].transitionTo(state, children); - } - }, - - push: function() { - this.length += arguments.length; - var views = this.views; - return views.push.apply(views, arguments); - }, - - objectAt: function(idx) { - return this.views[idx]; - }, - - forEach: function(callback) { - var views = this.views; - return a_forEach(views, callback); - }, - - clear: function() { - this.length = 0; - this.views.length = 0; - } -}; - -var EMPTY_ARRAY = []; - -/** - `Ember.View` is the class in Ember responsible for encapsulating templates of - HTML content, combining templates with data to render as sections of a page's - DOM, and registering and responding to user-initiated events. - - ## HTML Tag - - The default HTML tag name used for a view's DOM representation is `div`. This - can be customized by setting the `tagName` property. The following view - class: - - ```javascript - ParagraphView = Ember.View.extend({ - tagName: 'em' - }); - ``` - - Would result in instances with the following HTML: - - ```html - - ``` - - ## HTML `class` Attribute - - The HTML `class` attribute of a view's tag can be set by providing a - `classNames` property that is set to an array of strings: - - ```javascript - MyView = Ember.View.extend({ - classNames: ['my-class', 'my-other-class'] - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    - ``` - - `class` attribute values can also be set by providing a `classNameBindings` - property set to an array of properties names for the view. The return value - of these properties will be added as part of the value for the view's `class` - attribute. These properties can be computed properties: - - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['propertyA', 'propertyB'], - propertyA: 'from-a', - propertyB: function() { - if (someLogic) { return 'from-b'; } - }.property() - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    - ``` - - If the value of a class name binding returns a boolean the property name - itself will be used as the class name if the property is true. The class name - will not be added if the value is `false` or `undefined`. - - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['hovered'], - hovered: true - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    - ``` - - When using boolean class name bindings you can supply a string value other - than the property name for use as the `class` HTML attribute by appending the - preferred value after a ":" character when defining the binding: - - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['awesome:so-very-cool'], - awesome: true - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    - ``` - - Boolean value class name bindings whose property names are in a - camelCase-style format will be converted to a dasherized format: - - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['isUrgent'], - isUrgent: true - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    - ``` - - Class name bindings can also refer to object values that are found by - traversing a path relative to the view itself: - - ```javascript - MyView = Ember.View.extend({ - classNameBindings: ['messages.empty'] - messages: Ember.Object.create({ - empty: true - }) - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    - ``` - - If you want to add a class name for a property which evaluates to true and - and a different class name if it evaluates to false, you can pass a binding - like this: - - ```javascript - // Applies 'enabled' class when isEnabled is true and 'disabled' when isEnabled is false - Ember.View.extend({ - classNameBindings: ['isEnabled:enabled:disabled'] - isEnabled: true - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    - ``` - - When isEnabled is `false`, the resulting HTML reprensentation looks like - this: - - ```html -
    - ``` - - This syntax offers the convenience to add a class if a property is `false`: - - ```javascript - // Applies no class when isEnabled is true and class 'disabled' when isEnabled is false - Ember.View.extend({ - classNameBindings: ['isEnabled::disabled'] - isEnabled: true - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    - ``` - - When the `isEnabled` property on the view is set to `false`, it will result - in view instances with an HTML representation of: - - ```html -
    - ``` - - Updates to the the value of a class name binding will result in automatic - update of the HTML `class` attribute in the view's rendered HTML - representation. If the value becomes `false` or `undefined` the class name - will be removed. - - Both `classNames` and `classNameBindings` are concatenated properties. See - [Ember.Object](/api/classes/Ember.Object.html) documentation for more - information about concatenated properties. - - ## HTML Attributes - - The HTML attribute section of a view's tag can be set by providing an - `attributeBindings` property set to an array of property names on the view. - The return value of these properties will be used as the value of the view's - HTML associated attribute: - - ```javascript - AnchorView = Ember.View.extend({ - tagName: 'a', - attributeBindings: ['href'], - href: 'http://google.com' - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html - - ``` - - If the return value of an `attributeBindings` monitored property is a boolean - the property will follow HTML's pattern of repeating the attribute's name as - its value: - - ```javascript - MyTextInput = Ember.View.extend({ - tagName: 'input', - attributeBindings: ['disabled'], - disabled: true - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html - - ``` - - `attributeBindings` can refer to computed properties: - - ```javascript - MyTextInput = Ember.View.extend({ - tagName: 'input', - attributeBindings: ['disabled'], - disabled: function() { - if (someLogic) { - return true; - } else { - return false; - } - }.property() - }); - ``` - - Updates to the the property of an attribute binding will result in automatic - update of the HTML attribute in the view's rendered HTML representation. - - `attributeBindings` is a concatenated property. See [Ember.Object](/api/classes/Ember.Object.html) - documentation for more information about concatenated properties. - - ## Templates - - The HTML contents of a view's rendered representation are determined by its - template. Templates can be any function that accepts an optional context - parameter and returns a string of HTML that will be inserted within the - view's tag. Most typically in Ember this function will be a compiled - `Ember.Handlebars` template. - - ```javascript - AView = Ember.View.extend({ - template: Ember.Handlebars.compile('I am the template') - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    I am the template
    - ``` - - Within an Ember application is more common to define a Handlebars templates as - part of a page: - - ```html - - ``` - - And associate it by name using a view's `templateName` property: - - ```javascript - AView = Ember.View.extend({ - templateName: 'some-template' - }); - ``` - - If you have nested resources, your Handlebars template will look like this: - - ```html - - ``` - - And `templateName` property: - - ```javascript - AView = Ember.View.extend({ - templateName: 'posts/new' - }); - ``` - - Using a value for `templateName` that does not have a Handlebars template - with a matching `data-template-name` attribute will throw an error. - - For views classes that may have a template later defined (e.g. as the block - portion of a `{{view}}` Handlebars helper call in another template or in - a subclass), you can provide a `defaultTemplate` property set to compiled - template function. If a template is not later provided for the view instance - the `defaultTemplate` value will be used: - - ```javascript - AView = Ember.View.extend({ - defaultTemplate: Ember.Handlebars.compile('I was the default'), - template: null, - templateName: null - }); - ``` - - Will result in instances with an HTML representation of: - - ```html -
    I was the default
    - ``` - - If a `template` or `templateName` is provided it will take precedence over - `defaultTemplate`: - - ```javascript - AView = Ember.View.extend({ - defaultTemplate: Ember.Handlebars.compile('I was the default') - }); - - aView = AView.create({ - template: Ember.Handlebars.compile('I was the template, not default') - }); - ``` - - Will result in the following HTML representation when rendered: - - ```html -
    I was the template, not default
    - ``` - - ## View Context - - The default context of the compiled template is the view's controller: - - ```javascript - AView = Ember.View.extend({ - template: Ember.Handlebars.compile('Hello {{excitedGreeting}}') - }); - - aController = Ember.Object.create({ - firstName: 'Barry', - excitedGreeting: function() { - return this.get("content.firstName") + "!!!" - }.property() - }); - - aView = AView.create({ - controller: aController, - }); - ``` - - Will result in an HTML representation of: - - ```html -
    Hello Barry!!!
    - ``` - - A context can also be explicitly supplied through the view's `context` - property. If the view has neither `context` nor `controller` properties, the - `parentView`'s context will be used. - - ## Layouts - - Views can have a secondary template that wraps their main template. Like - primary templates, layouts can be any function that accepts an optional - context parameter and returns a string of HTML that will be inserted inside - view's tag. Views whose HTML element is self closing (e.g. ``) - cannot have a layout and this property will be ignored. - - Most typically in Ember a layout will be a compiled `Ember.Handlebars` - template. - - A view's layout can be set directly with the `layout` property or reference - an existing Handlebars template by name with the `layoutName` property. - - A template used as a layout must contain a single use of the Handlebars - `{{yield}}` helper. The HTML contents of a view's rendered `template` will be - inserted at this location: - - ```javascript - AViewWithLayout = Ember.View.extend({ - layout: Ember.Handlebars.compile("
    {{yield}}
    ") - template: Ember.Handlebars.compile("I got wrapped"), - }); - ``` - - Will result in view instances with an HTML representation of: - - ```html -
    -
    - I got wrapped -
    -
    - ``` - - See [Ember.Handlebars.helpers.yield](/api/classes/Ember.Handlebars.helpers.html#method_yield) - for more information. - - ## Responding to Browser Events - - Views can respond to user-initiated events in one of three ways: method - implementation, through an event manager, and through `{{action}}` helper use - in their template or layout. - - ### Method Implementation - - Views can respond to user-initiated events by implementing a method that - matches the event name. A `jQuery.Event` object will be passed as the - argument to this method. - - ```javascript - AView = Ember.View.extend({ - click: function(event) { - // will be called when when an instance's - // rendered element is clicked - } - }); - ``` - - ### Event Managers - - Views can define an object as their `eventManager` property. This object can - then implement methods that match the desired event names. Matching events - that occur on the view's rendered HTML or the rendered HTML of any of its DOM - descendants will trigger this method. A `jQuery.Event` object will be passed - as the first argument to the method and an `Ember.View` object as the - second. The `Ember.View` will be the view whose rendered HTML was interacted - with. This may be the view with the `eventManager` property or one of its - descendent views. - - ```javascript - AView = Ember.View.extend({ - eventManager: Ember.Object.create({ - doubleClick: function(event, view) { - // will be called when when an instance's - // rendered element or any rendering - // of this views's descendent - // elements is clicked - } - }) - }); - ``` - - An event defined for an event manager takes precedence over events of the - same name handled through methods on the view. - - ```javascript - AView = Ember.View.extend({ - mouseEnter: function(event) { - // will never trigger. - }, - eventManager: Ember.Object.create({ - mouseEnter: function(event, view) { - // takes precedence over AView#mouseEnter - } - }) - }); - ``` - - Similarly a view's event manager will take precedence for events of any views - rendered as a descendent. A method name that matches an event name will not - be called if the view instance was rendered inside the HTML representation of - a view that has an `eventManager` property defined that handles events of the - name. Events not handled by the event manager will still trigger method calls - on the descendent. - - ```javascript - OuterView = Ember.View.extend({ - template: Ember.Handlebars.compile("outer {{#view InnerView}}inner{{/view}} outer"), - eventManager: Ember.Object.create({ - mouseEnter: function(event, view) { - // view might be instance of either - // OuterView or InnerView depending on - // where on the page the user interaction occured - } - }) - }); - - InnerView = Ember.View.extend({ - click: function(event) { - // will be called if rendered inside - // an OuterView because OuterView's - // eventManager doesn't handle click events - }, - mouseEnter: function(event) { - // will never be called if rendered inside - // an OuterView. - } - }); - ``` - - ### Handlebars `{{action}}` Helper - - See [Handlebars.helpers.action](/api/classes/Ember.Handlebars.helpers.html#method_action). - - ### Event Names - - All of the event handling approaches described above respond to the same set - of events. The names of the built-in events are listed below. (The hash of - built-in events exists in `Ember.EventDispatcher`.) Additional, custom events - can be registered by using `Ember.Application.customEvents`. - - Touch events: - - * `touchStart` - * `touchMove` - * `touchEnd` - * `touchCancel` - - Keyboard events - - * `keyDown` - * `keyUp` - * `keyPress` - - Mouse events - - * `mouseDown` - * `mouseUp` - * `contextMenu` - * `click` - * `doubleClick` - * `mouseMove` - * `focusIn` - * `focusOut` - * `mouseEnter` - * `mouseLeave` - - Form events: - - * `submit` - * `change` - * `focusIn` - * `focusOut` - * `input` - - HTML5 drag and drop events: - - * `dragStart` - * `drag` - * `dragEnter` - * `dragLeave` - * `drop` - * `dragEnd` - - ## Handlebars `{{view}}` Helper - - Other `Ember.View` instances can be included as part of a view's template by - using the `{{view}}` Handlebars helper. See [Ember.Handlebars.helpers.view](/api/classes/Ember.Handlebars.helpers.html#method_view) - for additional information. - - @class View - @namespace Ember - @extends Ember.CoreView -*/ -Ember.View = Ember.CoreView.extend({ - - concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'], - - /** - @property isView - @type Boolean - @default true - @static - */ - isView: true, - - // .......................................................... - // TEMPLATE SUPPORT - // - - /** - The name of the template to lookup if no template is provided. - - By default `Ember.View` will lookup a template with this name in - `Ember.TEMPLATES` (a shared global object). - - @property templateName - @type String - @default null - */ - templateName: null, - - /** - The name of the layout to lookup if no layout is provided. - - By default `Ember.View` will lookup a template with this name in - `Ember.TEMPLATES` (a shared global object). - - @property layoutName - @type String - @default null - */ - layoutName: null, - - /** - The template used to render the view. This should be a function that - accepts an optional context parameter and returns a string of HTML that - will be inserted into the DOM relative to its parent view. - - In general, you should set the `templateName` property instead of setting - the template yourself. - - @property template - @type Function - */ - template: Ember.computed('templateName', function(key, value) { - if (value !== undefined) { return value; } - - var templateName = get(this, 'templateName'), - template = this.templateForName(templateName, 'template'); - - Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); - - return template || get(this, 'defaultTemplate'); - }), - - /** - The controller managing this view. If this property is set, it will be - made available for use by the template. - - @property controller - @type Object - */ - controller: Ember.computed('_parentView', function(key) { - var parentView = get(this, '_parentView'); - return parentView ? get(parentView, 'controller') : null; - }), - - /** - A view may contain a layout. A layout is a regular template but - supersedes the `template` property during rendering. It is the - responsibility of the layout template to retrieve the `template` - property from the view (or alternatively, call `Handlebars.helpers.yield`, - `{{yield}}`) to render it in the correct location. - - This is useful for a view that has a shared wrapper, but which delegates - the rendering of the contents of the wrapper to the `template` property - on a subclass. - - @property layout - @type Function - */ - layout: Ember.computed(function(key) { - var layoutName = get(this, 'layoutName'), - layout = this.templateForName(layoutName, 'layout'); - - Ember.assert("You specified the layoutName " + layoutName + " for " + this + ", but it did not exist.", !layoutName || layout); - - return layout || get(this, 'defaultLayout'); - }).property('layoutName'), - - _yield: function(context, options) { - var template = get(this, 'template'); - if (template) { template(context, options); } - }, - - templateForName: function(name, type) { - if (!name) { return; } - Ember.assert("templateNames are not allowed to contain periods: "+name, name.indexOf('.') === -1); - - // the defaultContainer is deprecated - var container = this.container || (Ember.Container && Ember.Container.defaultContainer); - return container && container.lookup('template:' + name); - }, - - /** - The object from which templates should access properties. - - This object will be passed to the template function each time the render - method is called, but it is up to the individual function to decide what - to do with it. - - By default, this will be the view's controller. - - @property context - @type Object - */ - context: Ember.computed(function(key, value) { - if (arguments.length === 2) { - set(this, '_context', value); - return value; - } else { - return get(this, '_context'); - } - }).volatile(), - - /** - Private copy of the view's template context. This can be set directly - by Handlebars without triggering the observer that causes the view - to be re-rendered. - - The context of a view is looked up as follows: - - 1. Supplied context (usually by Handlebars) - 2. Specified controller - 3. `parentView`'s context (for a child of a ContainerView) - - The code in Handlebars that overrides the `_context` property first - checks to see whether the view has a specified controller. This is - something of a hack and should be revisited. - - @property _context - @private - */ - _context: Ember.computed(function(key) { - var parentView, controller; - - if (controller = get(this, 'controller')) { - return controller; - } - - parentView = this._parentView; - if (parentView) { - return get(parentView, '_context'); - } - - return null; - }), - - /** - If a value that affects template rendering changes, the view should be - re-rendered to reflect the new value. - - @method _contextDidChange - @private - */ - _contextDidChange: Ember.observer('context', function() { - this.rerender(); - }), - - /** - If `false`, the view will appear hidden in DOM. - - @property isVisible - @type Boolean - @default null - */ - isVisible: true, - - /** - Array of child views. You should never edit this array directly. - Instead, use `appendChild` and `removeFromParent`. - - @property childViews - @type Array - @default [] - @private - */ - childViews: childViewsProperty, - - _childViews: EMPTY_ARRAY, - - // When it's a virtual view, we need to notify the parent that their - // childViews will change. - _childViewsWillChange: Ember.beforeObserver('childViews', function() { - if (this.isVirtual) { - var parentView = get(this, 'parentView'); - if (parentView) { Ember.propertyWillChange(parentView, 'childViews'); } - } - }), - - // When it's a virtual view, we need to notify the parent that their - // childViews did change. - _childViewsDidChange: Ember.observer('childViews', function() { - if (this.isVirtual) { - var parentView = get(this, 'parentView'); - if (parentView) { Ember.propertyDidChange(parentView, 'childViews'); } - } - }), - - /** - Return the nearest ancestor that is an instance of the provided - class. - - @method nearestInstanceOf - @param {Class} klass Subclass of Ember.View (or Ember.View itself) - @return Ember.View - @deprecated - */ - nearestInstanceOf: function(klass) { - Ember.deprecate("nearestInstanceOf is deprecated and will be removed from future releases. Use nearestOfType."); - var view = get(this, 'parentView'); - - while (view) { - if (view instanceof klass) { return view; } - view = get(view, 'parentView'); - } - }, - - /** - Return the nearest ancestor that is an instance of the provided - class or mixin. - - @method nearestOfType - @param {Class,Mixin} klass Subclass of Ember.View (or Ember.View itself), - or an instance of Ember.Mixin. - @return Ember.View - */ - nearestOfType: function(klass) { - var view = get(this, 'parentView'), - isOfType = klass instanceof Ember.Mixin ? - function(view) { return klass.detect(view); } : - function(view) { return klass.detect(view.constructor); }; - - while (view) { - if (isOfType(view)) { return view; } - view = get(view, 'parentView'); - } - }, - - /** - Return the nearest ancestor that has a given property. - - @function nearestWithProperty - @param {String} property A property name - @return Ember.View - */ - nearestWithProperty: function(property) { - var view = get(this, 'parentView'); - - while (view) { - if (property in view) { return view; } - view = get(view, 'parentView'); - } - }, - - /** - Return the nearest ancestor whose parent is an instance of - `klass`. - - @method nearestChildOf - @param {Class} klass Subclass of Ember.View (or Ember.View itself) - @return Ember.View - */ - nearestChildOf: function(klass) { - var view = get(this, 'parentView'); - - while (view) { - if (get(view, 'parentView') instanceof klass) { return view; } - view = get(view, 'parentView'); - } - }, - - /** - When the parent view changes, recursively invalidate `controller` - - @method _parentViewDidChange - @private - */ - _parentViewDidChange: Ember.observer('_parentView', function() { - if (this.isDestroying) { return; } - - this.trigger('parentViewDidChange'); - - if (get(this, 'parentView.controller') && !get(this, 'controller')) { - this.notifyPropertyChange('controller'); - } - }), - - _controllerDidChange: Ember.observer('controller', function() { - if (this.isDestroying) { return; } - - this.rerender(); - - this.forEachChildView(function(view) { - view.propertyDidChange('controller'); - }); - }), - - cloneKeywords: function() { - var templateData = get(this, 'templateData'); - - var keywords = templateData ? Ember.copy(templateData.keywords) : {}; - set(keywords, 'view', get(this, 'concreteView')); - set(keywords, '_view', this); - set(keywords, 'controller', get(this, 'controller')); - - return keywords; - }, - - /** - Called on your view when it should push strings of HTML into a - `Ember.RenderBuffer`. Most users will want to override the `template` - or `templateName` properties instead of this method. - - By default, `Ember.View` will look for a function in the `template` - property and invoke it with the value of `context`. The value of - `context` will be the view's controller unless you override it. - - @method render - @param {Ember.RenderBuffer} buffer The render buffer - */ - render: function(buffer) { - // If this view has a layout, it is the responsibility of the - // the layout to render the view's template. Otherwise, render the template - // directly. - var template = get(this, 'layout') || get(this, 'template'); - - if (template) { - var context = get(this, 'context'); - var keywords = this.cloneKeywords(); - var output; - - var data = { - view: this, - buffer: buffer, - isRenderData: true, - keywords: keywords, - insideGroup: get(this, 'templateData.insideGroup') - }; - - // Invoke the template with the provided template context, which - // is the view's controller by default. A hash of data is also passed that provides - // the template with access to the view and render buffer. - - Ember.assert('template must be a function. Did you mean to call Ember.Handlebars.compile("...") or specify templateName instead?', typeof template === 'function'); - // The template should write directly to the render buffer instead - // of returning a string. - output = template(context, { data: data }); - - // If the template returned a string instead of writing to the buffer, - // push the string onto the buffer. - if (output !== undefined) { buffer.push(output); } - } - }, - - /** - Renders the view again. This will work regardless of whether the - view is already in the DOM or not. If the view is in the DOM, the - rendering process will be deferred to give bindings a chance - to synchronize. - - If children were added during the rendering process using `appendChild`, - `rerender` will remove them, because they will be added again - if needed by the next `render`. - - In general, if the display of your view changes, you should modify - the DOM element directly instead of manually calling `rerender`, which can - be slow. - - @method rerender - */ - rerender: function() { - return this.currentState.rerender(this); - }, - - clearRenderedChildren: function() { - var lengthBefore = this.lengthBeforeRender, - lengthAfter = this.lengthAfterRender; - - // If there were child views created during the last call to render(), - // remove them under the assumption that they will be re-created when - // we re-render. - - // VIEW-TODO: Unit test this path. - var childViews = this._childViews; - for (var i=lengthAfter-1; i>=lengthBefore; i--) { - if (childViews[i]) { childViews[i].destroy(); } - } - }, - - /** - Iterates over the view's `classNameBindings` array, inserts the value - of the specified property into the `classNames` array, then creates an - observer to update the view's element if the bound property ever changes - in the future. - - @method _applyClassNameBindings - @private - */ - _applyClassNameBindings: function(classBindings) { - var classNames = this.classNames, - elem, newClass, dasherizedClass; - - // Loop through all of the configured bindings. These will be either - // property names ('isUrgent') or property paths relative to the view - // ('content.isUrgent') - a_forEach(classBindings, function(binding) { - - Ember.assert("classNameBindings must not have spaces in them. Multiple class name bindings can be provided as elements of an array, e.g. ['foo', ':bar']", binding.indexOf(' ') === -1); - - // Variable in which the old class value is saved. The observer function - // closes over this variable, so it knows which string to remove when - // the property changes. - var oldClass; - // Extract just the property name from bindings like 'foo:bar' - var parsedPath = Ember.View._parsePropertyPath(binding); - - // Set up an observer on the context. If the property changes, toggle the - // class name. - var observer = function() { - // Get the current value of the property - newClass = this._classStringForProperty(binding); - elem = this.$(); - - // If we had previously added a class to the element, remove it. - if (oldClass) { - elem.removeClass(oldClass); - // Also remove from classNames so that if the view gets rerendered, - // the class doesn't get added back to the DOM. - classNames.removeObject(oldClass); - } - - // If necessary, add a new class. Make sure we keep track of it so - // it can be removed in the future. - if (newClass) { - elem.addClass(newClass); - oldClass = newClass; - } else { - oldClass = null; - } - }; - - // Get the class name for the property at its current value - dasherizedClass = this._classStringForProperty(binding); - - if (dasherizedClass) { - // Ensure that it gets into the classNames array - // so it is displayed when we render. - a_addObject(classNames, dasherizedClass); - - // Save a reference to the class name so we can remove it - // if the observer fires. Remember that this variable has - // been closed over by the observer. - oldClass = dasherizedClass; - } - - this.registerObserver(this, parsedPath.path, observer); - // Remove className so when the view is rerendered, - // the className is added based on binding reevaluation - this.one('willClearRender', function() { - if (oldClass) { - classNames.removeObject(oldClass); - oldClass = null; - } - }); - - }, this); - }, - - _unspecifiedAttributeBindings: null, - - /** - Iterates through the view's attribute bindings, sets up observers for each, - then applies the current value of the attributes to the passed render buffer. - - @method _applyAttributeBindings - @param {Ember.RenderBuffer} buffer - @private - */ - _applyAttributeBindings: function(buffer, attributeBindings) { - var attributeValue, - unspecifiedAttributeBindings = this._unspecifiedAttributeBindings = this._unspecifiedAttributeBindings || {}; - - a_forEach(attributeBindings, function(binding) { - var split = binding.split(':'), - property = split[0], - attributeName = split[1] || property; - - if (property in this) { - this._setupAttributeBindingObservation(property, attributeName); - - // Determine the current value and add it to the render buffer - // if necessary. - attributeValue = get(this, property); - Ember.View.applyAttributeBindings(buffer, attributeName, attributeValue); - } else { - unspecifiedAttributeBindings[property] = attributeName; - } - }, this); - - // Lazily setup setUnknownProperty after attributeBindings are initially applied - this.setUnknownProperty = this._setUnknownProperty; - }, - - _setupAttributeBindingObservation: function(property, attributeName) { - var attributeValue, elem; - - // Create an observer to add/remove/change the attribute if the - // JavaScript property changes. - var observer = function() { - elem = this.$(); - - attributeValue = get(this, property); - - Ember.View.applyAttributeBindings(elem, attributeName, attributeValue); - }; - - this.registerObserver(this, property, observer); - }, - - /** - We're using setUnknownProperty as a hook to setup attributeBinding observers for - properties that aren't defined on a view at initialization time. - - Note: setUnknownProperty will only be called once for each property. - - @method setUnknownProperty - @param key - @param value - @private - */ - setUnknownProperty: null, // Gets defined after initialization by _applyAttributeBindings - - _setUnknownProperty: function(key, value) { - var attributeName = this._unspecifiedAttributeBindings && this._unspecifiedAttributeBindings[key]; - if (attributeName) { - this._setupAttributeBindingObservation(key, attributeName); - } - - defineProperty(this, key); - return set(this, key, value); - }, - - /** - Given a property name, returns a dasherized version of that - property name if the property evaluates to a non-falsy value. - - For example, if the view has property `isUrgent` that evaluates to true, - passing `isUrgent` to this method will return `"is-urgent"`. - - @method _classStringForProperty - @param property - @private - */ - _classStringForProperty: function(property) { - var parsedPath = Ember.View._parsePropertyPath(property); - var path = parsedPath.path; - - var val = get(this, path); - if (val === undefined && Ember.isGlobalPath(path)) { - val = get(Ember.lookup, path); - } - - return Ember.View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); - }, - - // .......................................................... - // ELEMENT SUPPORT - // - - /** - Returns the current DOM element for the view. - - @property element - @type DOMElement - */ - element: Ember.computed('_parentView', function(key, value) { - if (value !== undefined) { - return this.currentState.setElement(this, value); - } else { - return this.currentState.getElement(this); - } - }), - - /** - Returns a jQuery object for this view's element. If you pass in a selector - string, this method will return a jQuery object, using the current element - as its buffer. - - For example, calling `view.$('li')` will return a jQuery object containing - all of the `li` elements inside the DOM element of this view. - - @method $ - @param {String} [selector] a jQuery-compatible selector string - @return {jQuery} the jQuery object for the DOM node - */ - $: function(sel) { - return this.currentState.$(this, sel); - }, - - mutateChildViews: function(callback) { - var childViews = this._childViews, - idx = childViews.length, - view; - - while(--idx >= 0) { - view = childViews[idx]; - callback(this, view, idx); - } - - return this; - }, - - forEachChildView: function(callback) { - var childViews = this._childViews; - - if (!childViews) { return this; } - - var len = childViews.length, - view, idx; - - for (idx = 0; idx < len; idx++) { - view = childViews[idx]; - callback(view); - } - - return this; - }, - - /** - Appends the view's element to the specified parent element. - - If the view does not have an HTML representation yet, `createElement()` - will be called automatically. - - Note that this method just schedules the view to be appended; the DOM - element will not be appended to the given element until all bindings have - finished synchronizing. - - This is not typically a function that you will need to call directly when - building your application. You might consider using `Ember.ContainerView` - instead. If you do need to use `appendTo`, be sure that the target element - you are providing is associated with an `Ember.Application` and does not - have an ancestor element that is associated with an Ember view. - - @method appendTo - @param {String|DOMElement|jQuery} A selector, element, HTML string, or jQuery object - @return {Ember.View} receiver - */ - appendTo: function(target) { - // Schedule the DOM element to be created and appended to the given - // element after bindings have synchronized. - this._insertElementLater(function() { - Ember.assert("You tried to append to (" + target + ") but that isn't in the DOM", Ember.$(target).length > 0); - Ember.assert("You cannot append to an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view')); - this.$().appendTo(target); - }); - - return this; - }, - - /** - Replaces the content of the specified parent element with this view's - element. If the view does not have an HTML representation yet, - `createElement()` will be called automatically. - - Note that this method just schedules the view to be appended; the DOM - element will not be appended to the given element until all bindings have - finished synchronizing - - @method replaceIn - @param {String|DOMElement|jQuery} target A selector, element, HTML string, or jQuery object - @return {Ember.View} received - */ - replaceIn: function(target) { - Ember.assert("You tried to replace in (" + target + ") but that isn't in the DOM", Ember.$(target).length > 0); - Ember.assert("You cannot replace an existing Ember.View. Consider using Ember.ContainerView instead.", !Ember.$(target).is('.ember-view') && !Ember.$(target).parents().is('.ember-view')); - - this._insertElementLater(function() { - Ember.$(target).empty(); - this.$().appendTo(target); - }); - - return this; - }, - - /** - Schedules a DOM operation to occur during the next render phase. This - ensures that all bindings have finished synchronizing before the view is - rendered. - - To use, pass a function that performs a DOM operation. - - Before your function is called, this view and all child views will receive - the `willInsertElement` event. After your function is invoked, this view - and all of its child views will receive the `didInsertElement` event. - - ```javascript - view._insertElementLater(function() { - this.createElement(); - this.$().appendTo('body'); - }); - ``` - - @method _insertElementLater - @param {Function} fn the function that inserts the element into the DOM - @private - */ - _insertElementLater: function(fn) { - this._scheduledInsert = Ember.run.scheduleOnce('render', this, '_insertElement', fn); - }, - - _insertElement: function (fn) { - this._scheduledInsert = null; - this.currentState.insertElement(this, fn); - }, - - /** - Appends the view's element to the document body. If the view does - not have an HTML representation yet, `createElement()` will be called - automatically. - - If your application uses the `rootElement` property, you must append - the view within that element. Rendering views outside of the `rootElement` - is not supported. - - Note that this method just schedules the view to be appended; the DOM - element will not be appended to the document body until all bindings have - finished synchronizing. - - @method append - @return {Ember.View} receiver - */ - append: function() { - return this.appendTo(document.body); - }, - - /** - Removes the view's element from the element to which it is attached. - - @method remove - @return {Ember.View} receiver - */ - remove: function() { - // What we should really do here is wait until the end of the run loop - // to determine if the element has been re-appended to a different - // element. - // In the interim, we will just re-render if that happens. It is more - // important than elements get garbage collected. - if (!this.removedFromDOM) { this.destroyElement(); } - this.invokeRecursively(function(view) { - if (view.clearRenderedChildren) { view.clearRenderedChildren(); } - }); - }, - - elementId: null, - - /** - Attempts to discover the element in the parent element. The default - implementation looks for an element with an ID of `elementId` (or the - view's guid if `elementId` is null). You can override this method to - provide your own form of lookup. For example, if you want to discover your - element using a CSS class name instead of an ID. - - @method findElementInParentElement - @param {DOMElement} parentElement The parent's DOM element - @return {DOMElement} The discovered element - */ - findElementInParentElement: function(parentElem) { - var id = "#" + this.elementId; - return Ember.$(id)[0] || Ember.$(id, parentElem)[0]; - }, - - /** - Creates a DOM representation of the view and all of its - child views by recursively calling the `render()` method. - - After the element has been created, `didInsertElement` will - be called on this view and all of its child views. - - @method createElement - @return {Ember.View} receiver - */ - createElement: function() { - if (get(this, 'element')) { return this; } - - var buffer = this.renderToBuffer(); - set(this, 'element', buffer.element()); - - return this; - }, - - /** - Called when a view is going to insert an element into the DOM. - - @event willInsertElement - */ - willInsertElement: Ember.K, - - /** - Called when the element of the view has been inserted into the DOM - or after the view was re-rendered. Override this function to do any - set up that requires an element in the document body. - - @event didInsertElement - */ - didInsertElement: Ember.K, - - /** - Called when the view is about to rerender, but before anything has - been torn down. This is a good opportunity to tear down any manual - observers you have installed based on the DOM state - - @event willClearRender - */ - willClearRender: Ember.K, - - /** - Run this callback on the current view (unless includeSelf is false) and recursively on child views. - - @method invokeRecursively - @param fn {Function} - @param includeSelf {Boolean} Includes itself if true. - @private - */ - invokeRecursively: function(fn, includeSelf) { - var childViews = (includeSelf === false) ? this._childViews : [this]; - var currentViews, view, currentChildViews; - - while (childViews.length) { - currentViews = childViews.slice(); - childViews = []; - - for (var i=0, l=currentViews.length; i` tag for views. - - @property tagName - @type String - @default null - */ - - // We leave this null by default so we can tell the difference between - // the default case and a user-specified tag. - tagName: null, - - /** - The WAI-ARIA role of the control represented by this view. For example, a - button may have a role of type 'button', or a pane may have a role of - type 'alertdialog'. This property is used by assistive software to help - visually challenged users navigate rich web applications. - - The full list of valid WAI-ARIA roles is available at: - [http://www.w3.org/TR/wai-aria/roles#roles_categorization](http://www.w3.org/TR/wai-aria/roles#roles_categorization) - - @property ariaRole - @type String - @default null - */ - ariaRole: null, - - /** - Standard CSS class names to apply to the view's outer element. This - property automatically inherits any class names defined by the view's - superclasses as well. - - @property classNames - @type Array - @default ['ember-view'] - */ - classNames: ['ember-view'], - - /** - A list of properties of the view to apply as class names. If the property - is a string value, the value of that string will be applied as a class - name. - - ```javascript - // Applies the 'high' class to the view element - Ember.View.extend({ - classNameBindings: ['priority'] - priority: 'high' - }); - ``` - - If the value of the property is a Boolean, the name of that property is - added as a dasherized class name. - - ```javascript - // Applies the 'is-urgent' class to the view element - Ember.View.extend({ - classNameBindings: ['isUrgent'] - isUrgent: true - }); - ``` - - If you would prefer to use a custom value instead of the dasherized - property name, you can pass a binding like this: - - ```javascript - // Applies the 'urgent' class to the view element - Ember.View.extend({ - classNameBindings: ['isUrgent:urgent'] - isUrgent: true - }); - ``` - - This list of properties is inherited from the view's superclasses as well. - - @property classNameBindings - @type Array - @default [] - */ - classNameBindings: EMPTY_ARRAY, - - /** - A list of properties of the view to apply as attributes. If the property is - a string value, the value of that string will be applied as the attribute. - - ```javascript - // Applies the type attribute to the element - // with the value "button", like
    - Ember.View.extend({ - attributeBindings: ['type'], - type: 'button' - }); - ``` - - If the value of the property is a Boolean, the name of that property is - added as an attribute. - - ```javascript - // Renders something like
    - Ember.View.extend({ - attributeBindings: ['enabled'], - enabled: true - }); - ``` - - @property attributeBindings - */ - attributeBindings: EMPTY_ARRAY, - - // ....................................................... - // CORE DISPLAY METHODS - // - - /** - Setup a view, but do not finish waking it up. - - * configure `childViews` - * register the view with the global views hash, which is used for event - dispatch - - @method init - @private - */ - init: function() { - this.elementId = this.elementId || guidFor(this); - - this._super(); - - // setup child views. be sure to clone the child views array first - this._childViews = this._childViews.slice(); - - Ember.assert("Only arrays are allowed for 'classNameBindings'", Ember.typeOf(this.classNameBindings) === 'array'); - this.classNameBindings = Ember.A(this.classNameBindings.slice()); - - Ember.assert("Only arrays are allowed for 'classNames'", Ember.typeOf(this.classNames) === 'array'); - this.classNames = Ember.A(this.classNames.slice()); - }, - - appendChild: function(view, options) { - return this.currentState.appendChild(this, view, options); - }, - - /** - Removes the child view from the parent view. - - @method removeChild - @param {Ember.View} view - @return {Ember.View} receiver - */ - removeChild: function(view) { - // If we're destroying, the entire subtree will be - // freed, and the DOM will be handled separately, - // so no need to mess with childViews. - if (this.isDestroying) { return; } - - // update parent node - set(view, '_parentView', null); - - // remove view from childViews array. - var childViews = this._childViews; - - Ember.EnumerableUtils.removeObject(childViews, view); - - this.propertyDidChange('childViews'); // HUH?! what happened to will change? - - return this; - }, - - /** - Removes all children from the `parentView`. - - @method removeAllChildren - @return {Ember.View} receiver - */ - removeAllChildren: function() { - return this.mutateChildViews(function(parentView, view) { - parentView.removeChild(view); - }); - }, - - destroyAllChildren: function() { - return this.mutateChildViews(function(parentView, view) { - view.destroy(); - }); - }, - - /** - Removes the view from its `parentView`, if one is found. Otherwise - does nothing. - - @method removeFromParent - @return {Ember.View} receiver - */ - removeFromParent: function() { - var parent = this._parentView; - - // Remove DOM element from parent - this.remove(); - - if (parent) { parent.removeChild(this); } - return this; - }, - - /** - You must call `destroy` on a view to destroy the view (and all of its - child views). This will remove the view from any parent node, then make - sure that the DOM element managed by the view can be released by the - memory manager. - - @method destroy - */ - destroy: function() { - var childViews = this._childViews, - // get parentView before calling super because it'll be destroyed - nonVirtualParentView = get(this, 'parentView'), - viewName = this.viewName, - childLen, i; - - if (!this._super()) { return; } - - childLen = childViews.length; - for (i=childLen-1; i>=0; i--) { - childViews[i].removedFromDOM = true; - } - - // remove from non-virtual parent view if viewName was specified - if (viewName && nonVirtualParentView) { - nonVirtualParentView.set(viewName, null); - } - - childLen = childViews.length; - for (i=childLen-1; i>=0; i--) { - childViews[i].destroy(); - } - - return this; - }, - - /** - Instantiates a view to be added to the childViews array during view - initialization. You generally will not call this method directly unless - you are overriding `createChildViews()`. Note that this method will - automatically configure the correct settings on the new view instance to - act as a child of the parent. - - @method createChildView - @param {Class|String} viewClass - @param {Hash} [attrs] Attributes to add - @return {Ember.View} new instance - */ - createChildView: function(view, attrs) { - if (!view) { - throw new TypeError("createChildViews first argument must exist"); - } - - if (view.isView && view._parentView === this && view.container === this.container) { - return view; - } - - attrs = attrs || {}; - attrs._parentView = this; - - if (Ember.CoreView.detect(view)) { - attrs.templateData = attrs.templateData || get(this, 'templateData'); - - attrs.container = this.container; - view = view.create(attrs); - - // don't set the property on a virtual view, as they are invisible to - // consumers of the view API - if (view.viewName) { - set(get(this, 'concreteView'), view.viewName, view); - } - } else if ('string' === typeof view) { - var fullName = 'view:' + view; - var View = this.container.lookupFactory(fullName); - - Ember.assert("Could not find view: '" + fullName + "'", !!View); - - attrs.templateData = get(this, 'templateData'); - view = View.create(attrs); - } else { - Ember.assert('You must pass instance or subclass of View', view.isView); - attrs.container = this.container; - - if (!get(view, 'templateData')) { - attrs.templateData = get(this, 'templateData'); - } - - Ember.setProperties(view, attrs); - - } - - return view; - }, - - becameVisible: Ember.K, - becameHidden: Ember.K, - - /** - When the view's `isVisible` property changes, toggle the visibility - element of the actual DOM element. - - @method _isVisibleDidChange - @private - */ - _isVisibleDidChange: Ember.observer('isVisible', function() { - if (this._isVisible === get(this, 'isVisible')) { return ; } - Ember.run.scheduleOnce('render', this, this._toggleVisibility); - }), - - _toggleVisibility: function() { - var $el = this.$(); - if (!$el) { return; } - - var isVisible = get(this, 'isVisible'); - - if (this._isVisible === isVisible) { return ; } - - $el.toggle(isVisible); - - this._isVisible = isVisible; - - if (this._isAncestorHidden()) { return; } - - if (isVisible) { - this._notifyBecameVisible(); - } else { - this._notifyBecameHidden(); - } - }, - - _notifyBecameVisible: function() { - this.trigger('becameVisible'); - - this.forEachChildView(function(view) { - var isVisible = get(view, 'isVisible'); - - if (isVisible || isVisible === null) { - view._notifyBecameVisible(); - } - }); - }, - - _notifyBecameHidden: function() { - this.trigger('becameHidden'); - this.forEachChildView(function(view) { - var isVisible = get(view, 'isVisible'); - - if (isVisible || isVisible === null) { - view._notifyBecameHidden(); - } - }); - }, - - _isAncestorHidden: function() { - var parent = get(this, 'parentView'); - - while (parent) { - if (get(parent, 'isVisible') === false) { return true; } - - parent = get(parent, 'parentView'); - } - - return false; - }, - - clearBuffer: function() { - this.invokeRecursively(nullViewsBuffer); - }, - - transitionTo: function(state, children) { - var priorState = this.currentState, - currentState = this.currentState = this.states[state]; - this.state = state; - - if (priorState && priorState.exit) { priorState.exit(this); } - if (currentState.enter) { currentState.enter(this); } - if (state === 'inDOM') { delete Ember.meta(this).cache.element; } - - if (children !== false) { - this.forEachChildView(function(view) { - view.transitionTo(state); - }); - } - }, - - // ....................................................... - // EVENT HANDLING - // - - /** - Handle events from `Ember.EventDispatcher` - - @method handleEvent - @param eventName {String} - @param evt {Event} - @private - */ - handleEvent: function(eventName, evt) { - return this.currentState.handleEvent(this, eventName, evt); - }, - - registerObserver: function(root, path, target, observer) { - if (!observer && 'function' === typeof target) { - observer = target; - target = null; - } - - if (!root || typeof root !== 'object') { - return; - } - - var view = this, - stateCheckedObserver = function() { - view.currentState.invokeObserver(this, observer); - }, - scheduledObserver = function() { - Ember.run.scheduleOnce('render', this, stateCheckedObserver); - }; - - Ember.addObserver(root, path, target, scheduledObserver); - - this.one('willClearRender', function() { - Ember.removeObserver(root, path, target, scheduledObserver); - }); - } - -}); - -/* - Describe how the specified actions should behave in the various - states that a view can exist in. Possible states: - - * preRender: when a view is first instantiated, and after its - element was destroyed, it is in the preRender state - * inBuffer: once a view has been rendered, but before it has - been inserted into the DOM, it is in the inBuffer state - * hasElement: the DOM representation of the view is created, - and is ready to be inserted - * inDOM: once a view has been inserted into the DOM it is in - the inDOM state. A view spends the vast majority of its - existence in this state. - * destroyed: once a view has been destroyed (using the destroy - method), it is in this state. No further actions can be invoked - on a destroyed view. -*/ - - // in the destroyed state, everything is illegal - - // before rendering has begun, all legal manipulations are noops. - - // inside the buffer, legal manipulations are done on the buffer - - // once the view has been inserted into the DOM, legal manipulations - // are done on the DOM element. - -function notifyMutationListeners() { - Ember.run.once(Ember.View, 'notifyMutationListeners'); -} - -var DOMManager = { - prepend: function(view, html) { - view.$().prepend(html); - notifyMutationListeners(); - }, - - after: function(view, html) { - view.$().after(html); - notifyMutationListeners(); - }, - - html: function(view, html) { - view.$().html(html); - notifyMutationListeners(); - }, - - replace: function(view) { - var element = get(view, 'element'); - - set(view, 'element', null); - - view._insertElementLater(function() { - Ember.$(element).replaceWith(get(view, 'element')); - notifyMutationListeners(); - }); - }, - - remove: function(view) { - view.$().remove(); - notifyMutationListeners(); - }, - - empty: function(view) { - view.$().empty(); - notifyMutationListeners(); - } -}; - -Ember.View.reopen({ - domManager: DOMManager -}); - -Ember.View.reopenClass({ - - /** - Parse a path and return an object which holds the parsed properties. - - For example a path like "content.isEnabled:enabled:disabled" will return the - following object: - - ```javascript - { - path: "content.isEnabled", - className: "enabled", - falsyClassName: "disabled", - classNames: ":enabled:disabled" - } - ``` - - @method _parsePropertyPath - @static - @private - */ - _parsePropertyPath: function(path) { - var split = path.split(':'), - propertyPath = split[0], - classNames = "", - className, - falsyClassName; - - // check if the property is defined as prop:class or prop:trueClass:falseClass - if (split.length > 1) { - className = split[1]; - if (split.length === 3) { falsyClassName = split[2]; } - - classNames = ':' + className; - if (falsyClassName) { classNames += ":" + falsyClassName; } - } - - return { - path: propertyPath, - classNames: classNames, - className: (className === '') ? undefined : className, - falsyClassName: falsyClassName - }; - }, - - /** - Get the class name for a given value, based on the path, optional - `className` and optional `falsyClassName`. - - - if a `className` or `falsyClassName` has been specified: - - if the value is truthy and `className` has been specified, - `className` is returned - - if the value is falsy and `falsyClassName` has been specified, - `falsyClassName` is returned - - otherwise `null` is returned - - if the value is `true`, the dasherized last part of the supplied path - is returned - - if the value is not `false`, `undefined` or `null`, the `value` - is returned - - if none of the above rules apply, `null` is returned - - @method _classStringForValue - @param path - @param val - @param className - @param falsyClassName - @static - @private - */ - _classStringForValue: function(path, val, className, falsyClassName) { - // When using the colon syntax, evaluate the truthiness or falsiness - // of the value to determine which className to return - if (className || falsyClassName) { - if (className && !!val) { - return className; - - } else if (falsyClassName && !val) { - return falsyClassName; - - } else { - return null; - } - - // If value is a Boolean and true, return the dasherized property - // name. - } else if (val === true) { - // Normalize property path to be suitable for use - // as a class name. For exaple, content.foo.barBaz - // becomes bar-baz. - var parts = path.split('.'); - return Ember.String.dasherize(parts[parts.length-1]); - - // If the value is not false, undefined, or null, return the current - // value of the property. - } else if (val !== false && val != null) { - return val; - - // Nothing to display. Return null so that the old class is removed - // but no new class is added. - } else { - return null; - } - } -}); - -var mutation = Ember.Object.extend(Ember.Evented).create(); - -Ember.View.addMutationListener = function(callback) { - mutation.on('change', callback); -}; - -Ember.View.removeMutationListener = function(callback) { - mutation.off('change', callback); -}; - -Ember.View.notifyMutationListeners = function() { - mutation.trigger('change'); -}; - -/** - Global views hash - - @property views - @static - @type Hash -*/ -Ember.View.views = {}; - -// If someone overrides the child views computed property when -// defining their class, we want to be able to process the user's -// supplied childViews and then restore the original computed property -// at view initialization time. This happens in Ember.ContainerView's init -// method. -Ember.View.childViewsProperty = childViewsProperty; - -Ember.View.applyAttributeBindings = function(elem, name, value) { - var type = Ember.typeOf(value); - - // if this changes, also change the logic in ember-handlebars/lib/helpers/binding.js - if (name !== 'value' && (type === 'string' || (type === 'number' && !isNaN(value)))) { - if (value !== elem.attr(name)) { - elem.attr(name, value); - } - } else if (name === 'value' || type === 'boolean') { - if (Ember.isNone(value) || value === false) { - // `null`, `undefined` or `false` should remove attribute - elem.removeAttr(name); - elem.prop(name, ''); - } else if (value !== elem.prop(name)) { - // value should always be properties - elem.prop(name, value); - } - } else if (!value) { - elem.removeAttr(name); - } -}; - -Ember.View.states = states; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -var get = Ember.get, set = Ember.set; - -Ember.View.states._default = { - // appendChild is only legal while rendering the buffer. - appendChild: function() { - throw "You can't use appendChild outside of the rendering process"; - }, - - $: function() { - return undefined; - }, - - getElement: function() { - return null; - }, - - // Handle events from `Ember.EventDispatcher` - handleEvent: function() { - return true; // continue event propagation - }, - - destroyElement: function(view) { - set(view, 'element', null); - if (view._scheduledInsert) { - Ember.run.cancel(view._scheduledInsert); - view._scheduledInsert = null; - } - return view; - }, - - renderToBufferIfNeeded: function () { - return false; - }, - - rerender: Ember.K, - invokeObserver: Ember.K -}; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -var preRender = Ember.View.states.preRender = Ember.create(Ember.View.states._default); - -Ember.merge(preRender, { - // a view leaves the preRender state once its element has been - // created (createElement). - insertElement: function(view, fn) { - view.createElement(); - var viewCollection = view.viewHierarchyCollection(); - - viewCollection.trigger('willInsertElement'); - - fn.call(view); - - // We transition to `inDOM` if the element exists in the DOM - var element = view.get('element'); - if (document.body.contains(element)) { - viewCollection.transitionTo('inDOM', false); - viewCollection.trigger('didInsertElement'); - } - }, - - renderToBufferIfNeeded: function(view, buffer) { - view.renderToBuffer(buffer); - return true; - }, - - empty: Ember.K, - - setElement: function(view, value) { - if (value !== null) { - view.transitionTo('hasElement'); - } - return value; - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -var get = Ember.get, set = Ember.set; - -var inBuffer = Ember.View.states.inBuffer = Ember.create(Ember.View.states._default); - -Ember.merge(inBuffer, { - $: function(view, sel) { - // if we don't have an element yet, someone calling this.$() is - // trying to update an element that isn't in the DOM. Instead, - // rerender the view to allow the render method to reflect the - // changes. - view.rerender(); - return Ember.$(); - }, - - // when a view is rendered in a buffer, rerendering it simply - // replaces the existing buffer with a new one - rerender: function(view) { - throw new Ember.Error("Something you did caused a view to re-render after it rendered but before it was inserted into the DOM."); - }, - - // when a view is rendered in a buffer, appending a child - // view will render that view and append the resulting - // buffer into its buffer. - appendChild: function(view, childView, options) { - var buffer = view.buffer, _childViews = view._childViews; - - childView = view.createChildView(childView, options); - if (!_childViews.length) { _childViews = view._childViews = _childViews.slice(); } - _childViews.push(childView); - - childView.renderToBuffer(buffer); - - view.propertyDidChange('childViews'); - - return childView; - }, - - // when a view is rendered in a buffer, destroying the - // element will simply destroy the buffer and put the - // state back into the preRender state. - destroyElement: function(view) { - view.clearBuffer(); - var viewCollection = view._notifyWillDestroyElement(); - viewCollection.transitionTo('preRender', false); - - return view; - }, - - empty: function() { - Ember.assert("Emptying a view in the inBuffer state is not allowed and " + - "should not happen under normal circumstances. Most likely " + - "there is a bug in your application. This may be due to " + - "excessive property change notifications."); - }, - - renderToBufferIfNeeded: function (view, buffer) { - return false; - }, - - // It should be impossible for a rendered view to be scheduled for - // insertion. - insertElement: function() { - throw "You can't insert an element that has already been rendered"; - }, - - setElement: function(view, value) { - if (value === null) { - view.transitionTo('preRender'); - } else { - view.clearBuffer(); - view.transitionTo('hasElement'); - } - - return value; - }, - - invokeObserver: function(target, observer) { - observer.call(target); - } -}); - - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -var get = Ember.get, set = Ember.set; - -var hasElement = Ember.View.states.hasElement = Ember.create(Ember.View.states._default); - -Ember.merge(hasElement, { - $: function(view, sel) { - var elem = get(view, 'element'); - return sel ? Ember.$(sel, elem) : Ember.$(elem); - }, - - getElement: function(view) { - var parent = get(view, 'parentView'); - if (parent) { parent = get(parent, 'element'); } - if (parent) { return view.findElementInParentElement(parent); } - return Ember.$("#" + get(view, 'elementId'))[0]; - }, - - setElement: function(view, value) { - if (value === null) { - view.transitionTo('preRender'); - } else { - throw "You cannot set an element to a non-null value when the element is already in the DOM."; - } - - return value; - }, - - // once the view has been inserted into the DOM, rerendering is - // deferred to allow bindings to synchronize. - rerender: function(view) { - view.triggerRecursively('willClearRender'); - - view.clearRenderedChildren(); - - view.domManager.replace(view); - return view; - }, - - // once the view is already in the DOM, destroying it removes it - // from the DOM, nukes its element, and puts it back into the - // preRender state if inDOM. - - destroyElement: function(view) { - view._notifyWillDestroyElement(); - view.domManager.remove(view); - set(view, 'element', null); - if (view._scheduledInsert) { - Ember.run.cancel(view._scheduledInsert); - view._scheduledInsert = null; - } - return view; - }, - - empty: function(view) { - var _childViews = view._childViews, len, idx; - if (_childViews) { - len = _childViews.length; - for (idx = 0; idx < len; idx++) { - _childViews[idx]._notifyWillDestroyElement(); - } - } - view.domManager.empty(view); - }, - - // Handle events from `Ember.EventDispatcher` - handleEvent: function(view, eventName, evt) { - if (view.has(eventName)) { - // Handler should be able to re-dispatch events, so we don't - // preventDefault or stopPropagation. - return view.trigger(eventName, evt); - } else { - return true; // continue event propagation - } - }, - - invokeObserver: function(target, observer) { - observer.call(target); - } -}); -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -var hasElement = Ember.View.states.hasElement; -var inDOM = Ember.View.states.inDOM = Ember.create(hasElement); - -Ember.merge(inDOM, { - enter: function(view) { - // Register the view for event handling. This hash is used by - // Ember.EventDispatcher to dispatch incoming events. - if (!view.isVirtual) { - Ember.assert("Attempted to register a view with an id already in use: "+view.elementId, !Ember.View.views[view.elementId]); - Ember.View.views[view.elementId] = view; - } - - view.addBeforeObserver('elementId', function() { - throw new Ember.Error("Changing a view's elementId after creation is not allowed"); - }); - }, - - exit: function(view) { - if (!this.isVirtual) delete Ember.View.views[view.elementId]; - }, - - insertElement: function(view, fn) { - throw "You can't insert an element into the DOM that has already been inserted"; - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -var destroyingError = "You can't call %@ on a view being destroyed", fmt = Ember.String.fmt; - -var destroying = Ember.View.states.destroying = Ember.create(Ember.View.states._default); - -Ember.merge(destroying, { - appendChild: function() { - throw fmt(destroyingError, ['appendChild']); - }, - rerender: function() { - throw fmt(destroyingError, ['rerender']); - }, - destroyElement: function() { - throw fmt(destroyingError, ['destroyElement']); - }, - empty: function() { - throw fmt(destroyingError, ['empty']); - }, - - setElement: function() { - throw fmt(destroyingError, ["set('element', ...)"]); - }, - - renderToBufferIfNeeded: function() { - return false; - }, - - // Since element insertion is scheduled, don't do anything if - // the view has been destroyed between scheduling and execution - insertElement: Ember.K -}); - - -})(); - - - -(function() { -Ember.View.cloneStates = function(from) { - var into = {}; - - into._default = {}; - into.preRender = Ember.create(into._default); - into.destroying = Ember.create(into._default); - into.inBuffer = Ember.create(into._default); - into.hasElement = Ember.create(into._default); - into.inDOM = Ember.create(into.hasElement); - - for (var stateName in from) { - if (!from.hasOwnProperty(stateName)) { continue; } - Ember.merge(into[stateName], from[stateName]); - } - - return into; -}; - -})(); - - - -(function() { -var states = Ember.View.cloneStates(Ember.View.states); - -/** -@module ember -@submodule ember-views -*/ - -var get = Ember.get, set = Ember.set; -var forEach = Ember.EnumerableUtils.forEach; -var ViewCollection = Ember._ViewCollection; - -/** - A `ContainerView` is an `Ember.View` subclass that implements `Ember.MutableArray` - allowing programmatic management of its child views. - - ## Setting Initial Child Views - - The initial array of child views can be set in one of two ways. You can - provide a `childViews` property at creation time that contains instance of - `Ember.View`: - - ```javascript - aContainer = Ember.ContainerView.create({ - childViews: [Ember.View.create(), Ember.View.create()] - }); - ``` - - You can also provide a list of property names whose values are instances of - `Ember.View`: - - ```javascript - aContainer = Ember.ContainerView.create({ - childViews: ['aView', 'bView', 'cView'], - aView: Ember.View.create(), - bView: Ember.View.create(), - cView: Ember.View.create() - }); - ``` - - The two strategies can be combined: - - ```javascript - aContainer = Ember.ContainerView.create({ - childViews: ['aView', Ember.View.create()], - aView: Ember.View.create() - }); - ``` - - Each child view's rendering will be inserted into the container's rendered - HTML in the same order as its position in the `childViews` property. - - ## Adding and Removing Child Views - - The container view implements `Ember.MutableArray` allowing programmatic management of its child views. - - To remove a view, pass that view into a `removeObject` call on the container view. - - Given an empty `` the following code - - ```javascript - aContainer = Ember.ContainerView.create({ - classNames: ['the-container'], - childViews: ['aView', 'bView'], - aView: Ember.View.create({ - template: Ember.Handlebars.compile("A") - }), - bView: Ember.View.create({ - template: Ember.Handlebars.compile("B") - }) - }); - - aContainer.appendTo('body'); - ``` - - Results in the HTML - - ```html -
    -
    A
    -
    B
    -
    - ``` - - Removing a view - - ```javascript - aContainer.toArray(); // [aContainer.aView, aContainer.bView] - aContainer.removeObject(aContainer.get('bView')); - aContainer.toArray(); // [aContainer.aView] - ``` - - Will result in the following HTML - - ```html -
    -
    A
    -
    - ``` - - Similarly, adding a child view is accomplished by adding `Ember.View` instances to the - container view. - - Given an empty `` the following code - - ```javascript - aContainer = Ember.ContainerView.create({ - classNames: ['the-container'], - childViews: ['aView', 'bView'], - aView: Ember.View.create({ - template: Ember.Handlebars.compile("A") - }), - bView: Ember.View.create({ - template: Ember.Handlebars.compile("B") - }) - }); - - aContainer.appendTo('body'); - ``` - - Results in the HTML - - ```html -
    -
    A
    -
    B
    -
    - ``` - - Adding a view - - ```javascript - AnotherViewClass = Ember.View.extend({ - template: Ember.Handlebars.compile("Another view") - }); - - aContainer.toArray(); // [aContainer.aView, aContainer.bView] - aContainer.pushObject(AnotherViewClass.create()); - aContainer.toArray(); // [aContainer.aView, aContainer.bView, ] - ``` - - Will result in the following HTML - - ```html -
    -
    A
    -
    B
    -
    Another view
    -
    - ``` - - ## Templates and Layout - - A `template`, `templateName`, `defaultTemplate`, `layout`, `layoutName` or - `defaultLayout` property on a container view will not result in the template - or layout being rendered. The HTML contents of a `Ember.ContainerView`'s DOM - representation will only be the rendered HTML of its child views. - - @class ContainerView - @namespace Ember - @extends Ember.View -*/ -Ember.ContainerView = Ember.View.extend(Ember.MutableArray, { - states: states, - - init: function() { - this._super(); - - var childViews = get(this, 'childViews'); - - // redefine view's childViews property that was obliterated - Ember.defineProperty(this, 'childViews', Ember.View.childViewsProperty); - - var _childViews = this._childViews; - - forEach(childViews, function(viewName, idx) { - var view; - - if ('string' === typeof viewName) { - view = get(this, viewName); - view = this.createChildView(view); - set(this, viewName, view); - } else { - view = this.createChildView(viewName); - } - - _childViews[idx] = view; - }, this); - - var currentView = get(this, 'currentView'); - if (currentView) { - if (!_childViews.length) { _childViews = this._childViews = this._childViews.slice(); } - _childViews.push(this.createChildView(currentView)); - } - }, - - replace: function(idx, removedCount, addedViews) { - var addedCount = addedViews ? get(addedViews, 'length') : 0; - var self = this; - Ember.assert("You can't add a child to a container that is already a child of another view", Ember.A(addedViews).every(function(item) { return !get(item, '_parentView') || get(item, '_parentView') === self; })); - - this.arrayContentWillChange(idx, removedCount, addedCount); - this.childViewsWillChange(this._childViews, idx, removedCount); - - if (addedCount === 0) { - this._childViews.splice(idx, removedCount) ; - } else { - var args = [idx, removedCount].concat(addedViews); - if (addedViews.length && !this._childViews.length) { this._childViews = this._childViews.slice(); } - this._childViews.splice.apply(this._childViews, args); - } - - this.arrayContentDidChange(idx, removedCount, addedCount); - this.childViewsDidChange(this._childViews, idx, removedCount, addedCount); - - return this; - }, - - objectAt: function(idx) { - return this._childViews[idx]; - }, - - length: Ember.computed(function () { - return this._childViews.length; - }).volatile(), - - /** - Instructs each child view to render to the passed render buffer. - - @private - @method render - @param {Ember.RenderBuffer} buffer the buffer to render to - */ - render: function(buffer) { - this.forEachChildView(function(view) { - view.renderToBuffer(buffer); - }); - }, - - instrumentName: 'container', - - /** - When a child view is removed, destroy its element so that - it is removed from the DOM. - - The array observer that triggers this action is set up in the - `renderToBuffer` method. - - @private - @method childViewsWillChange - @param {Ember.Array} views the child views array before mutation - @param {Number} start the start position of the mutation - @param {Number} removed the number of child views removed - **/ - childViewsWillChange: function(views, start, removed) { - this.propertyWillChange('childViews'); - - if (removed > 0) { - var changedViews = views.slice(start, start+removed); - // transition to preRender before clearing parentView - this.currentState.childViewsWillChange(this, views, start, removed); - this.initializeViews(changedViews, null, null); - } - }, - - removeChild: function(child) { - this.removeObject(child); - return this; - }, - - /** - When a child view is added, make sure the DOM gets updated appropriately. - - If the view has already rendered an element, we tell the child view to - create an element and insert it into the DOM. If the enclosing container - view has already written to a buffer, but not yet converted that buffer - into an element, we insert the string representation of the child into the - appropriate place in the buffer. - - @private - @method childViewsDidChange - @param {Ember.Array} views the array of child views afte the mutation has occurred - @param {Number} start the start position of the mutation - @param {Number} removed the number of child views removed - @param {Number} the number of child views added - */ - childViewsDidChange: function(views, start, removed, added) { - if (added > 0) { - var changedViews = views.slice(start, start+added); - this.initializeViews(changedViews, this, get(this, 'templateData')); - this.currentState.childViewsDidChange(this, views, start, added); - } - this.propertyDidChange('childViews'); - }, - - initializeViews: function(views, parentView, templateData) { - forEach(views, function(view) { - set(view, '_parentView', parentView); - - if (!view.container && parentView) { - set(view, 'container', parentView.container); - } - - if (!get(view, 'templateData')) { - set(view, 'templateData', templateData); - } - }); - }, - - currentView: null, - - _currentViewWillChange: Ember.beforeObserver('currentView', function() { - var currentView = get(this, 'currentView'); - if (currentView) { - currentView.destroy(); - } - }), - - _currentViewDidChange: Ember.observer('currentView', function() { - var currentView = get(this, 'currentView'); - if (currentView) { - Ember.assert("You tried to set a current view that already has a parent. Make sure you don't have multiple outlets in the same view.", !get(currentView, '_parentView')); - this.pushObject(currentView); - } - }), - - _ensureChildrenAreInDOM: function () { - this.currentState.ensureChildrenAreInDOM(this); - } -}); - -Ember.merge(states._default, { - childViewsWillChange: Ember.K, - childViewsDidChange: Ember.K, - ensureChildrenAreInDOM: Ember.K -}); - -Ember.merge(states.inBuffer, { - childViewsDidChange: function(parentView, views, start, added) { - throw new Ember.Error('You cannot modify child views while in the inBuffer state'); - } -}); - -Ember.merge(states.hasElement, { - childViewsWillChange: function(view, views, start, removed) { - for (var i=start; i` and the following code: - - ```javascript - someItemsView = Ember.CollectionView.create({ - classNames: ['a-collection'], - content: ['A','B','C'], - itemViewClass: Ember.View.extend({ - template: Ember.Handlebars.compile("the letter: {{view.content}}") - }) - }); - - someItemsView.appendTo('body'); - ``` - - Will result in the following HTML structure - - ```html -
    -
    the letter: A
    -
    the letter: B
    -
    the letter: C
    -
    - ``` - - ## Automatic matching of parent/child tagNames - - Setting the `tagName` property of a `CollectionView` to any of - "ul", "ol", "table", "thead", "tbody", "tfoot", "tr", or "select" will result - in the item views receiving an appropriately matched `tagName` property. - - Given an empty `` and the following code: - - ```javascript - anUnorderedListView = Ember.CollectionView.create({ - tagName: 'ul', - content: ['A','B','C'], - itemViewClass: Ember.View.extend({ - template: Ember.Handlebars.compile("the letter: {{view.content}}") - }) - }); - - anUnorderedListView.appendTo('body'); - ``` - - Will result in the following HTML structure - - ```html -
      -
    • the letter: A
    • -
    • the letter: B
    • -
    • the letter: C
    • -
    - ``` - - Additional `tagName` pairs can be provided by adding to - `Ember.CollectionView.CONTAINER_MAP ` - - ```javascript - Ember.CollectionView.CONTAINER_MAP['article'] = 'section' - ``` - - ## Programmatic creation of child views - - For cases where additional customization beyond the use of a single - `itemViewClass` or `tagName` matching is required CollectionView's - `createChildView` method can be overidden: - - ```javascript - CustomCollectionView = Ember.CollectionView.extend({ - createChildView: function(viewClass, attrs) { - if (attrs.content.kind == 'album') { - viewClass = App.AlbumView; - } else { - viewClass = App.SongView; - } - return this._super(viewClass, attrs); - } - }); - ``` - - ## Empty View - - You can provide an `Ember.View` subclass to the `Ember.CollectionView` - instance as its `emptyView` property. If the `content` property of a - `CollectionView` is set to `null` or an empty array, an instance of this view - will be the `CollectionView`s only child. - - ```javascript - aListWithNothing = Ember.CollectionView.create({ - classNames: ['nothing'] - content: null, - emptyView: Ember.View.extend({ - template: Ember.Handlebars.compile("The collection is empty") - }) - }); - - aListWithNothing.appendTo('body'); - ``` - - Will result in the following HTML structure - - ```html -
    -
    - The collection is empty -
    -
    - ``` - - ## Adding and Removing items - - The `childViews` property of a `CollectionView` should not be directly - manipulated. Instead, add, remove, replace items from its `content` property. - This will trigger appropriate changes to its rendered HTML. - - - @class CollectionView - @namespace Ember - @extends Ember.ContainerView - @since Ember 0.9 -*/ -Ember.CollectionView = Ember.ContainerView.extend({ - - /** - A list of items to be displayed by the `Ember.CollectionView`. - - @property content - @type Ember.Array - @default null - */ - content: null, - - /** - This provides metadata about what kind of empty view class this - collection would like if it is being instantiated from another - system (like Handlebars) - - @private - @property emptyViewClass - */ - emptyViewClass: Ember.View, - - /** - An optional view to display if content is set to an empty array. - - @property emptyView - @type Ember.View - @default null - */ - emptyView: null, - - /** - @property itemViewClass - @type Ember.View - @default Ember.View - */ - itemViewClass: Ember.View, - - /** - Setup a CollectionView - - @method init - */ - init: function() { - var ret = this._super(); - this._contentDidChange(); - return ret; - }, - - /** - Invoked when the content property is about to change. Notifies observers that the - entire array content will change. - - @private - @method _contentWillChange - */ - _contentWillChange: Ember.beforeObserver('content', function() { - var content = this.get('content'); - - if (content) { content.removeArrayObserver(this); } - var len = content ? get(content, 'length') : 0; - this.arrayWillChange(content, 0, len); - }), - - /** - Check to make sure that the content has changed, and if so, - update the children directly. This is always scheduled - asynchronously, to allow the element to be created before - bindings have synchronized and vice versa. - - @private - @method _contentDidChange - */ - _contentDidChange: Ember.observer('content', function() { - var content = get(this, 'content'); - - if (content) { - this._assertArrayLike(content); - content.addArrayObserver(this); - } - - var len = content ? get(content, 'length') : 0; - this.arrayDidChange(content, 0, null, len); - }), - - /** - Ensure that the content implements Ember.Array - - @private - @method _assertArrayLike - */ - _assertArrayLike: function(content) { - Ember.assert(fmt("an Ember.CollectionView's content must implement Ember.Array. You passed %@", [content]), Ember.Array.detect(content)); - }, - - /** - Removes the content and content observers. - - @method destroy - */ - destroy: function() { - if (!this._super()) { return; } - - var content = get(this, 'content'); - if (content) { content.removeArrayObserver(this); } - - if (this._createdEmptyView) { - this._createdEmptyView.destroy(); - } - - return this; - }, - - /** - Called when a mutation to the underlying content array will occur. - - This method will remove any views that are no longer in the underlying - content array. - - Invokes whenever the content array itself will change. - - @method arrayWillChange - @param {Array} content the managed collection of objects - @param {Number} start the index at which the changes will occurr - @param {Number} removed number of object to be removed from content - */ - arrayWillChange: function(content, start, removedCount) { - // If the contents were empty before and this template collection has an - // empty view remove it now. - var emptyView = get(this, 'emptyView'); - if (emptyView && emptyView instanceof Ember.View) { - emptyView.removeFromParent(); - } - - // Loop through child views that correspond with the removed items. - // Note that we loop from the end of the array to the beginning because - // we are mutating it as we go. - var childViews = this._childViews, childView, idx, len; - - len = this._childViews.length; - - var removingAll = removedCount === len; - - if (removingAll) { - this.currentState.empty(this); - this.invokeRecursively(function(view) { - view.removedFromDOM = true; - }, false); - } - - for (idx = start + removedCount - 1; idx >= start; idx--) { - childView = childViews[idx]; - childView.destroy(); - } - }, - - /** - Called when a mutation to the underlying content array occurs. - - This method will replay that mutation against the views that compose the - `Ember.CollectionView`, ensuring that the view reflects the model. - - This array observer is added in `contentDidChange`. - - @method arrayDidChange - @param {Array} content the managed collection of objects - @param {Number} start the index at which the changes occurred - @param {Number} removed number of object removed from content - @param {Number} added number of object added to content - */ - arrayDidChange: function(content, start, removed, added) { - var addedViews = [], view, item, idx, len, itemViewClass, - emptyView; - - len = content ? get(content, 'length') : 0; - - if (len) { - itemViewClass = get(this, 'itemViewClass'); - - if ('string' === typeof itemViewClass) { - itemViewClass = get(itemViewClass) || itemViewClass; - } - - Ember.assert(fmt("itemViewClass must be a subclass of Ember.View, not %@", - [itemViewClass]), - 'string' === typeof itemViewClass || Ember.View.detect(itemViewClass)); - - for (idx = start; idx < start+added; idx++) { - item = content.objectAt(idx); - - view = this.createChildView(itemViewClass, { - content: item, - contentIndex: idx - }); - - addedViews.push(view); - } - } else { - emptyView = get(this, 'emptyView'); - - if (!emptyView) { return; } - - if ('string' === typeof emptyView) { - emptyView = get(emptyView) || emptyView; - } - - emptyView = this.createChildView(emptyView); - addedViews.push(emptyView); - set(this, 'emptyView', emptyView); - - if (Ember.CoreView.detect(emptyView)) { - this._createdEmptyView = emptyView; - } - } - - this.replace(start, 0, addedViews); - }, - - /** - Instantiates a view to be added to the childViews array during view - initialization. You generally will not call this method directly unless - you are overriding `createChildViews()`. Note that this method will - automatically configure the correct settings on the new view instance to - act as a child of the parent. - - The tag name for the view will be set to the tagName of the viewClass - passed in. - - @method createChildView - @param {Class} viewClass - @param {Hash} [attrs] Attributes to add - @return {Ember.View} new instance - */ - createChildView: function(view, attrs) { - view = this._super(view, attrs); - - var itemTagName = get(view, 'tagName'); - - if (itemTagName === null || itemTagName === undefined) { - itemTagName = Ember.CollectionView.CONTAINER_MAP[get(this, 'tagName')]; - set(view, 'tagName', itemTagName); - } - - return view; - } -}); - -/** - A map of parent tags to their default child tags. You can add - additional parent tags if you want collection views that use - a particular parent tag to default to a child tag. - - @property CONTAINER_MAP - @type Hash - @static - @final -*/ -Ember.CollectionView.CONTAINER_MAP = { - ul: 'li', - ol: 'li', - table: 'tr', - thead: 'tr', - tbody: 'tr', - tfoot: 'tr', - tr: 'td', - select: 'option' -}; - -})(); - - - -(function() { -var get = Ember.get; - -/** - The ComponentTemplateDeprecation mixin is used to provide a useful - deprecation warning when using either `template` or `templateName` with - a component. The `template` and `templateName` properties specified at - extend time are moved to `layout` and `layoutName` respectively. - - `Ember.ComponentTemplateDeprecation` is used internally by Ember in - `Ember.Component`. - - @class ComponentTemplateDeprecation - @namespace Ember -*/ -Ember.ComponentTemplateDeprecation = Ember.Mixin.create({ - /** - @private - - Moves `templateName` to `layoutName` and `template` to `layout` at extend - time if a layout is not also specified. - - Note that this currently modifies the mixin themselves, which is technically - dubious but is practically of little consequence. This may change in the - future. - - @method willMergeMixin - */ - willMergeMixin: function(props) { - // must call _super here to ensure that the ActionHandler - // mixin is setup properly (moves actions -> _actions) - // - // Calling super is only OK here since we KNOW that - // there is another Mixin loaded first. - this._super.apply(this, arguments); - - var deprecatedProperty, replacementProperty, - layoutSpecified = (props.layoutName || props.layout || get(this, 'layoutName')); - - if (props.templateName && !layoutSpecified) { - deprecatedProperty = 'templateName'; - replacementProperty = 'layoutName'; - - props.layoutName = props.templateName; - delete props['templateName']; - } - - if (props.template && !layoutSpecified) { - deprecatedProperty = 'template'; - replacementProperty = 'layout'; - - props.layout = props.template; - delete props['template']; - } - - if (deprecatedProperty) { - Ember.deprecate('Do not specify ' + deprecatedProperty + ' on a Component, use ' + replacementProperty + ' instead.', false); - } - } -}); - - -})(); - - - -(function() { -var get = Ember.get, set = Ember.set, isNone = Ember.isNone, - a_slice = Array.prototype.slice; - - -/** -@module ember -@submodule ember-views -*/ - -/** - An `Ember.Component` is a view that is completely - isolated. Property access in its templates go - to the view object and actions are targeted at - the view object. There is no access to the - surrounding context or outer controller; all - contextual information must be passed in. - - The easiest way to create an `Ember.Component` is via - a template. If you name a template - `components/my-foo`, you will be able to use - `{{my-foo}}` in other templates, which will make - an instance of the isolated component. - - ```handlebars - {{app-profile person=currentUser}} - ``` - - ```handlebars - -

    {{person.title}}

    - -

    {{person.signature}}

    - ``` - - You can use `yield` inside a template to - include the **contents** of any block attached to - the component. The block will be executed in the - context of the surrounding context or outer controller: - - ```handlebars - {{#app-profile person=currentUser}} -

    Admin mode

    - {{! Executed in the controller's context. }} - {{/app-profile}} - ``` - - ```handlebars - -

    {{person.title}}

    - {{! Executed in the components context. }} - {{yield}} {{! block contents }} - ``` - - If you want to customize the component, in order to - handle events or actions, you implement a subclass - of `Ember.Component` named after the name of the - component. Note that `Component` needs to be appended to the name of - your subclass like `AppProfileComponent`. - - For example, you could implement the action - `hello` for the `app-profile` component: - - ```javascript - App.AppProfileComponent = Ember.Component.extend({ - actions: { - hello: function(name) { - console.log("Hello", name); - } - } - }); - ``` - - And then use it in the component's template: - - ```handlebars - - -

    {{person.title}}

    - {{yield}} - - - ``` - - Components must have a `-` in their name to avoid - conflicts with built-in controls that wrap HTML - elements. This is consistent with the same - requirement in web components. - - @class Component - @namespace Ember - @extends Ember.View -*/ -Ember.Component = Ember.View.extend(Ember.TargetActionSupport, Ember.ComponentTemplateDeprecation, { - init: function() { - this._super(); - set(this, 'context', this); - set(this, 'controller', this); - }, - - defaultLayout: function(context, options){ - Ember.Handlebars.helpers['yield'].call(context, options); - }, - - /** - A components template property is set by passing a block - during its invocation. It is executed within the parent context. - - Example: - - ```handlebars - {{#my-component}} - // something that is run in the context - // of the parent context - {{/my-component}} - ``` - - Specifying a template directly to a component is deprecated without - also specifying the layout property. - - @deprecated - @property template - */ - template: Ember.computed(function(key, value) { - if (value !== undefined) { return value; } - - var templateName = get(this, 'templateName'), - template = this.templateForName(templateName, 'template'); - - Ember.assert("You specified the templateName " + templateName + " for " + this + ", but it did not exist.", !templateName || template); - - return template || get(this, 'defaultTemplate'); - }).property('templateName'), - - /** - Specifying a components `templateName` is deprecated without also - providing the `layout` or `layoutName` properties. - - @deprecated - @property templateName - */ - templateName: null, - - // during render, isolate keywords - cloneKeywords: function() { - return { - view: this, - controller: this - }; - }, - - _yield: function(context, options) { - var view = options.data.view, - parentView = this._parentView, - template = get(this, 'template'); - - if (template) { - Ember.assert("A Component must have a parent view in order to yield.", parentView); - - view.appendChild(Ember.View, { - isVirtual: true, - tagName: '', - _contextView: parentView, - template: template, - context: get(parentView, 'context'), - controller: get(parentView, 'controller'), - templateData: { keywords: parentView.cloneKeywords() } - }); - } - }, - - /** - If the component is currently inserted into the DOM of a parent view, this - property will point to the controller of the parent view. - - @property targetObject - @type Ember.Controller - @default null - */ - targetObject: Ember.computed(function(key) { - var parentView = get(this, '_parentView'); - return parentView ? get(parentView, 'controller') : null; - }).property('_parentView'), - - /** - Triggers a named action on the controller context where the component is used if - this controller has registered for notifications of the action. - - For example a component for playing or pausing music may translate click events - into action notifications of "play" or "stop" depending on some internal state - of the component: - - - ```javascript - App.PlayButtonComponent = Ember.Component.extend({ - click: function(){ - if (this.get('isPlaying')) { - this.sendAction('play'); - } else { - this.sendAction('stop'); - } - } - }); - ``` - - When used inside a template these component actions are configured to - trigger actions in the outer application context: - - ```handlebars - {{! application.hbs }} - {{play-button play="musicStarted" stop="musicStopped"}} - ``` - - When the component receives a browser `click` event it translate this - interaction into application-specific semantics ("play" or "stop") and - triggers the specified action name on the controller for the template - where the component is used: - - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - actions: { - musicStarted: function(){ - // called when the play button is clicked - // and the music started playing - }, - musicStopped: function(){ - // called when the play button is clicked - // and the music stopped playing - } - } - }); - ``` - - If no action name is passed to `sendAction` a default name of "action" - is assumed. - - ```javascript - App.NextButtonComponent = Ember.Component.extend({ - click: function(){ - this.sendAction(); - } - }); - ``` - - ```handlebars - {{! application.hbs }} - {{next-button action="playNextSongInAlbum"}} - ``` - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - actions: { - playNextSongInAlbum: function(){ - ... - } - } - }); - ``` - - @method sendAction - @param [action] {String} the action to trigger - @param [context] {*} a context to send with the action - */ - sendAction: function(action) { - var actionName, - contexts = a_slice.call(arguments, 1); - - // Send the default action - if (action === undefined) { - actionName = get(this, 'action'); - Ember.assert("The default action was triggered on the component " + this.toString() + - ", but the action name (" + actionName + ") was not a string.", - isNone(actionName) || typeof actionName === 'string'); - } else { - actionName = get(this, action); - Ember.assert("The " + action + " action was triggered on the component " + - this.toString() + ", but the action name (" + actionName + - ") was not a string.", - isNone(actionName) || typeof actionName === 'string'); - } - - // If no action name for that action could be found, just abort. - if (actionName === undefined) { return; } - - this.triggerAction({ - action: actionName, - actionContext: contexts - }); - } -}); - -})(); - - - -(function() { - -})(); - - - -(function() { -/** -`Ember.ViewTargetActionSupport` is a mixin that can be included in a -view class to add a `triggerAction` method with semantics similar to -the Handlebars `{{action}}` helper. It provides intelligent defaults -for the action's target: the view's controller; and the context that is -sent with the action: the view's context. - -Note: In normal Ember usage, the `{{action}}` helper is usually the best -choice. This mixin is most often useful when you are doing more complex -event handling in custom View subclasses. - -For example: - -```javascript -App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, { - action: 'save', - click: function() { - this.triggerAction(); // Sends the `save` action, along with the current context - // to the current controller - } -}); -``` - -The `action` can be provided as properties of an optional object argument -to `triggerAction` as well. - -```javascript -App.SaveButtonView = Ember.View.extend(Ember.ViewTargetActionSupport, { - click: function() { - this.triggerAction({ - action: 'save' - }); // Sends the `save` action, along with the current context - // to the current controller - } -}); -``` - -@class ViewTargetActionSupport -@namespace Ember -@extends Ember.TargetActionSupport -*/ -Ember.ViewTargetActionSupport = Ember.Mixin.create(Ember.TargetActionSupport, { - /** - @property target - */ - target: Ember.computed.alias('controller'), - /** - @property actionContext - */ - actionContext: Ember.computed.alias('context') -}); - -})(); - - - -(function() { - -})(); - - - -(function() { -/** -Ember Views - -@module ember -@submodule ember-views -@requires ember-runtime -@main ember-views -*/ - -})(); - -(function() { -define("metamorph", - [], - function() { - "use strict"; - // ========================================================================== - // Project: metamorph - // Copyright: ©2014 Tilde, Inc. All rights reserved. - // ========================================================================== - - var K = function() {}, - guid = 0, - disableRange = (function(){ - if ('undefined' !== typeof MetamorphENV) { - return MetamorphENV.DISABLE_RANGE_API; - } else if ('undefined' !== ENV) { - return ENV.DISABLE_RANGE_API; - } else { - return false; - } - })(), - - // Feature-detect the W3C range API, the extended check is for IE9 which only partially supports ranges - supportsRange = (!disableRange) && typeof document !== 'undefined' && ('createRange' in document) && (typeof Range !== 'undefined') && Range.prototype.createContextualFragment, - - // Internet Explorer prior to 9 does not allow setting innerHTML if the first element - // is a "zero-scope" element. This problem can be worked around by making - // the first node an invisible text node. We, like Modernizr, use ­ - needsShy = typeof document !== 'undefined' && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "
    "; - testEl.firstChild.innerHTML = ""; - return testEl.firstChild.innerHTML === ''; - })(), - - - // IE 8 (and likely earlier) likes to move whitespace preceeding - // a script tag to appear after it. This means that we can - // accidentally remove whitespace when updating a morph. - movesWhitespace = document && (function() { - var testEl = document.createElement('div'); - testEl.innerHTML = "Test: Value"; - return testEl.childNodes[0].nodeValue === 'Test:' && - testEl.childNodes[2].nodeValue === ' Value'; - })(); - - // Constructor that supports either Metamorph('foo') or new - // Metamorph('foo'); - // - // Takes a string of HTML as the argument. - - var Metamorph = function(html) { - var self; - - if (this instanceof Metamorph) { - self = this; - } else { - self = new K(); - } - - self.innerHTML = html; - var myGuid = 'metamorph-'+(guid++); - self.start = myGuid + '-start'; - self.end = myGuid + '-end'; - - return self; - }; - - K.prototype = Metamorph.prototype; - - var rangeFor, htmlFunc, removeFunc, outerHTMLFunc, appendToFunc, afterFunc, prependFunc, startTagFunc, endTagFunc; - - outerHTMLFunc = function() { - return this.startTag() + this.innerHTML + this.endTag(); - }; - - startTagFunc = function() { - /* - * We replace chevron by its hex code in order to prevent escaping problems. - * Check this thread for more explaination: - * http://stackoverflow.com/questions/8231048/why-use-x3c-instead-of-when-generating-html-from-javascript - */ - return "hi"; - * div.firstChild.firstChild.tagName //=> "" - * - * If our script markers are inside such a node, we need to find that - * node and use *it* as the marker. - */ - var realNode = function(start) { - while (start.parentNode.tagName === "") { - start = start.parentNode; - } - - return start; - }; - - /* - * When automatically adding a tbody, Internet Explorer inserts the - * tbody immediately before the first . Other browsers create it - * before the first node, no matter what. - * - * This means the the following code: - * - * div = document.createElement("div"); - * div.innerHTML = "
    hi
    - * - * Generates the following DOM in IE: - * - * + div - * + table - * - script id='first' - * + tbody - * + tr - * + td - * - "hi" - * - script id='last' - * - * Which means that the two script tags, even though they were - * inserted at the same point in the hierarchy in the original - * HTML, now have different parents. - * - * This code reparents the first script tag by making it the tbody's - * first child. - * - */ - var fixParentage = function(start, end) { - if (start.parentNode !== end.parentNode) { - end.parentNode.insertBefore(start, end.parentNode.firstChild); - } - }; - - htmlFunc = function(html, outerToo) { - // get the real starting node. see realNode for details. - var start = realNode(document.getElementById(this.start)); - var end = document.getElementById(this.end); - var parentNode = end.parentNode; - var node, nextSibling, last; - - // make sure that the start and end nodes share the same - // parent. If not, fix it. - fixParentage(start, end); - - // remove all of the nodes after the starting placeholder and - // before the ending placeholder. - node = start.nextSibling; - while (node) { - nextSibling = node.nextSibling; - last = node === end; - - // if this is the last node, and we want to remove it as well, - // set the `end` node to the next sibling. This is because - // for the rest of the function, we insert the new nodes - // before the end (note that insertBefore(node, null) is - // the same as appendChild(node)). - // - // if we do not want to remove it, just break. - if (last) { - if (outerToo) { end = node.nextSibling; } else { break; } - } - - node.parentNode.removeChild(node); - - // if this is the last node and we didn't break before - // (because we wanted to remove the outer nodes), break - // now. - if (last) { break; } - - node = nextSibling; - } - - // get the first node for the HTML string, even in cases like - // tables and lists where a simple innerHTML on a div would - // swallow some of the content. - node = firstNodeFor(start.parentNode, html); - - if (outerToo) { - start.parentNode.removeChild(start); - } - - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, end); - node = nextSibling; - } - }; - - // remove the nodes in the DOM representing this metamorph. - // - // this includes the starting and ending placeholders. - removeFunc = function() { - var start = realNode(document.getElementById(this.start)); - var end = document.getElementById(this.end); - - this.html(''); - start.parentNode.removeChild(start); - end.parentNode.removeChild(end); - }; - - appendToFunc = function(parentNode) { - var node = firstNodeFor(parentNode, this.outerHTML()); - var nextSibling; - - while (node) { - nextSibling = node.nextSibling; - parentNode.appendChild(node); - node = nextSibling; - } - }; - - afterFunc = function(html) { - // get the real starting node. see realNode for details. - var end = document.getElementById(this.end); - var insertBefore = end.nextSibling; - var parentNode = end.parentNode; - var nextSibling; - var node; - - // get the first node for the HTML string, even in cases like - // tables and lists where a simple innerHTML on a div would - // swallow some of the content. - node = firstNodeFor(parentNode, html); - - // copy the nodes for the HTML between the starting and ending - // placeholder. - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } - }; - - prependFunc = function(html) { - var start = document.getElementById(this.start); - var parentNode = start.parentNode; - var nextSibling; - var node; - - node = firstNodeFor(parentNode, html); - var insertBefore = start.nextSibling; - - while (node) { - nextSibling = node.nextSibling; - parentNode.insertBefore(node, insertBefore); - node = nextSibling; - } - }; - } - - Metamorph.prototype.html = function(html) { - this.checkRemoved(); - if (html === undefined) { return this.innerHTML; } - - htmlFunc.call(this, html); - - this.innerHTML = html; - }; - - Metamorph.prototype.replaceWith = function(html) { - this.checkRemoved(); - htmlFunc.call(this, html, true); - }; - - Metamorph.prototype.remove = removeFunc; - Metamorph.prototype.outerHTML = outerHTMLFunc; - Metamorph.prototype.appendTo = appendToFunc; - Metamorph.prototype.after = afterFunc; - Metamorph.prototype.prepend = prependFunc; - Metamorph.prototype.startTag = startTagFunc; - Metamorph.prototype.endTag = endTagFunc; - - Metamorph.prototype.isRemoved = function() { - var before = document.getElementById(this.start); - var after = document.getElementById(this.end); - - return !before || !after; - }; - - Metamorph.prototype.checkRemoved = function() { - if (this.isRemoved()) { - throw new Error("Cannot perform operations on a Metamorph that is not in the DOM."); - } - }; - - return Metamorph; - }); - -})(); - -(function() { -/** -@module ember -@submodule ember-handlebars-compiler -*/ - -// Eliminate dependency on any Ember to simplify precompilation workflow -var objectCreate = Object.create || function(parent) { - function F() {} - F.prototype = parent; - return new F(); -}; - -var Handlebars = (Ember.imports && Ember.imports.Handlebars) || (this && this.Handlebars); -if (!Handlebars && typeof require === 'function') { - Handlebars = require('handlebars'); -} - -Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1. Include " + - "a SCRIPT tag in the HTML HEAD linking to the Handlebars file " + - "before you link to Ember.", Handlebars); - -Ember.assert("Ember Handlebars requires Handlebars version 1.0 or 1.1, " + - "COMPILER_REVISION expected: 4, got: " + Handlebars.COMPILER_REVISION + - " - Please note: Builds of master may have other COMPILER_REVISION values.", - Handlebars.COMPILER_REVISION === 4); - -/** - Prepares the Handlebars templating library for use inside Ember's view - system. - - The `Ember.Handlebars` object is the standard Handlebars library, extended to - use Ember's `get()` method instead of direct property access, which allows - computed properties to be used inside templates. - - To create an `Ember.Handlebars` template, call `Ember.Handlebars.compile()`. - This will return a function that can be used by `Ember.View` for rendering. - - @class Handlebars - @namespace Ember -*/ -Ember.Handlebars = objectCreate(Handlebars); - -/** - Register a bound helper or custom view helper. - - ## Simple bound helper example - - ```javascript - Ember.Handlebars.helper('capitalize', function(value) { - return value.toUpperCase(); - }); - ``` - - The above bound helper can be used inside of templates as follows: - - ```handlebars - {{capitalize name}} - ``` - - In this case, when the `name` property of the template's context changes, - the rendered value of the helper will update to reflect this change. - - For more examples of bound helpers, see documentation for - `Ember.Handlebars.registerBoundHelper`. - - ## Custom view helper example - - Assuming a view subclass named `App.CalendarView` were defined, a helper - for rendering instances of this view could be registered as follows: - - ```javascript - Ember.Handlebars.helper('calendar', App.CalendarView): - ``` - - The above bound helper can be used inside of templates as follows: - - ```handlebars - {{calendar}} - ``` - - Which is functionally equivalent to: - - ```handlebars - {{view App.CalendarView}} - ``` - - Options in the helper will be passed to the view in exactly the same - manner as with the `view` helper. - - @method helper - @for Ember.Handlebars - @param {String} name - @param {Function|Ember.View} function or view class constructor - @param {String} dependentKeys* -*/ -Ember.Handlebars.helper = function(name, value) { - Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Ember.Component.detect(value) || name.match(/-/)); - - if (Ember.View.detect(value)) { - Ember.Handlebars.registerHelper(name, Ember.Handlebars.makeViewHelper(value)); - } else { - Ember.Handlebars.registerBoundHelper.apply(null, arguments); - } -}; - -/** - Returns a helper function that renders the provided ViewClass. - - Used internally by Ember.Handlebars.helper and other methods - involving helper/component registration. - - @private - @method makeViewHelper - @for Ember.Handlebars - @param {Function} ViewClass view class constructor -*/ -Ember.Handlebars.makeViewHelper = function(ViewClass) { - return function(options) { - Ember.assert("You can only pass attributes (such as name=value) not bare values to a helper for a View found in '" + ViewClass.toString() + "'", arguments.length < 2); - return Ember.Handlebars.helpers.view.call(this, ViewClass, options); - }; -}; - -/** -@class helpers -@namespace Ember.Handlebars -*/ -Ember.Handlebars.helpers = objectCreate(Handlebars.helpers); - -/** - Override the the opcode compiler and JavaScript compiler for Handlebars. - - @class Compiler - @namespace Ember.Handlebars - @private - @constructor -*/ -Ember.Handlebars.Compiler = function() {}; - -// Handlebars.Compiler doesn't exist in runtime-only -if (Handlebars.Compiler) { - Ember.Handlebars.Compiler.prototype = objectCreate(Handlebars.Compiler.prototype); -} - -Ember.Handlebars.Compiler.prototype.compiler = Ember.Handlebars.Compiler; - -/** - @class JavaScriptCompiler - @namespace Ember.Handlebars - @private - @constructor -*/ -Ember.Handlebars.JavaScriptCompiler = function() {}; - -// Handlebars.JavaScriptCompiler doesn't exist in runtime-only -if (Handlebars.JavaScriptCompiler) { - Ember.Handlebars.JavaScriptCompiler.prototype = objectCreate(Handlebars.JavaScriptCompiler.prototype); - Ember.Handlebars.JavaScriptCompiler.prototype.compiler = Ember.Handlebars.JavaScriptCompiler; -} - - -Ember.Handlebars.JavaScriptCompiler.prototype.namespace = "Ember.Handlebars"; - -Ember.Handlebars.JavaScriptCompiler.prototype.initializeBuffer = function() { - return "''"; -}; - -/** - Override the default buffer for Ember Handlebars. By default, Handlebars - creates an empty String at the beginning of each invocation and appends to - it. Ember's Handlebars overrides this to append to a single shared buffer. - - @private - @method appendToBuffer - @param string {String} -*/ -Ember.Handlebars.JavaScriptCompiler.prototype.appendToBuffer = function(string) { - return "data.buffer.push("+string+");"; -}; - -// Hacks ahead: -// Handlebars presently has a bug where the `blockHelperMissing` hook -// doesn't get passed the name of the missing helper name, but rather -// gets passed the value of that missing helper evaluated on the current -// context, which is most likely `undefined` and totally useless. -// -// So we alter the compiled template function to pass the name of the helper -// instead, as expected. -// -// This can go away once the following is closed: -// https://github.com/wycats/handlebars.js/issues/634 - -var DOT_LOOKUP_REGEX = /helpers\.(.*?)\)/, - BRACKET_STRING_LOOKUP_REGEX = /helpers\['(.*?)'/, - INVOCATION_SPLITTING_REGEX = /(.*blockHelperMissing\.call\(.*)(stack[0-9]+)(,.*)/; - -Ember.Handlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation = function(source) { - var helperInvocation = source[source.length - 1], - helperName = (DOT_LOOKUP_REGEX.exec(helperInvocation) || BRACKET_STRING_LOOKUP_REGEX.exec(helperInvocation))[1], - matches = INVOCATION_SPLITTING_REGEX.exec(helperInvocation); - - source[source.length - 1] = matches[1] + "'" + helperName + "'" + matches[3]; -}; -var stringifyBlockHelperMissing = Ember.Handlebars.JavaScriptCompiler.stringifyLastBlockHelperMissingInvocation; - -var originalBlockValue = Ember.Handlebars.JavaScriptCompiler.prototype.blockValue; -Ember.Handlebars.JavaScriptCompiler.prototype.blockValue = function() { - originalBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); -}; - -var originalAmbiguousBlockValue = Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue; -Ember.Handlebars.JavaScriptCompiler.prototype.ambiguousBlockValue = function() { - originalAmbiguousBlockValue.apply(this, arguments); - stringifyBlockHelperMissing(this.source); -}; - -/** - Rewrite simple mustaches from `{{foo}}` to `{{bind "foo"}}`. This means that - all simple mustaches in Ember's Handlebars will also set up an observer to - keep the DOM up to date when the underlying property changes. - - @private - @method mustache - @for Ember.Handlebars.Compiler - @param mustache -*/ -Ember.Handlebars.Compiler.prototype.mustache = function(mustache) { - if (!(mustache.params.length || mustache.hash)) { - var id = new Handlebars.AST.IdNode([{ part: '_triageMustache' }]); - - // Update the mustache node to include a hash value indicating whether the original node - // was escaped. This will allow us to properly escape values when the underlying value - // changes and we need to re-render the value. - if (!mustache.escaped) { - mustache.hash = mustache.hash || new Handlebars.AST.HashNode([]); - mustache.hash.pairs.push(["unescaped", new Handlebars.AST.StringNode("true")]); - } - mustache = new Handlebars.AST.MustacheNode([id].concat([mustache.id]), mustache.hash, !mustache.escaped); - } - - return Handlebars.Compiler.prototype.mustache.call(this, mustache); -}; - -/** - Used for precompilation of Ember Handlebars templates. This will not be used - during normal app execution. - - @method precompile - @for Ember.Handlebars - @static - @param {String} string The template to precompile -*/ -Ember.Handlebars.precompile = function(string) { - var ast = Handlebars.parse(string); - - var options = { - knownHelpers: { - action: true, - unbound: true, - 'bind-attr': true, - template: true, - view: true, - _triageMustache: true - }, - data: true, - stringParams: true - }; - - var environment = new Ember.Handlebars.Compiler().compile(ast, options); - return new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true); -}; - -// We don't support this for Handlebars runtime-only -if (Handlebars.compile) { - /** - The entry point for Ember Handlebars. This replaces the default - `Handlebars.compile` and turns on template-local data and String - parameters. - - @method compile - @for Ember.Handlebars - @static - @param {String} string The template to compile - @return {Function} - */ - Ember.Handlebars.compile = function(string) { - var ast = Handlebars.parse(string); - var options = { data: true, stringParams: true }; - var environment = new Ember.Handlebars.Compiler().compile(ast, options); - var templateSpec = new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true); - - var template = Ember.Handlebars.template(templateSpec); - template.isMethod = false; //Make sure we don't wrap templates with ._super - - return template; - }; -} - - -})(); - -(function() { -var slice = Array.prototype.slice, - originalTemplate = Ember.Handlebars.template; - -/** - If a path starts with a reserved keyword, returns the root - that should be used. - - @private - @method normalizePath - @for Ember - @param root {Object} - @param path {String} - @param data {Hash} -*/ -var normalizePath = Ember.Handlebars.normalizePath = function(root, path, data) { - var keywords = (data && data.keywords) || {}, - keyword, isKeyword; - - // Get the first segment of the path. For example, if the - // path is "foo.bar.baz", returns "foo". - keyword = path.split('.', 1)[0]; - - // Test to see if the first path is a keyword that has been - // passed along in the view's data hash. If so, we will treat - // that object as the new root. - if (keywords.hasOwnProperty(keyword)) { - // Look up the value in the template's data hash. - root = keywords[keyword]; - isKeyword = true; - - // Handle cases where the entire path is the reserved - // word. In that case, return the object itself. - if (path === keyword) { - path = ''; - } else { - // Strip the keyword from the path and look up - // the remainder from the newly found root. - path = path.substr(keyword.length+1); - } - } - - return { root: root, path: path, isKeyword: isKeyword }; -}; - - -/** - Lookup both on root and on window. If the path starts with - a keyword, the corresponding object will be looked up in the - template's data hash and used to resolve the path. - - @method get - @for Ember.Handlebars - @param {Object} root The object to look up the property on - @param {String} path The path to be lookedup - @param {Object} options The template's option hash -*/ -var handlebarsGet = Ember.Handlebars.get = function(root, path, options) { - var data = options && options.data, - normalizedPath = normalizePath(root, path, data), - value; - - - root = normalizedPath.root; - path = normalizedPath.path; - - value = Ember.get(root, path); - - if (value === undefined && root !== Ember.lookup && Ember.isGlobalPath(path)) { - value = Ember.get(Ember.lookup, path); - } - - - return value; -}; - -/** - This method uses `Ember.Handlebars.get` to lookup a value, then ensures - that the value is escaped properly. - - If `unescaped` is a truthy value then the escaping will not be performed. - - @method getEscaped - @for Ember.Handlebars - @param {Object} root The object to look up the property on - @param {String} path The path to be lookedup - @param {Object} options The template's option hash -*/ -Ember.Handlebars.getEscaped = function(root, path, options) { - var result = handlebarsGet(root, path, options); - - if (result === null || result === undefined) { - result = ""; - } else if (!(result instanceof Handlebars.SafeString)) { - result = String(result); - } - if (!options.hash.unescaped){ - result = Handlebars.Utils.escapeExpression(result); - } - - return result; -}; - -Ember.Handlebars.resolveParams = function(context, params, options) { - var resolvedParams = [], types = options.types, param, type; - - for (var i=0, l=params.length; isomeString
    ') - ``` - - @method htmlSafe - @for Ember.String - @static - @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars -*/ -Ember.String.htmlSafe = function(str) { - return new Handlebars.SafeString(str); -}; - -var htmlSafe = Ember.String.htmlSafe; - -if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { - - /** - Mark a string as being safe for unescaped output with Handlebars. - - ```javascript - '
    someString
    '.htmlSafe() - ``` - - See [Ember.String.htmlSafe](/api/classes/Ember.String.html#method_htmlSafe). - - @method htmlSafe - @for String - @return {Handlebars.SafeString} a string that will not be html escaped by Handlebars - */ - String.prototype.htmlSafe = function() { - return htmlSafe(this); - }; -} - -})(); - - - -(function() { -Ember.Handlebars.resolvePaths = function(options) { - var ret = [], - contexts = options.contexts, - roots = options.roots, - data = options.data; - - for (var i=0, l=contexts.length; i{{user.name}} - -
    -
    {{user.role.label}}
    - {{user.role.id}} - -

    {{user.role.description}}

    -
    - ``` - - `{{with}}` can be our best friend in these cases, - instead of writing `user.role.*` over and over, we use `{{#with user.role}}`. - Now the context within the `{{#with}} .. {{/with}}` block is `user.role` so you can do the following: - - ```handlebars -
    {{user.name}}
    - -
    - {{#with user.role}} -
    {{label}}
    - {{id}} - -

    {{description}}

    - {{/with}} -
    - ``` - - ### `as` operator - - This operator aliases the scope to a new name. It's helpful for semantic clarity and to retain - default scope or to reference from another `{{with}}` block. - - ```handlebars - // posts might not be - {{#with user.posts as blogPosts}} -
    - There are {{blogPosts.length}} blog posts written by {{user.name}}. -
    - - {{#each post in blogPosts}} -
  • {{post.title}}
  • - {{/each}} - {{/with}} - ``` - - Without the `as` operator, it would be impossible to reference `user.name` in the example above. - - NOTE: The alias should not reuse a name from the bound property path. - For example: `{{#with foo.bar as foo}}` is not supported because it attempts to alias using - the first part of the property path, `foo`. Instead, use `{{#with foo.bar as baz}}`. - - ### `controller` option - - Adding `controller='something'` instructs the `{{with}}` helper to create and use an instance of - the specified controller with the new context as its content. - - This is very similar to using an `itemController` option with the `{{each}}` helper. - - ```handlebars - {{#with users.posts controller='userBlogPosts'}} - {{!- The current context is wrapped in our controller instance }} - {{/with}} - ``` - - In the above example, the template provided to the `{{with}}` block is now wrapped in the - `userBlogPost` controller, which provides a very elegant way to decorate the context with custom - functions/properties. - - @method with - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('with', function withHelper(context, options) { - if (arguments.length === 4) { - var keywordName, path, rootPath, normalized, contextPath; - - Ember.assert("If you pass more than one argument to the with helper, it must be in the form #with foo as bar", arguments[1] === "as"); - options = arguments[3]; - keywordName = arguments[2]; - path = arguments[0]; - - Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); - - var localizedOptions = o_create(options); - localizedOptions.data = o_create(options.data); - localizedOptions.data.keywords = o_create(options.data.keywords || {}); - - if (Ember.isGlobalPath(path)) { - contextPath = path; - } else { - normalized = normalizePath(this, path, options.data); - path = normalized.path; - rootPath = normalized.root; - - // This is a workaround for the fact that you cannot bind separate objects - // together. When we implement that functionality, we should use it here. - var contextKey = Ember.$.expando + Ember.guidFor(rootPath); - localizedOptions.data.keywords[contextKey] = rootPath; - // if the path is '' ("this"), just bind directly to the current context - contextPath = path ? contextKey + '.' + path : contextKey; - } - - Ember.bind(localizedOptions.data.keywords, keywordName, contextPath); - - return bind.call(this, path, localizedOptions, true, exists); - } else { - Ember.assert("You must pass exactly one argument to the with helper", arguments.length === 2); - Ember.assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop); - return helpers.bind.call(options.contexts[0], context, options); - } -}); - - -/** - See [boundIf](/api/classes/Ember.Handlebars.helpers.html#method_boundIf) - and [unboundIf](/api/classes/Ember.Handlebars.helpers.html#method_unboundIf) - - @method if - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('if', function ifHelper(context, options) { - Ember.assert("You must pass exactly one argument to the if helper", arguments.length === 2); - Ember.assert("You must pass a block to the if helper", options.fn && options.fn !== Handlebars.VM.noop); - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } -}); - -/** - @method unless - @for Ember.Handlebars.helpers - @param {Function} context - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('unless', function unlessHelper(context, options) { - Ember.assert("You must pass exactly one argument to the unless helper", arguments.length === 2); - Ember.assert("You must pass a block to the unless helper", options.fn && options.fn !== Handlebars.VM.noop); - - var fn = options.fn, inverse = options.inverse; - - options.fn = inverse; - options.inverse = fn; - - if (options.data.isUnbound) { - return helpers.unboundIf.call(options.contexts[0], context, options); - } else { - return helpers.boundIf.call(options.contexts[0], context, options); - } -}); - -/** - `bind-attr` allows you to create a binding between DOM element attributes and - Ember objects. For example: - - ```handlebars - imageTitle - ``` - - The above handlebars template will fill the ``'s `src` attribute will - the value of the property referenced with `"imageUrl"` and its `alt` - attribute with the value of the property referenced with `"imageTitle"`. - - If the rendering context of this template is the following object: - - ```javascript - { - imageUrl: 'http://lolcats.info/haz-a-funny', - imageTitle: 'A humorous image of a cat' - } - ``` - - The resulting HTML output will be: - - ```html - A humorous image of a cat - ``` - - `bind-attr` cannot redeclare existing DOM element attributes. The use of `src` - in the following `bind-attr` example will be ignored and the hard coded value - of `src="/failwhale.gif"` will take precedence: - - ```handlebars - imageTitle - ``` - - ### `bind-attr` and the `class` attribute - - `bind-attr` supports a special syntax for handling a number of cases unique - to the `class` DOM element attribute. The `class` attribute combines - multiple discrete values into a single attribute as a space-delimited - list of strings. Each string can be: - - * a string return value of an object's property. - * a boolean return value of an object's property - * a hard-coded value - - A string return value works identically to other uses of `bind-attr`. The - return value of the property will become the value of the attribute. For - example, the following view and template: - - ```javascript - AView = Ember.View.extend({ - someProperty: function() { - return "aValue"; - }.property() - }) - ``` - - ```handlebars - - ``` - - A boolean return value will insert a specified class name if the property - returns `true` and remove the class name if the property returns `false`. - - A class name is provided via the syntax - `somePropertyName:class-name-if-true`. - - ```javascript - AView = Ember.View.extend({ - someBool: true - }) - ``` - - ```handlebars - - ``` - - Result in the following rendered output: - - ```html - - ``` - - An additional section of the binding can be provided if you want to - replace the existing class instead of removing it when the boolean - value changes: - - ```handlebars - - ``` - - A hard-coded value can be used by prepending `:` to the desired - class name: `:class-name-to-always-apply`. - - ```handlebars - - ``` - - Results in the following rendered output: - - ```html - - ``` - - All three strategies - string return value, boolean return value, and - hard-coded value – can be combined in a single declaration: - - ```handlebars - - ``` - - @method bind-attr - @for Ember.Handlebars.helpers - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('bind-attr', function bindAttrHelper(options) { - - var attrs = options.hash; - - Ember.assert("You must specify at least one hash argument to bind-attr", !!Ember.keys(attrs).length); - - var view = options.data.view; - var ret = []; - var ctx = this; - - // Generate a unique id for this element. This will be added as a - // data attribute to the element so it can be looked up when - // the bound property changes. - var dataId = ++Ember.uuid; - - // Handle classes differently, as we can bind multiple classes - var classBindings = attrs['class']; - if (classBindings != null) { - var classResults = EmberHandlebars.bindClasses(this, classBindings, view, dataId, options); - - ret.push('class="' + Handlebars.Utils.escapeExpression(classResults.join(' ')) + '"'); - delete attrs['class']; - } - - var attrKeys = Ember.keys(attrs); - - // For each attribute passed, create an observer and emit the - // current value of the property as an attribute. - forEach.call(attrKeys, function(attr) { - var path = attrs[attr], - normalized; - - Ember.assert(fmt("You must provide an expression as the value of bound attribute. You specified: %@=%@", [attr, path]), typeof path === 'string'); - - normalized = normalizePath(ctx, path, options.data); - - var value = (path === 'this') ? normalized.root : handlebarsGet(ctx, path, options), - type = Ember.typeOf(value); - - Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [value]), value === null || value === undefined || type === 'number' || type === 'string' || type === 'boolean'); - - var observer, invoker; - - observer = function observer() { - var result = handlebarsGet(ctx, path, options); - - Ember.assert(fmt("Attributes must be numbers, strings or booleans, not %@", [result]), - result === null || result === undefined || typeof result === 'number' || - typeof result === 'string' || typeof result === 'boolean'); - - var elem = view.$("[data-bindattr-" + dataId + "='" + dataId + "']"); - - // If we aren't able to find the element, it means the element - // to which we were bound has been removed from the view. - // In that case, we can assume the template has been re-rendered - // and we need to clean up the observer. - if (!elem || elem.length === 0) { - Ember.removeObserver(normalized.root, normalized.path, invoker); - return; - } - - Ember.View.applyAttributeBindings(elem, attr, result); - }; - - // Add an observer to the view for when the property changes. - // When the observer fires, find the element using the - // unique data id and update the attribute to the new value. - // Note: don't add observer when path is 'this' or path - // is whole keyword e.g. {{#each x in list}} ... {{bind-attr attr="x"}} - if (path !== 'this' && !(normalized.isKeyword && normalized.path === '' )) { - view.registerObserver(normalized.root, normalized.path, observer); - } - - // if this changes, also change the logic in ember-views/lib/views/view.js - if ((type === 'string' || (type === 'number' && !isNaN(value)))) { - ret.push(attr + '="' + Handlebars.Utils.escapeExpression(value) + '"'); - } else if (value && type === 'boolean') { - // The developer controls the attr name, so it should always be safe - ret.push(attr + '="' + attr + '"'); - } - }, this); - - // Add the unique identifier - // NOTE: We use all lower-case since Firefox has problems with mixed case in SVG - ret.push('data-bindattr-' + dataId + '="' + dataId + '"'); - return new EmberHandlebars.SafeString(ret.join(' ')); -}); - -/** - See `bind-attr` - - @method bindAttr - @for Ember.Handlebars.helpers - @deprecated - @param {Function} context - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('bindAttr', function bindAttrHelper() { - Ember.warn("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); - return EmberHandlebars.helpers['bind-attr'].apply(this, arguments); -}); - -/** - Helper that, given a space-separated string of property paths and a context, - returns an array of class names. Calling this method also has the side - effect of setting up observers at those property paths, such that if they - change, the correct class name will be reapplied to the DOM element. - - For example, if you pass the string "fooBar", it will first look up the - "fooBar" value of the context. If that value is true, it will add the - "foo-bar" class to the current element (i.e., the dasherized form of - "fooBar"). If the value is a string, it will add that string as the class. - Otherwise, it will not add any new class name. - - @private - @method bindClasses - @for Ember.Handlebars - @param {Ember.Object} context The context from which to lookup properties - @param {String} classBindings A string, space-separated, of class bindings - to use - @param {Ember.View} view The view in which observers should look for the - element to update - @param {Srting} bindAttrId Optional bindAttr id used to lookup elements - @return {Array} An array of class names to add -*/ -EmberHandlebars.bindClasses = function(context, classBindings, view, bindAttrId, options) { - var ret = [], newClass, value, elem; - - // Helper method to retrieve the property from the context and - // determine which class string to return, based on whether it is - // a Boolean or not. - var classStringForPath = function(root, parsedPath, options) { - var val, - path = parsedPath.path; - - if (path === 'this') { - val = root; - } else if (path === '') { - val = true; - } else { - val = handlebarsGet(root, path, options); - } - - return Ember.View._classStringForValue(path, val, parsedPath.className, parsedPath.falsyClassName); - }; - - // For each property passed, loop through and setup - // an observer. - forEach.call(classBindings.split(' '), function(binding) { - - // Variable in which the old class value is saved. The observer function - // closes over this variable, so it knows which string to remove when - // the property changes. - var oldClass; - - var observer, invoker; - - var parsedPath = Ember.View._parsePropertyPath(binding), - path = parsedPath.path, - pathRoot = context, - normalized; - - if (path !== '' && path !== 'this') { - normalized = normalizePath(context, path, options.data); - - pathRoot = normalized.root; - path = normalized.path; - } - - // Set up an observer on the context. If the property changes, toggle the - // class name. - observer = function() { - // Get the current value of the property - newClass = classStringForPath(context, parsedPath, options); - elem = bindAttrId ? view.$("[data-bindattr-" + bindAttrId + "='" + bindAttrId + "']") : view.$(); - - // If we can't find the element anymore, a parent template has been - // re-rendered and we've been nuked. Remove the observer. - if (!elem || elem.length === 0) { - Ember.removeObserver(pathRoot, path, invoker); - } else { - // If we had previously added a class to the element, remove it. - if (oldClass) { - elem.removeClass(oldClass); - } - - // If necessary, add a new class. Make sure we keep track of it so - // it can be removed in the future. - if (newClass) { - elem.addClass(newClass); - oldClass = newClass; - } else { - oldClass = null; - } - } - }; - - if (path !== '' && path !== 'this') { - view.registerObserver(pathRoot, path, observer); - } - - // We've already setup the observer; now we just need to figure out the - // correct behavior right now on the first pass through. - value = classStringForPath(context, parsedPath, options); - - if (value) { - ret.push(value); - - // Make sure we save the current value so that it can be removed if the - // observer fires. - oldClass = value; - } - }); - - return ret; -}; - - -})(); - - - -(function() { -/*globals Handlebars */ - -// TODO: Don't require the entire module -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, set = Ember.set; -var EmberHandlebars = Ember.Handlebars; -var LOWERCASE_A_Z = /^[a-z]/; -var VIEW_PREFIX = /^view\./; - -function makeBindings(thisContext, options) { - var hash = options.hash, - hashType = options.hashTypes; - - for (var prop in hash) { - if (hashType[prop] === 'ID') { - - var value = hash[prop]; - - if (Ember.IS_BINDING.test(prop)) { - Ember.warn("You're attempting to render a view by passing " + prop + "=" + value + " to a view helper, but this syntax is ambiguous. You should either surround " + value + " in quotes or remove `Binding` from " + prop + "."); - } else { - hash[prop + 'Binding'] = value; - hashType[prop + 'Binding'] = 'STRING'; - delete hash[prop]; - delete hashType[prop]; - } - } - } - - if (hash.hasOwnProperty('idBinding')) { - // id can't be bound, so just perform one-time lookup. - hash.id = EmberHandlebars.get(thisContext, hash.idBinding, options); - hashType.id = 'STRING'; - delete hash.idBinding; - delete hashType.idBinding; - } -} - -EmberHandlebars.ViewHelper = Ember.Object.create({ - - propertiesFromHTMLOptions: function(options) { - var hash = options.hash, data = options.data; - var extensions = {}, - classes = hash['class'], - dup = false; - - if (hash.id) { - extensions.elementId = hash.id; - dup = true; - } - - if (hash.tag) { - extensions.tagName = hash.tag; - dup = true; - } - - if (classes) { - classes = classes.split(' '); - extensions.classNames = classes; - dup = true; - } - - if (hash.classBinding) { - extensions.classNameBindings = hash.classBinding.split(' '); - dup = true; - } - - if (hash.classNameBindings) { - if (extensions.classNameBindings === undefined) extensions.classNameBindings = []; - extensions.classNameBindings = extensions.classNameBindings.concat(hash.classNameBindings.split(' ')); - dup = true; - } - - if (hash.attributeBindings) { - Ember.assert("Setting 'attributeBindings' via Handlebars is not allowed. Please subclass Ember.View and set it there instead."); - extensions.attributeBindings = null; - dup = true; - } - - if (dup) { - hash = Ember.$.extend({}, hash); - delete hash.id; - delete hash.tag; - delete hash['class']; - delete hash.classBinding; - } - - // Set the proper context for all bindings passed to the helper. This applies to regular attribute bindings - // as well as class name bindings. If the bindings are local, make them relative to the current context - // instead of the view. - var path; - - // Evaluate the context of regular attribute bindings: - for (var prop in hash) { - if (!hash.hasOwnProperty(prop)) { continue; } - - // Test if the property ends in "Binding" - if (Ember.IS_BINDING.test(prop) && typeof hash[prop] === 'string') { - path = this.contextualizeBindingPath(hash[prop], data); - if (path) { hash[prop] = path; } - } - } - - // Evaluate the context of class name bindings: - if (extensions.classNameBindings) { - for (var b in extensions.classNameBindings) { - var full = extensions.classNameBindings[b]; - if (typeof full === 'string') { - // Contextualize the path of classNameBinding so this: - // - // classNameBinding="isGreen:green" - // - // is converted to this: - // - // classNameBinding="_parentView.context.isGreen:green" - var parsedPath = Ember.View._parsePropertyPath(full); - path = this.contextualizeBindingPath(parsedPath.path, data); - if (path) { extensions.classNameBindings[b] = path + parsedPath.classNames; } - } - } - } - - return Ember.$.extend(hash, extensions); - }, - - // Transform bindings from the current context to a context that can be evaluated within the view. - // Returns null if the path shouldn't be changed. - // - // TODO: consider the addition of a prefix that would allow this method to return `path`. - contextualizeBindingPath: function(path, data) { - var normalized = Ember.Handlebars.normalizePath(null, path, data); - if (normalized.isKeyword) { - return 'templateData.keywords.' + path; - } else if (Ember.isGlobalPath(path)) { - return null; - } else if (path === 'this' || path === '') { - return '_parentView.context'; - } else { - return '_parentView.context.' + path; - } - }, - - helper: function(thisContext, path, options) { - var data = options.data, - fn = options.fn, - newView; - - makeBindings(thisContext, options); - - if ('string' === typeof path) { - - // TODO: this is a lame conditional, this should likely change - // but something along these lines will likely need to be added - // as deprecation warnings - // - if (options.types[0] === 'STRING' && LOWERCASE_A_Z.test(path) && !VIEW_PREFIX.test(path)) { - Ember.assert("View requires a container", !!data.view.container); - newView = data.view.container.lookupFactory('view:' + path); - } else { - newView = EmberHandlebars.get(thisContext, path, options); - } - - Ember.assert("Unable to find view at path '" + path + "'", !!newView); - } else { - newView = path; - } - - Ember.assert(Ember.String.fmt('You must pass a view to the #view helper, not %@ (%@)', [path, newView]), Ember.View.detect(newView) || Ember.View.detectInstance(newView)); - - var viewOptions = this.propertiesFromHTMLOptions(options, thisContext); - var currentView = data.view; - viewOptions.templateData = data; - var newViewProto = newView.proto ? newView.proto() : newView; - - if (fn) { - Ember.assert("You cannot provide a template block if you also specified a templateName", !get(viewOptions, 'templateName') && !get(newViewProto, 'templateName')); - viewOptions.template = fn; - } - - // We only want to override the `_context` computed property if there is - // no specified controller. See View#_context for more information. - if (!newViewProto.controller && !newViewProto.controllerBinding && !viewOptions.controller && !viewOptions.controllerBinding) { - viewOptions._context = thisContext; - } - - currentView.appendChild(newView, viewOptions); - } -}); - -/** - `{{view}}` inserts a new instance of `Ember.View` into a template passing its - options to the `Ember.View`'s `create` method and using the supplied block as - the view's own template. - - An empty `` and the following template: - - ```handlebars - A span: - {{#view tagName="span"}} - hello. - {{/view}} - ``` - - Will result in HTML structure: - - ```html - - - -
    - A span: - - Hello. - -
    - - ``` - - ### `parentView` setting - - The `parentView` property of the new `Ember.View` instance created through - `{{view}}` will be set to the `Ember.View` instance of the template where - `{{view}}` was called. - - ```javascript - aView = Ember.View.create({ - template: Ember.Handlebars.compile("{{#view}} my parent: {{parentView.elementId}} {{/view}}") - }); - - aView.appendTo('body'); - ``` - - Will result in HTML structure: - - ```html -
    -
    - my parent: ember1 -
    -
    - ``` - - ### Setting CSS id and class attributes - - The HTML `id` attribute can be set on the `{{view}}`'s resulting element with - the `id` option. This option will _not_ be passed to `Ember.View.create`. - - ```handlebars - {{#view tagName="span" id="a-custom-id"}} - hello. - {{/view}} - ``` - - Results in the following HTML structure: - - ```html -
    - - hello. - -
    - ``` - - The HTML `class` attribute can be set on the `{{view}}`'s resulting element - with the `class` or `classNameBindings` options. The `class` option will - directly set the CSS `class` attribute and will not be passed to - `Ember.View.create`. `classNameBindings` will be passed to `create` and use - `Ember.View`'s class name binding functionality: - - ```handlebars - {{#view tagName="span" class="a-custom-class"}} - hello. - {{/view}} - ``` - - Results in the following HTML structure: - - ```html -
    - - hello. - -
    - ``` - - ### Supplying a different view class - - `{{view}}` can take an optional first argument before its supplied options to - specify a path to a custom view class. - - ```handlebars - {{#view "MyApp.CustomView"}} - hello. - {{/view}} - ``` - - The first argument can also be a relative path accessible from the current - context. - - ```javascript - MyApp = Ember.Application.create({}); - MyApp.OuterView = Ember.View.extend({ - innerViewClass: Ember.View.extend({ - classNames: ['a-custom-view-class-as-property'] - }), - template: Ember.Handlebars.compile('{{#view "view.innerViewClass"}} hi {{/view}}') - }); - - MyApp.OuterView.create().appendTo('body'); - ``` - - Will result in the following HTML: - - ```html -
    -
    - hi -
    -
    - ``` - - ### Blockless use - - If you supply a custom `Ember.View` subclass that specifies its own template - or provide a `templateName` option to `{{view}}` it can be used without - supplying a block. Attempts to use both a `templateName` option and supply a - block will throw an error. - - ```handlebars - {{view "MyApp.ViewWithATemplateDefined"}} - ``` - - ### `viewName` property - - You can supply a `viewName` option to `{{view}}`. The `Ember.View` instance - will be referenced as a property of its parent view by this name. - - ```javascript - aView = Ember.View.create({ - template: Ember.Handlebars.compile('{{#view viewName="aChildByName"}} hi {{/view}}') - }); - - aView.appendTo('body'); - aView.get('aChildByName') // the instance of Ember.View created by {{view}} helper - ``` - - @method view - @for Ember.Handlebars.helpers - @param {String} path - @param {Hash} options - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('view', function viewHelper(path, options) { - Ember.assert("The view helper only takes a single argument", arguments.length <= 2); - - // If no path is provided, treat path param as options. - if (path && path.data && path.data.isRenderData) { - options = path; - path = "Ember.View"; - } - - return EmberHandlebars.ViewHelper.helper(this, path, options); -}); - - -})(); - - - -(function() { -// TODO: Don't require all of this module -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, handlebarsGet = Ember.Handlebars.get, fmt = Ember.String.fmt; - -/** - `{{collection}}` is a `Ember.Handlebars` helper for adding instances of - `Ember.CollectionView` to a template. See [Ember.CollectionView](/api/classes/Ember.CollectionView.html) - for additional information on how a `CollectionView` functions. - - `{{collection}}`'s primary use is as a block helper with a `contentBinding` - option pointing towards an `Ember.Array`-compatible object. An `Ember.View` - instance will be created for each item in its `content` property. Each view - will have its own `content` property set to the appropriate item in the - collection. - - The provided block will be applied as the template for each item's view. - - Given an empty `` the following template: - - ```handlebars - {{#collection contentBinding="App.items"}} - Hi {{view.content.name}} - {{/collection}} - ``` - - And the following application code - - ```javascript - App = Ember.Application.create() - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] - ``` - - Will result in the HTML structure below - - ```html -
    -
    Hi Dave
    -
    Hi Mary
    -
    Hi Sara
    -
    - ``` - - ### Blockless use in a collection - - If you provide an `itemViewClass` option that has its own `template` you can - omit the block. - - The following template: - - ```handlebars - {{collection contentBinding="App.items" itemViewClass="App.AnItemView"}} - ``` - - And application code - - ```javascript - App = Ember.Application.create(); - App.items = [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ]; - - App.AnItemView = Ember.View.extend({ - template: Ember.Handlebars.compile("Greetings {{view.content.name}}") - }); - ``` - - Will result in the HTML structure below - - ```html -
    -
    Greetings Dave
    -
    Greetings Mary
    -
    Greetings Sara
    -
    - ``` - - ### Specifying a CollectionView subclass - - By default the `{{collection}}` helper will create an instance of - `Ember.CollectionView`. You can supply a `Ember.CollectionView` subclass to - the helper by passing it as the first argument: - - ```handlebars - {{#collection App.MyCustomCollectionClass contentBinding="App.items"}} - Hi {{view.content.name}} - {{/collection}} - ``` - - ### Forwarded `item.*`-named Options - - As with the `{{view}}`, helper options passed to the `{{collection}}` will be - set on the resulting `Ember.CollectionView` as properties. Additionally, - options prefixed with `item` will be applied to the views rendered for each - item (note the camelcasing): - - ```handlebars - {{#collection contentBinding="App.items" - itemTagName="p" - itemClassNames="greeting"}} - Howdy {{view.content.name}} - {{/collection}} - ``` - - Will result in the following HTML structure: - - ```html -
    -

    Howdy Dave

    -

    Howdy Mary

    -

    Howdy Sara

    -
    - ``` - - @method collection - @for Ember.Handlebars.helpers - @param {String} path - @param {Hash} options - @return {String} HTML string - @deprecated Use `{{each}}` helper instead. -*/ -Ember.Handlebars.registerHelper('collection', function collectionHelper(path, options) { - Ember.deprecate("Using the {{collection}} helper without specifying a class has been deprecated as the {{each}} helper now supports the same functionality.", path !== 'collection'); - - // If no path is provided, treat path param as options. - if (path && path.data && path.data.isRenderData) { - options = path; - path = undefined; - Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 1); - } else { - Ember.assert("You cannot pass more than one argument to the collection helper", arguments.length === 2); - } - - var fn = options.fn; - var data = options.data; - var inverse = options.inverse; - var view = options.data.view; - - - var controller, container; - // If passed a path string, convert that into an object. - // Otherwise, just default to the standard class. - var collectionClass; - if (path) { - controller = data.keywords.controller; - container = controller && controller.container; - collectionClass = handlebarsGet(this, path, options) || container.lookupFactory('view:' + path); - Ember.assert(fmt("%@ #collection: Could not find collection class %@", [data.view, path]), !!collectionClass); - } - else { - collectionClass = Ember.CollectionView; - } - - var hash = options.hash, itemHash = {}, match; - - // Extract item view class if provided else default to the standard class - var collectionPrototype = collectionClass.proto(), - itemViewClass; - - if (hash.itemView) { - controller = data.keywords.controller; - Ember.assert('You specified an itemView, but the current context has no ' + - 'container to look the itemView up in. This probably means ' + - 'that you created a view manually, instead of through the ' + - 'container. Instead, use container.lookup("view:viewName"), ' + - 'which will properly instantiate your view.', - controller && controller.container); - container = controller.container; - itemViewClass = container.lookupFactory('view:' + hash.itemView); - Ember.assert('You specified the itemView ' + hash.itemView + ", but it was " + - "not found at " + container.describe("view:" + hash.itemView) + - " (and it was not registered in the container)", !!itemViewClass); - } else if (hash.itemViewClass) { - itemViewClass = handlebarsGet(collectionPrototype, hash.itemViewClass, options); - } else { - itemViewClass = collectionPrototype.itemViewClass; - } - - Ember.assert(fmt("%@ #collection: Could not find itemViewClass %@", [data.view, itemViewClass]), !!itemViewClass); - - delete hash.itemViewClass; - delete hash.itemView; - - // Go through options passed to the {{collection}} helper and extract options - // that configure item views instead of the collection itself. - for (var prop in hash) { - if (hash.hasOwnProperty(prop)) { - match = prop.match(/^item(.)(.*)$/); - - if (match && prop !== 'itemController') { - // Convert itemShouldFoo -> shouldFoo - itemHash[match[1].toLowerCase() + match[2]] = hash[prop]; - // Delete from hash as this will end up getting passed to the - // {{view}} helper method. - delete hash[prop]; - } - } - } - - if (fn) { - itemHash.template = fn; - delete options.fn; - } - - var emptyViewClass; - if (inverse && inverse !== Ember.Handlebars.VM.noop) { - emptyViewClass = get(collectionPrototype, 'emptyViewClass'); - emptyViewClass = emptyViewClass.extend({ - template: inverse, - tagName: itemHash.tagName - }); - } else if (hash.emptyViewClass) { - emptyViewClass = handlebarsGet(this, hash.emptyViewClass, options); - } - if (emptyViewClass) { hash.emptyView = emptyViewClass; } - - if (!hash.keyword) { - itemHash._context = Ember.computed.alias('content'); - } - - var viewOptions = Ember.Handlebars.ViewHelper.propertiesFromHTMLOptions({ data: data, hash: itemHash }, this); - hash.itemViewClass = itemViewClass.extend(viewOptions); - - return Ember.Handlebars.helpers.view.call(this, collectionClass, options); -}); - - -})(); - - - -(function() { -/*globals Handlebars */ -/** -@module ember -@submodule ember-handlebars -*/ - -var handlebarsGet = Ember.Handlebars.get; - -/** - `unbound` allows you to output a property without binding. *Important:* The - output will not be updated if the property changes. Use with caution. - - ```handlebars -
    {{unbound somePropertyThatDoesntChange}}
    - ``` - - `unbound` can also be used in conjunction with a bound helper to - render it in its unbound form: - - ```handlebars -
    {{unbound helperName somePropertyThatDoesntChange}}
    - ``` - - @method unbound - @for Ember.Handlebars.helpers - @param {String} property - @return {String} HTML string -*/ -Ember.Handlebars.registerHelper('unbound', function unboundHelper(property, fn) { - var options = arguments[arguments.length - 1], helper, context, out; - - if (arguments.length > 2) { - // Unbound helper call. - options.data.isUnbound = true; - helper = Ember.Handlebars.helpers[arguments[0]] || Ember.Handlebars.helpers.helperMissing; - out = helper.apply(this, Array.prototype.slice.call(arguments, 1)); - delete options.data.isUnbound; - return out; - } - - context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; - return handlebarsGet(context, property, fn); -}); - -})(); - - - -(function() { -/*jshint debug:true*/ -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get; -var handlebarsGet = Ember.Handlebars.get, normalizePath = Ember.Handlebars.normalizePath; -var a_slice = [].slice; - -/** - `log` allows you to output the value of variables in the current rendering - context. `log` also accepts primitive types such as strings or numbers. - - ```handlebars - {{log "myVariable:" myVariable }} - ``` - - @method log - @for Ember.Handlebars.helpers - @param {String} property -*/ -Ember.Handlebars.registerHelper('log', function logHelper() { - var params = a_slice.call(arguments, 0, -1), - options = arguments[arguments.length - 1], - logger = Ember.Logger.log, - values = [], - allowPrimitives = false; - - - allowPrimitives = true; - - - for (var i = 0; i < params.length; i++) { - var type = options.types[i]; - - if (type === 'ID' || !allowPrimitives) { - var context = (options.contexts && options.contexts[i]) || this, - normalized = normalizePath(context, params[i], options.data); - - if (normalized.path === 'this') { - values.push(normalized.root); - } else { - values.push(handlebarsGet(normalized.root, normalized.path, options)); - } - } else { - values.push(params[i]); - } - } - - logger.apply(logger, values); -}); - -/** - Execute the `debugger` statement in the current context. - - ```handlebars - {{debugger}} - ``` - - Before invoking the `debugger` statement, there - are a few helpful variables defined in the - body of this helper that you can inspect while - debugging that describe how and where this - helper was invoked: - - - templateContext: this is most likely a controller - from which this template looks up / displays properties - - typeOfTemplateContext: a string description of - what the templateContext is - - For example, if you're wondering why a value `{{foo}}` - isn't rendering as expected within a template, you - could place a `{{debugger}}` statement, and when - the `debugger;` breakpoint is hit, you can inspect - `templateContext`, determine if it's the object you - expect, and/or evaluate expressions in the console - to perform property lookups on the `templateContext`: - - ``` - > templateContext.get('foo') // -> "" - ``` - - @method debugger - @for Ember.Handlebars.helpers - @param {String} property -*/ -Ember.Handlebars.registerHelper('debugger', function debuggerHelper(options) { - - // These are helpful values you can inspect while debugging. - var templateContext = this; - var typeOfTemplateContext = Ember.inspect(templateContext); - - debugger; -}); - - - -})(); - - - -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, set = Ember.set; -var fmt = Ember.String.fmt; - -Ember.Handlebars.EachView = Ember.CollectionView.extend(Ember._Metamorph, { - init: function() { - var itemController = get(this, 'itemController'); - var binding; - - if (itemController) { - var controller = get(this, 'controller.container').lookupFactory('controller:array').create({ - _isVirtual: true, - parentController: get(this, 'controller'), - itemController: itemController, - target: get(this, 'controller'), - _eachView: this - }); - - this.disableContentObservers(function() { - set(this, 'content', controller); - binding = new Ember.Binding('content', '_eachView.dataSource').oneWay(); - binding.connect(controller); - }); - - set(this, '_arrayController', controller); - } else { - this.disableContentObservers(function() { - binding = new Ember.Binding('content', 'dataSource').oneWay(); - binding.connect(this); - }); - } - - return this._super(); - }, - - _assertArrayLike: function(content) { - Ember.assert(fmt("The value that #each loops over must be an Array. You " + - "passed %@, but it should have been an ArrayController", - [content.constructor]), - !Ember.ControllerMixin.detect(content) || - (content && content.isGenerated) || - content instanceof Ember.ArrayController); - Ember.assert(fmt("The value that #each loops over must be an Array. You passed %@", [(Ember.ControllerMixin.detect(content) && content.get('model') !== undefined) ? fmt("'%@' (wrapped in %@)", [content.get('model'), content]) : content]), Ember.Array.detect(content)); - }, - - disableContentObservers: function(callback) { - Ember.removeBeforeObserver(this, 'content', null, '_contentWillChange'); - Ember.removeObserver(this, 'content', null, '_contentDidChange'); - - callback.call(this); - - Ember.addBeforeObserver(this, 'content', null, '_contentWillChange'); - Ember.addObserver(this, 'content', null, '_contentDidChange'); - }, - - itemViewClass: Ember._MetamorphView, - emptyViewClass: Ember._MetamorphView, - - createChildView: function(view, attrs) { - view = this._super(view, attrs); - - // At the moment, if a container view subclass wants - // to insert keywords, it is responsible for cloning - // the keywords hash. This will be fixed momentarily. - var keyword = get(this, 'keyword'); - var content = get(view, 'content'); - - if (keyword) { - var data = get(view, 'templateData'); - - data = Ember.copy(data); - data.keywords = view.cloneKeywords(); - set(view, 'templateData', data); - - // In this case, we do not bind, because the `content` of - // a #each item cannot change. - data.keywords[keyword] = content; - } - - // If {{#each}} is looping over an array of controllers, - // point each child view at their respective controller. - if (content && content.isController) { - set(view, 'controller', content); - } - - return view; - }, - - destroy: function() { - if (!this._super()) { return; } - - var arrayController = get(this, '_arrayController'); - - if (arrayController) { - arrayController.destroy(); - } - - return this; - } -}); - -// Defeatureify doesn't seem to like nested functions that need to be removed -function _addMetamorphCheck() { - Ember.Handlebars.EachView.reopen({ - _checkMetamorph: Ember.on('didInsertElement', function() { - Ember.assert("The metamorph tags, " + - this.morph.start + " and " + this.morph.end + - ", have different parents.\nThe browser has fixed your template to output valid HTML (for example, check that you have properly closed all tags and have used a TBODY tag when creating a table with '{{#each}}')", - document.getElementById( this.morph.start ).parentNode === - document.getElementById( this.morph.end ).parentNode - ); - }) - }); -} - -Ember.runInDebug( function() { - _addMetamorphCheck(); -}); - -var GroupedEach = Ember.Handlebars.GroupedEach = function(context, path, options) { - var self = this, - normalized = Ember.Handlebars.normalizePath(context, path, options.data); - - this.context = context; - this.path = path; - this.options = options; - this.template = options.fn; - this.containingView = options.data.view; - this.normalizedRoot = normalized.root; - this.normalizedPath = normalized.path; - this.content = this.lookupContent(); - - this.addContentObservers(); - this.addArrayObservers(); - - this.containingView.on('willClearRender', function() { - self.destroy(); - }); -}; - -GroupedEach.prototype = { - contentWillChange: function() { - this.removeArrayObservers(); - }, - - contentDidChange: function() { - this.content = this.lookupContent(); - this.addArrayObservers(); - this.rerenderContainingView(); - }, - - contentArrayWillChange: Ember.K, - - contentArrayDidChange: function() { - this.rerenderContainingView(); - }, - - lookupContent: function() { - return Ember.Handlebars.get(this.normalizedRoot, this.normalizedPath, this.options); - }, - - addArrayObservers: function() { - if (!this.content) { return; } - - this.content.addArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, - - removeArrayObservers: function() { - if (!this.content) { return; } - - this.content.removeArrayObserver(this, { - willChange: 'contentArrayWillChange', - didChange: 'contentArrayDidChange' - }); - }, - - addContentObservers: function() { - Ember.addBeforeObserver(this.normalizedRoot, this.normalizedPath, this, this.contentWillChange); - Ember.addObserver(this.normalizedRoot, this.normalizedPath, this, this.contentDidChange); - }, - - removeContentObservers: function() { - Ember.removeBeforeObserver(this.normalizedRoot, this.normalizedPath, this.contentWillChange); - Ember.removeObserver(this.normalizedRoot, this.normalizedPath, this.contentDidChange); - }, - - render: function() { - if (!this.content) { return; } - - var content = this.content, - contentLength = get(content, 'length'), - data = this.options.data, - template = this.template; - - data.insideEach = true; - for (var i = 0; i < contentLength; i++) { - template(content.objectAt(i), { data: data }); - } - }, - - rerenderContainingView: function() { - var self = this; - Ember.run.scheduleOnce('render', this, function() { - // It's possible it's been destroyed after we enqueued a re-render call. - if (!self.destroyed) { - self.containingView.rerender(); - } - }); - }, - - destroy: function() { - this.removeContentObservers(); - if (this.content) { - this.removeArrayObservers(); - } - this.destroyed = true; - } -}; - -/** - The `{{#each}}` helper loops over elements in a collection, rendering its - block once for each item. It is an extension of the base Handlebars `{{#each}}` - helper: - - ```javascript - Developers = [{name: 'Yehuda'},{name: 'Tom'}, {name: 'Paul'}]; - ``` - - ```handlebars - {{#each Developers}} - {{name}} - {{/each}} - ``` - - `{{each}}` supports an alternative syntax with element naming: - - ```handlebars - {{#each person in Developers}} - {{person.name}} - {{/each}} - ``` - - When looping over objects that do not have properties, `{{this}}` can be used - to render the object: - - ```javascript - DeveloperNames = ['Yehuda', 'Tom', 'Paul'] - ``` - - ```handlebars - {{#each DeveloperNames}} - {{this}} - {{/each}} - ``` - ### {{else}} condition - `{{#each}}` can have a matching `{{else}}`. The contents of this block will render - if the collection is empty. - - ``` - {{#each person in Developers}} - {{person.name}} - {{else}} -

    Sorry, nobody is available for this task.

    - {{/each}} - ``` - ### Specifying a View class for items - If you provide an `itemViewClass` option that references a view class - with its own `template` you can omit the block. - - The following template: - - ```handlebars - {{#view App.MyView }} - {{each view.items itemViewClass="App.AnItemView"}} - {{/view}} - ``` - - And application code - - ```javascript - App = Ember.Application.create({ - MyView: Ember.View.extend({ - items: [ - Ember.Object.create({name: 'Dave'}), - Ember.Object.create({name: 'Mary'}), - Ember.Object.create({name: 'Sara'}) - ] - }) - }); - - App.AnItemView = Ember.View.extend({ - template: Ember.Handlebars.compile("Greetings {{name}}") - }); - ``` - - Will result in the HTML structure below - - ```html -
    -
    Greetings Dave
    -
    Greetings Mary
    -
    Greetings Sara
    -
    - ``` - - If an `itemViewClass` is defined on the helper, and therefore the helper is not - being used as a block, an `emptyViewClass` can also be provided optionally. - The `emptyViewClass` will match the behavior of the `{{else}}` condition - described above. That is, the `emptyViewClass` will render if the collection - is empty. - - ### Representing each item with a Controller. - By default the controller lookup within an `{{#each}}` block will be - the controller of the template where the `{{#each}}` was used. If each - item needs to be presented by a custom controller you can provide a - `itemController` option which references a controller by lookup name. - Each item in the loop will be wrapped in an instance of this controller - and the item itself will be set to the `content` property of that controller. - - This is useful in cases where properties of model objects need transformation - or synthesis for display: - - ```javascript - App.DeveloperController = Ember.ObjectController.extend({ - isAvailableForHire: function() { - return !this.get('content.isEmployed') && this.get('content.isSeekingWork'); - }.property('isEmployed', 'isSeekingWork') - }) - ``` - - ```handlebars - {{#each person in developers itemController="developer"}} - {{person.name}} {{#if person.isAvailableForHire}}Hire me!{{/if}} - {{/each}} - ``` - - Each itemController will receive a reference to the current controller as - a `parentController` property. - - ### (Experimental) Grouped Each - - When used in conjunction with the experimental [group helper](https://github.com/emberjs/group-helper), - you can inform Handlebars to re-render an entire group of items instead of - re-rendering them one at a time (in the event that they are changed en masse - or an item is added/removed). - - ```handlebars - {{#group}} - {{#each people}} - {{firstName}} {{lastName}} - {{/each}} - {{/group}} - ``` - - This can be faster than the normal way that Handlebars re-renders items - in some cases. - - If for some reason you have a group with more than one `#each`, you can make - one of the collections be updated in normal (non-grouped) fashion by setting - the option `groupedRows=true` (counter-intuitive, I know). - - For example, - - ```handlebars - {{dealershipName}} - - {{#group}} - {{#each dealers}} - {{firstName}} {{lastName}} - {{/each}} - - {{#each car in cars groupedRows=true}} - {{car.make}} {{car.model}} {{car.color}} - {{/each}} - {{/group}} - ``` - Any change to `dealershipName` or the `dealers` collection will cause the - entire group to be re-rendered. However, changes to the `cars` collection - will be re-rendered individually (as normal). - - Note that `group` behavior is also disabled by specifying an `itemViewClass`. - - @method each - @for Ember.Handlebars.helpers - @param [name] {String} name for item (used with `in`) - @param [path] {String} path - @param [options] {Object} Handlebars key/value pairs of options - @param [options.itemViewClass] {String} a path to a view class used for each item - @param [options.itemController] {String} name of a controller to be created for each item - @param [options.groupedRows] {boolean} enable normal item-by-item rendering when inside a `#group` helper -*/ -Ember.Handlebars.registerHelper('each', function eachHelper(path, options) { - if (arguments.length === 4) { - Ember.assert("If you pass more than one argument to the each helper, it must be in the form #each foo in bar", arguments[1] === "in"); - - var keywordName = arguments[0]; - - options = arguments[3]; - path = arguments[2]; - if (path === '') { path = "this"; } - - options.hash.keyword = keywordName; - } - - if (arguments.length === 1) { - options = path; - path = 'this'; - } - - options.hash.dataSourceBinding = path; - // Set up emptyView as a metamorph with no tag - //options.hash.emptyViewClass = Ember._MetamorphView; - - if (options.data.insideGroup && !options.hash.groupedRows && !options.hash.itemViewClass) { - new Ember.Handlebars.GroupedEach(this, path, options).render(); - } else { - return Ember.Handlebars.helpers.collection.call(this, 'Ember.Handlebars.EachView', options); - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -/** - `template` allows you to render a template from inside another template. - This allows you to re-use the same template in multiple places. For example: - - ```html - - ``` - - ```html - - ``` - - ```handlebars - {{#if isUser}} - {{template "user_info"}} - {{else}} - {{template "unlogged_user_info"}} - {{/if}} - ``` - - This helper looks for templates in the global `Ember.TEMPLATES` hash. If you - add ` - ``` - - Take note that `"welcome"` is a string and not an object - reference. - - @method loc - @for Ember.Handlebars.helpers - @param {String} str The string to format -*/ - -Ember.Handlebars.registerHelper('loc', function locHelper(str) { - return Ember.String.loc(str); -}); - -})(); - - - -(function() { - -})(); - - - -(function() { - -})(); - - - -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -var set = Ember.set, get = Ember.get; - -/** - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `checkbox`. - - See [handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - - ## Direct manipulation of `checked` - - The `checked` attribute of an `Ember.Checkbox` object should always be set - through the Ember object or by interacting with its rendered element - representation via the mouse, keyboard, or touch. Updating the value of the - checkbox via jQuery will result in the checked value of the object and its - element losing synchronization. - - ## Layout and LayoutName properties - - Because HTML `input` elements are self closing `layout` and `layoutName` - properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s - layout section for more information. - - @class Checkbox - @namespace Ember - @extends Ember.View -*/ -Ember.Checkbox = Ember.View.extend({ - classNames: ['ember-checkbox'], - - tagName: 'input', - - attributeBindings: ['type', 'checked', 'indeterminate', 'disabled', 'tabindex', 'name', - 'autofocus', 'form'], - - type: "checkbox", - checked: false, - disabled: false, - indeterminate: false, - - init: function() { - this._super(); - this.on("change", this, this._updateElementValue); - }, - - didInsertElement: function() { - this._super(); - this.get('element').indeterminate = !!this.get('indeterminate'); - }, - - _updateElementValue: function() { - set(this, 'checked', this.$().prop('checked')); - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, set = Ember.set; - -/** - Shared mixin used by `Ember.TextField` and `Ember.TextArea`. - - @class TextSupport - @namespace Ember - @uses Ember.TargetActionSupport - @extends Ember.Mixin - @private -*/ -Ember.TextSupport = Ember.Mixin.create(Ember.TargetActionSupport, { - value: "", - - attributeBindings: ['placeholder', 'disabled', 'maxlength', 'tabindex', 'readonly', - 'autofocus', 'form', 'selectionDirection', 'spellcheck', 'required'], - placeholder: null, - disabled: false, - maxlength: null, - - init: function() { - this._super(); - this.on("focusOut", this, this._elementValueDidChange); - this.on("change", this, this._elementValueDidChange); - this.on("paste", this, this._elementValueDidChange); - this.on("cut", this, this._elementValueDidChange); - this.on("input", this, this._elementValueDidChange); - this.on("keyUp", this, this.interpretKeyEvents); - }, - - /** - The action to be sent when the user presses the return key. - - This is similar to the `{{action}}` helper, but is fired when - the user presses the return key when editing a text field, and sends - the value of the field as the context. - - @property action - @type String - @default null - */ - action: null, - - /** - The event that should send the action. - - Options are: - - * `enter`: the user pressed enter - * `keyPress`: the user pressed a key - - @property onEvent - @type String - @default enter - */ - onEvent: 'enter', - - /** - Whether they `keyUp` event that triggers an `action` to be sent continues - propagating to other views. - - By default, when the user presses the return key on their keyboard and - the text field has an `action` set, the action will be sent to the view's - controller and the key event will stop propagating. - - If you would like parent views to receive the `keyUp` event even after an - action has been dispatched, set `bubbles` to true. - - @property bubbles - @type Boolean - @default false - */ - bubbles: false, - - interpretKeyEvents: function(event) { - var map = Ember.TextSupport.KEY_EVENTS; - var method = map[event.keyCode]; - - this._elementValueDidChange(); - if (method) { return this[method](event); } - }, - - _elementValueDidChange: function() { - set(this, 'value', this.$().val()); - }, - - /** - The action to be sent when the user inserts a new line. - - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 13. - Uses sendAction to send the `enter` action to the controller. - - @method insertNewline - @param {Event} event - */ - insertNewline: function(event) { - sendAction('enter', this, event); - sendAction('insert-newline', this, event); - }, - - /** - Called when the user hits escape. - - Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 27. - Uses sendAction to send the `escape-press` action to the controller. - - @method cancel - @param {Event} event - */ - cancel: function(event) { - sendAction('escape-press', this, event); - }, - - /** - Called when the text area is focused. - - @method focusIn - @param {Event} event - */ - focusIn: function(event) { - sendAction('focus-in', this, event); - }, - - /** - Called when the text area is blurred. - - @method focusOut - @param {Event} event - */ - focusOut: function(event) { - sendAction('focus-out', this, event); - }, - - /** - The action to be sent when the user presses a key. Enabled by setting - the `onEvent` property to `keyPress`. - - Uses sendAction to send the `keyPress` action to the controller. - - @method keyPress - @param {Event} event - */ - keyPress: function(event) { - sendAction('key-press', this, event); - } - -}); - -Ember.TextSupport.KEY_EVENTS = { - 13: 'insertNewline', - 27: 'cancel' -}; - -// In principle, this shouldn't be necessary, but the legacy -// sectionAction semantics for TextField are different from -// the component semantics so this method normalizes them. -function sendAction(eventName, view, event) { - var action = get(view, eventName), - on = get(view, 'onEvent'), - value = get(view, 'value'); - - // back-compat support for keyPress as an event name even though - // it's also a method name that consumes the event (and therefore - // incompatible with sendAction semantics). - if (on === eventName || (on === 'keyPress' && eventName === 'key-press')) { - view.sendAction('action', value); - } - - view.sendAction(eventName, value); - - if (action || on === eventName) { - if(!get(view, 'bubbles')) { - event.stopPropagation(); - } - } -} - -})(); - - - -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, set = Ember.set; - -/** - - The internal class used to create text inputs when the `{{input}}` - helper is used with `type` of `text`. - - See [Handlebars.helpers.input](/api/classes/Ember.Handlebars.helpers.html#method_input) for usage details. - - ## Layout and LayoutName properties - - Because HTML `input` elements are self closing `layout` and `layoutName` - properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s - layout section for more information. - - @class TextField - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport -*/ -Ember.TextField = Ember.Component.extend(Ember.TextSupport, { - - classNames: ['ember-text-field'], - tagName: "input", - attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max', - 'accept', 'autocomplete', 'autosave', 'formaction', - 'formenctype', 'formmethod', 'formnovalidate', 'formtarget', - 'height', 'inputmode', 'list', 'multiple', 'pattern', 'step', - 'width'], - - /** - The `value` attribute of the input element. As the user inputs text, this - property is updated live. - - @property value - @type String - @default "" - */ - value: "", - - /** - The `type` attribute of the input element. - - @property type - @type String - @default "text" - */ - type: "text", - - /** - The `size` of the text field in characters. - - @property size - @type String - @default null - */ - size: null, - - /** - The `pattern` attribute of input element. - - @property pattern - @type String - @default null - */ - pattern: null, - - /** - The `min` attribute of input element used with `type="number"` or `type="range"`. - - @property min - @type String - @default null - */ - min: null, - - /** - The `max` attribute of input element used with `type="number"` or `type="range"`. - - @property max - @type String - @default null - */ - max: null -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-handlebars -*/ - -var get = Ember.get, set = Ember.set; - -/** - The internal class used to create textarea element when the `{{textarea}}` - helper is used. - - See [handlebars.helpers.textarea](/api/classes/Ember.Handlebars.helpers.html#method_textarea) for usage details. - - ## Layout and LayoutName properties - - Because HTML `textarea` elements do not contain inner HTML the `layout` and - `layoutName` properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s - layout section for more information. - - @class TextArea - @namespace Ember - @extends Ember.Component - @uses Ember.TextSupport -*/ -Ember.TextArea = Ember.Component.extend(Ember.TextSupport, { - classNames: ['ember-text-area'], - - tagName: "textarea", - attributeBindings: ['rows', 'cols', 'name', 'selectionEnd', 'selectionStart', 'wrap'], - rows: null, - cols: null, - - _updateElementValue: Ember.observer('value', function() { - // We do this check so cursor position doesn't get affected in IE - var value = get(this, 'value'), - $el = this.$(); - if ($el && value !== $el.val()) { - $el.val(value); - } - }), - - init: function() { - this._super(); - this.on("didInsertElement", this, this._updateElementValue); - } - -}); - -})(); - - - -(function() { -/*jshint eqeqeq:false */ - -/** -@module ember -@submodule ember-handlebars -*/ - -var set = Ember.set, - get = Ember.get, - indexOf = Ember.EnumerableUtils.indexOf, - indexesOf = Ember.EnumerableUtils.indexesOf, - forEach = Ember.EnumerableUtils.forEach, - replace = Ember.EnumerableUtils.replace, - isArray = Ember.isArray, - precompileTemplate = Ember.Handlebars.compile; - -Ember.SelectOption = Ember.View.extend({ - tagName: 'option', - attributeBindings: ['value', 'selected'], - - defaultTemplate: function(context, options) { - options = { data: options.data, hash: {} }; - Ember.Handlebars.helpers.bind.call(context, "view.label", options); - }, - - init: function() { - this.labelPathDidChange(); - this.valuePathDidChange(); - - this._super(); - }, - - selected: Ember.computed(function() { - var content = get(this, 'content'), - selection = get(this, 'parentView.selection'); - if (get(this, 'parentView.multiple')) { - return selection && indexOf(selection, content.valueOf()) > -1; - } else { - // Primitives get passed through bindings as objects... since - // `new Number(4) !== 4`, we use `==` below - return content == selection; - } - }).property('content', 'parentView.selection'), - - labelPathDidChange: Ember.observer('parentView.optionLabelPath', function() { - var labelPath = get(this, 'parentView.optionLabelPath'); - - if (!labelPath) { return; } - - Ember.defineProperty(this, 'label', Ember.computed(function() { - return get(this, labelPath); - }).property(labelPath)); - }), - - valuePathDidChange: Ember.observer('parentView.optionValuePath', function() { - var valuePath = get(this, 'parentView.optionValuePath'); - - if (!valuePath) { return; } - - Ember.defineProperty(this, 'value', Ember.computed(function() { - return get(this, valuePath); - }).property(valuePath)); - }) -}); - -Ember.SelectOptgroup = Ember.CollectionView.extend({ - tagName: 'optgroup', - attributeBindings: ['label'], - - selectionBinding: 'parentView.selection', - multipleBinding: 'parentView.multiple', - optionLabelPathBinding: 'parentView.optionLabelPath', - optionValuePathBinding: 'parentView.optionValuePath', - - itemViewClassBinding: 'parentView.optionView' -}); - -/** - The `Ember.Select` view class renders a - [select](https://developer.mozilla.org/en/HTML/Element/select) HTML element, - allowing the user to choose from a list of options. - - The text and `value` property of each ` - - - ``` - - The `value` attribute of the selected `"); - return buffer; - } - -function program3(depth0,data) { - - var stack1; - stack1 = helpers.each.call(depth0, "view.groupedContent", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(4, program4, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - else { data.buffer.push(''); } - } -function program4(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.groupView", {hash:{ - 'content': ("content"), - 'label': ("label") - },hashTypes:{'content': "ID",'label': "ID"},hashContexts:{'content': depth0,'label': depth0},contexts:[depth0],types:["ID"],data:data}))); - } - -function program6(depth0,data) { - - var stack1; - stack1 = helpers.each.call(depth0, "view.content", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(7, program7, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - else { data.buffer.push(''); } - } -function program7(depth0,data) { - - - data.buffer.push(escapeExpression(helpers.view.call(depth0, "view.optionView", {hash:{ - 'content': ("") - },hashTypes:{'content': "ID"},hashContexts:{'content': depth0},contexts:[depth0],types:["ID"],data:data}))); - } - - stack1 = helpers['if'].call(depth0, "view.prompt", {hash:{},hashTypes:{},hashContexts:{},inverse:self.noop,fn:self.program(1, program1, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - stack1 = helpers['if'].call(depth0, "view.optionGroupPath", {hash:{},hashTypes:{},hashContexts:{},inverse:self.program(6, program6, data),fn:self.program(3, program3, data),contexts:[depth0],types:["ID"],data:data}); - if(stack1 || stack1 === 0) { data.buffer.push(stack1); } - return buffer; - -}), - attributeBindings: ['multiple', 'disabled', 'tabindex', 'name', 'required', 'autofocus', - 'form', 'size'], - - /** - The `multiple` attribute of the select element. Indicates whether multiple - options can be selected. - - @property multiple - @type Boolean - @default false - */ - multiple: false, - - /** - The `disabled` attribute of the select element. Indicates whether - the element is disabled from interactions. - - @property disabled - @type Boolean - @default false - */ - disabled: false, - - /** - The `required` attribute of the select element. Indicates whether - a selected option is required for form validation. - - @property required - @type Boolean - @default false - */ - required: false, - - /** - The list of options. - - If `optionLabelPath` and `optionValuePath` are not overridden, this should - be a list of strings, which will serve simultaneously as labels and values. - - Otherwise, this should be a list of objects. For instance: - - ```javascript - Ember.Select.create({ - content: Ember.A([ - { id: 1, firstName: 'Yehuda' }, - { id: 2, firstName: 'Tom' } - ]), - optionLabelPath: 'content.firstName', - optionValuePath: 'content.id' - }); - ``` - - @property content - @type Array - @default null - */ - content: null, - - /** - When `multiple` is `false`, the element of `content` that is currently - selected, if any. - - When `multiple` is `true`, an array of such elements. - - @property selection - @type Object or Array - @default null - */ - selection: null, - - /** - In single selection mode (when `multiple` is `false`), value can be used to - get the current selection's value or set the selection by it's value. - - It is not currently supported in multiple selection mode. - - @property value - @type String - @default null - */ - value: Ember.computed(function(key, value) { - if (arguments.length === 2) { return value; } - var valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''); - return valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection'); - }).property('selection'), - - /** - If given, a top-most dummy option will be rendered to serve as a user - prompt. - - @property prompt - @type String - @default null - */ - prompt: null, - - /** - The path of the option labels. See [content](/api/classes/Ember.Select.html#property_content). - - @property optionLabelPath - @type String - @default 'content' - */ - optionLabelPath: 'content', - - /** - The path of the option values. See [content](/api/classes/Ember.Select.html#property_content). - - @property optionValuePath - @type String - @default 'content' - */ - optionValuePath: 'content', - - /** - The path of the option group. - When this property is used, `content` should be sorted by `optionGroupPath`. - - @property optionGroupPath - @type String - @default null - */ - optionGroupPath: null, - - /** - The view class for optgroup. - - @property groupView - @type Ember.View - @default Ember.SelectOptgroup - */ - groupView: Ember.SelectOptgroup, - - groupedContent: Ember.computed(function() { - var groupPath = get(this, 'optionGroupPath'); - var groupedContent = Ember.A(); - var content = get(this, 'content') || []; - - forEach(content, function(item) { - var label = get(item, groupPath); - - if (get(groupedContent, 'lastObject.label') !== label) { - groupedContent.pushObject({ - label: label, - content: Ember.A() - }); - } - - get(groupedContent, 'lastObject.content').push(item); - }); - - return groupedContent; - }).property('optionGroupPath', 'content.@each'), - - /** - The view class for option. - - @property optionView - @type Ember.View - @default Ember.SelectOption - */ - optionView: Ember.SelectOption, - - _change: function() { - if (get(this, 'multiple')) { - this._changeMultiple(); - } else { - this._changeSingle(); - } - }, - - selectionDidChange: Ember.observer('selection.@each', function() { - var selection = get(this, 'selection'); - if (get(this, 'multiple')) { - if (!isArray(selection)) { - set(this, 'selection', Ember.A([selection])); - return; - } - this._selectionDidChangeMultiple(); - } else { - this._selectionDidChangeSingle(); - } - }), - - valueDidChange: Ember.observer('value', function() { - var content = get(this, 'content'), - value = get(this, 'value'), - valuePath = get(this, 'optionValuePath').replace(/^content\.?/, ''), - selectedValue = (valuePath ? get(this, 'selection.' + valuePath) : get(this, 'selection')), - selection; - - if (value !== selectedValue) { - selection = content ? content.find(function(obj) { - return value === (valuePath ? get(obj, valuePath) : obj); - }) : null; - - this.set('selection', selection); - } - }), - - - _triggerChange: function() { - var selection = get(this, 'selection'); - var value = get(this, 'value'); - - if (!Ember.isNone(selection)) { this.selectionDidChange(); } - if (!Ember.isNone(value)) { this.valueDidChange(); } - - this._change(); - }, - - _changeSingle: function() { - var selectedIndex = this.$()[0].selectedIndex, - content = get(this, 'content'), - prompt = get(this, 'prompt'); - - if (!content || !get(content, 'length')) { return; } - if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; } - - if (prompt) { selectedIndex -= 1; } - set(this, 'selection', content.objectAt(selectedIndex)); - }, - - - _changeMultiple: function() { - var options = this.$('option:selected'), - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - content = get(this, 'content'), - selection = get(this, 'selection'); - - if (!content) { return; } - if (options) { - var selectedIndexes = options.map(function() { - return this.index - offset; - }).toArray(); - var newSelection = content.objectsAt(selectedIndexes); - - if (isArray(selection)) { - replace(selection, 0, get(selection, 'length'), newSelection); - } else { - set(this, 'selection', newSelection); - } - } - }, - - _selectionDidChangeSingle: function() { - var el = this.get('element'); - if (!el) { return; } - - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectionIndex = content ? indexOf(content, selection) : -1, - prompt = get(this, 'prompt'); - - if (prompt) { selectionIndex += 1; } - if (el) { el.selectedIndex = selectionIndex; } - }, - - _selectionDidChangeMultiple: function() { - var content = get(this, 'content'), - selection = get(this, 'selection'), - selectedIndexes = content ? indexesOf(content, selection) : [-1], - prompt = get(this, 'prompt'), - offset = prompt ? 1 : 0, - options = this.$('option'), - adjusted; - - if (options) { - options.each(function() { - adjusted = this.index > -1 ? this.index - offset : -1; - this.selected = indexOf(selectedIndexes, adjusted) > -1; - }); - } - }, - - init: function() { - this._super(); - this.on("didInsertElement", this, this._triggerChange); - this.on("change", this, this._change); - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-handlebars-compiler -*/ - -/** - - The `{{input}}` helper inserts an HTML `` tag into the template, - with a `type` value of either `text` or `checkbox`. If no `type` is provided, - `text` will be the default value applied. The attributes of `{{input}}` - match those of the native HTML tag as closely as possible for these two types. - - ## Use as text field - An `{{input}}` with no `type` or a `type` of `text` will render an HTML text input. - The following HTML attributes can be set via the helper: - - - - - - - - - - - - -
    `readonly``required``autofocus`
    `value``placeholder``disabled`
    `size``tabindex``maxlength`
    `name``min``max`
    `pattern``accept``autocomplete`
    `autosave``formaction``formenctype`
    `formmethod``formnovalidate``formtarget`
    `height``inputmode``multiple`
    `step``width``form`
    `selectionDirection``spellcheck` 
    - - - When set to a quoted string, these values will be directly applied to the HTML - element. When left unquoted, these values will be bound to a property on the - template's current rendering context (most typically a controller instance). - - ## Unbound: - - ```handlebars - {{input value="http://www.facebook.com"}} - ``` - - - ```html - - ``` - - ## Bound: - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - firstName: "Stanley", - entryNotAllowed: true - }); - ``` - - - ```handlebars - {{input type="text" value=firstName disabled=entryNotAllowed size="50"}} - ``` - - - ```html - - ``` - - ## Extension - - Internally, `{{input type="text"}}` creates an instance of `Ember.TextField`, passing - arguments from the helper to `Ember.TextField`'s `create` method. You can extend the - capablilties of text inputs in your applications by reopening this class. For example, - if you are deploying to browsers where the `required` attribute is used, you - can add this to the `TextField`'s `attributeBindings` property: - - - ```javascript - Ember.TextField.reopen({ - attributeBindings: ['required'] - }); - ``` - - Keep in mind when writing `Ember.TextField` subclasses that `Ember.TextField` - itself extends `Ember.Component`, meaning that it does NOT inherit - the `controller` of the parent view. - - See more about [Ember components](api/classes/Ember.Component.html) - - - ## Use as checkbox - - An `{{input}}` with a `type` of `checkbox` will render an HTML checkbox input. - The following HTML attributes can be set via the helper: - -* `checked` -* `disabled` -* `tabindex` -* `indeterminate` -* `name` -* `autofocus` -* `form` - - - When set to a quoted string, these values will be directly applied to the HTML - element. When left unquoted, these values will be bound to a property on the - template's current rendering context (most typically a controller instance). - - ## Unbound: - - ```handlebars - {{input type="checkbox" name="isAdmin"}} - ``` - - ```html - - ``` - - ## Bound: - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - isAdmin: true - }); - ``` - - - ```handlebars - {{input type="checkbox" checked=isAdmin }} - ``` - - - ```html - - ``` - - ## Extension - - Internally, `{{input type="checkbox"}}` creates an instance of `Ember.Checkbox`, passing - arguments from the helper to `Ember.Checkbox`'s `create` method. You can extend the - capablilties of checkbox inputs in your applications by reopening this class. For example, - if you wanted to add a css class to all checkboxes in your application: - - - ```javascript - Ember.Checkbox.reopen({ - classNames: ['my-app-checkbox'] - }); - ``` - - - @method input - @for Ember.Handlebars.helpers - @param {Hash} options -*/ -Ember.Handlebars.registerHelper('input', function(options) { - Ember.assert('You can only pass attributes to the `input` helper, not arguments', arguments.length < 2); - - var hash = options.hash, - types = options.hashTypes, - inputType = hash.type, - onEvent = hash.on; - - delete hash.type; - delete hash.on; - - if (inputType === 'checkbox') { - Ember.assert("{{input type='checkbox'}} does not support setting `value=someBooleanValue`; you must use `checked=someBooleanValue` instead.", options.hashTypes.value !== 'ID'); - return Ember.Handlebars.helpers.view.call(this, Ember.Checkbox, options); - } else { - if (inputType) { hash.type = inputType; } - hash.onEvent = onEvent || 'enter'; - return Ember.Handlebars.helpers.view.call(this, Ember.TextField, options); - } -}); - -/** - `{{textarea}}` inserts a new instance of ` - ``` - - Bound: - - In the following example, the `writtenWords` property on `App.ApplicationController` - will be updated live as the user types 'Lots of text that IS bound' into - the text area of their browser's window. - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound" - }); - ``` - - ```handlebars - {{textarea value=writtenWords}} - ``` - - Would result in the following HTML: - - ```html - - ``` - - If you wanted a one way binding between the text area and a div tag - somewhere else on your screen, you could use `Ember.computed.oneWay`: - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound", - outputWrittenWords: Ember.computed.oneWay("writtenWords") - }); - ``` - - ```handlebars - {{textarea value=writtenWords}} - -
    - {{outputWrittenWords}} -
    - ``` - - Would result in the following HTML: - - ```html - - - <-- the following div will be updated in real time as you type --> - -
    - Lots of text that IS bound -
    - ``` - - Finally, this example really shows the power and ease of Ember when two - properties are bound to eachother via `Ember.computed.alias`. Type into - either text area box and they'll both stay in sync. Note that - `Ember.computed.alias` costs more in terms of performance, so only use it when - your really binding in both directions: - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - writtenWords: "Lots of text that IS bound", - twoWayWrittenWords: Ember.computed.alias("writtenWords") - }); - ``` - - ```handlebars - {{textarea value=writtenWords}} - {{textarea value=twoWayWrittenWords}} - ``` - - ```html - - - <-- both updated in real time --> - - - ``` - - ## Extension - - Internally, `{{textarea}}` creates an instance of `Ember.TextArea`, passing - arguments from the helper to `Ember.TextArea`'s `create` method. You can - extend the capabilities of text areas in your application by reopening this - class. For example, if you are deploying to browsers where the `required` - attribute is used, you can globally add support for the `required` attribute - on all `{{textarea}}`s' in your app by reopening `Ember.TextArea` or - `Ember.TextSupport` and adding it to the `attributeBindings` concatenated - property: - - ```javascript - Ember.TextArea.reopen({ - attributeBindings: ['required'] - }); - ``` - - Keep in mind when writing `Ember.TextArea` subclasses that `Ember.TextArea` - itself extends `Ember.Component`, meaning that it does NOT inherit - the `controller` of the parent view. - - See more about [Ember components](api/classes/Ember.Component.html) - - @method textarea - @for Ember.Handlebars.helpers - @param {Hash} options -*/ -Ember.Handlebars.registerHelper('textarea', function(options) { - Ember.assert('You can only pass attributes to the `textarea` helper, not arguments', arguments.length < 2); - - var hash = options.hash, - types = options.hashTypes; - - return Ember.Handlebars.helpers.view.call(this, Ember.TextArea, options); -}); - -})(); - - - -(function() { -Ember.ComponentLookup = Ember.Object.extend({ - lookupFactory: function(name, container) { - - container = container || this.container; - - var fullName = 'component:' + name, - templateFullName = 'template:components/' + name, - templateRegistered = container && container.has(templateFullName); - - if (templateRegistered) { - container.injection(fullName, 'layout', templateFullName); - } - - var Component = container.lookupFactory(fullName); - - // Only treat as a component if either the component - // or a template has been registered. - if (templateRegistered || Component) { - if (!Component) { - container.register(fullName, Ember.Component); - Component = container.lookupFactory(fullName); - } - return Component; - } - } -}); - -})(); - - - -(function() { -/*globals Handlebars */ -/** -@module ember -@submodule ember-handlebars -*/ - -/** - Find templates stored in the head tag as script tags and make them available - to `Ember.CoreView` in the global `Ember.TEMPLATES` object. This will be run - as as jQuery DOM-ready callback. - - Script tags with `text/x-handlebars` will be compiled - with Ember's Handlebars and are suitable for use as a view's template. - Those with type `text/x-raw-handlebars` will be compiled with regular - Handlebars and are suitable for use in views' computed properties. - - @private - @method bootstrap - @for Ember.Handlebars - @static - @param ctx -*/ -Ember.Handlebars.bootstrap = function(ctx) { - var selectors = 'script[type="text/x-handlebars"], script[type="text/x-raw-handlebars"]'; - - Ember.$(selectors, ctx) - .each(function() { - // Get a reference to the script tag - var script = Ember.$(this); - - var compile = (script.attr('type') === 'text/x-raw-handlebars') ? - Ember.$.proxy(Handlebars.compile, Handlebars) : - Ember.$.proxy(Ember.Handlebars.compile, Ember.Handlebars), - // Get the name of the script, used by Ember.View's templateName property. - // First look for data-template-name attribute, then fall back to its - // id if no name is found. - templateName = script.attr('data-template-name') || script.attr('id') || 'application', - template = compile(script.html()); - - // Check if template of same name already exists - if (Ember.TEMPLATES[templateName] !== undefined) { - throw new Ember.Error('Template named "' + templateName + '" already exists.'); - } - - // For templates which have a name, we save them and then remove them from the DOM - Ember.TEMPLATES[templateName] = template; - - // Remove script tag from DOM - script.remove(); - }); -}; - -function bootstrap() { - Ember.Handlebars.bootstrap( Ember.$(document) ); -} - -function registerComponentLookup(container) { - container.register('component-lookup:main', Ember.ComponentLookup); -} - -/* - We tie this to application.load to ensure that we've at least - attempted to bootstrap at the point that the application is loaded. - - We also tie this to document ready since we're guaranteed that all - the inline templates are present at this point. - - There's no harm to running this twice, since we remove the templates - from the DOM after processing. -*/ - -Ember.onLoad('Ember.Application', function(Application) { - Application.initializer({ - name: 'domTemplates', - initialize: bootstrap - }); - - Application.initializer({ - name: 'registerComponentLookup', - after: 'domTemplates', - initialize: registerComponentLookup - }); -}); - -})(); - - - -(function() { -/** -Ember Handlebars - -@module ember -@submodule ember-handlebars -@requires ember-views -*/ - -Ember.runLoadHooks('Ember.Handlebars', Ember.Handlebars); - -})(); - -(function() { -define("route-recognizer", - ["exports"], - function(__exports__) { - "use strict"; - var specials = [ - '/', '.', '*', '+', '?', '|', - '(', ')', '[', ']', '{', '}', '\\' - ]; - - var escapeRegex = new RegExp('(\\' + specials.join('|\\') + ')', 'g'); - - function isArray(test) { - return Object.prototype.toString.call(test) === "[object Array]"; - } - - // A Segment represents a segment in the original route description. - // Each Segment type provides an `eachChar` and `regex` method. - // - // The `eachChar` method invokes the callback with one or more character - // specifications. A character specification consumes one or more input - // characters. - // - // The `regex` method returns a regex fragment for the segment. If the - // segment is a dynamic of star segment, the regex fragment also includes - // a capture. - // - // A character specification contains: - // - // * `validChars`: a String with a list of all valid characters, or - // * `invalidChars`: a String with a list of all invalid characters - // * `repeat`: true if the character specification can repeat - - function StaticSegment(string) { this.string = string; } - StaticSegment.prototype = { - eachChar: function(callback) { - var string = this.string, ch; - - for (var i=0, l=string.length; i " + n.nextStates.map(function(s) { return s.debug() }).join(" or ") + " )"; - }).join(", ") - } - END IF **/ - - // This is a somewhat naive strategy, but should work in a lot of cases - // A better strategy would properly resolve /posts/:id/new and /posts/edit/:id. - // - // This strategy generally prefers more static and less dynamic matching. - // Specifically, it - // - // * prefers fewer stars to more, then - // * prefers using stars for less of the match to more, then - // * prefers fewer dynamic segments to more, then - // * prefers more static segments to more - function sortSolutions(states) { - return states.sort(function(a, b) { - if (a.types.stars !== b.types.stars) { return a.types.stars - b.types.stars; } - - if (a.types.stars) { - if (a.types.statics !== b.types.statics) { return b.types.statics - a.types.statics; } - if (a.types.dynamics !== b.types.dynamics) { return b.types.dynamics - a.types.dynamics; } - } - - if (a.types.dynamics !== b.types.dynamics) { return a.types.dynamics - b.types.dynamics; } - if (a.types.statics !== b.types.statics) { return b.types.statics - a.types.statics; } - - return 0; - }); - } - - function recognizeChar(states, ch) { - var nextStates = []; - - for (var i=0, l=states.length; i 2 && key.slice(keyLength -2) === '[]') { - isArray = true; - key = key.slice(0, keyLength - 2); - if(!queryParams[key]) { - queryParams[key] = []; - } - } - value = pair[1] ? decodeURIComponent(pair[1]) : ''; - } - if (isArray) { - queryParams[key].push(value); - } else { - queryParams[key] = value; - } - - } - return queryParams; - }, - - recognize: function(path) { - var states = [ this.rootState ], - pathLen, i, l, queryStart, queryParams = {}, - isSlashDropped = false; - - path = decodeURI(path); - - queryStart = path.indexOf('?'); - if (queryStart !== -1) { - var queryString = path.substr(queryStart + 1, path.length); - path = path.substr(0, queryStart); - queryParams = this.parseQueryString(queryString); - } - - // DEBUG GROUP path - - if (path.charAt(0) !== "/") { path = "/" + path; } - - pathLen = path.length; - if (pathLen > 1 && path.charAt(pathLen - 1) === "/") { - path = path.substr(0, pathLen - 1); - isSlashDropped = true; - } - - for (i=0, l=path.length; i= 0 && proceed; --i) { - var route = routes[i]; - recognizer.add(routes, { as: route.handler }); - proceed = route.path === '/' || route.path === '' || route.handler.slice(-6) === '.index'; - } - }); - }, - - hasRoute: function(route) { - return this.recognizer.hasRoute(route); - }, - - // NOTE: this doesn't really belong here, but here - // it shall remain until our ES6 transpiler can - // handle cyclical deps. - transitionByIntent: function(intent, isIntermediate) { - - var wasTransitioning = !!this.activeTransition; - var oldState = wasTransitioning ? this.activeTransition.state : this.state; - var newTransition; - var router = this; - - try { - var newState = intent.applyToState(oldState, this.recognizer, this.getHandler, isIntermediate); - - if (handlerInfosEqual(newState.handlerInfos, oldState.handlerInfos)) { - - // This is a no-op transition. See if query params changed. - var queryParamChangelist = getChangelist(oldState.queryParams, newState.queryParams); - if (queryParamChangelist) { - - // This is a little hacky but we need some way of storing - // changed query params given that no activeTransition - // is guaranteed to have occurred. - this._changedQueryParams = queryParamChangelist.changed; - trigger(this, newState.handlerInfos, true, ['queryParamsDidChange', queryParamChangelist.changed, queryParamChangelist.all, queryParamChangelist.removed]); - this._changedQueryParams = null; - - if (!wasTransitioning && this.activeTransition) { - // One of the handlers in queryParamsDidChange - // caused a transition. Just return that transition. - return this.activeTransition; - } else { - // Running queryParamsDidChange didn't change anything. - // Just update query params and be on our way. - oldState.queryParams = finalizeQueryParamChange(this, newState.handlerInfos, newState.queryParams); - - // We have to return a noop transition that will - // perform a URL update at the end. This gives - // the user the ability to set the url update - // method (default is replaceState). - newTransition = new Transition(this); - newTransition.urlMethod = 'replace'; - newTransition.promise = newTransition.promise.then(function(result) { - updateURL(newTransition, oldState, true); - if (router.didTransition) { - router.didTransition(router.currentHandlerInfos); - } - return result; - }, null, promiseLabel("Transition complete")); - return newTransition; - } - } - - // No-op. No need to create a new transition. - return new Transition(this); - } - - if (isIntermediate) { - setupContexts(this, newState); - return; - } - - // Create a new transition to the destination route. - newTransition = new Transition(this, intent, newState); - - // Abort and usurp any previously active transition. - if (this.activeTransition) { - this.activeTransition.abort(); - } - this.activeTransition = newTransition; - - // Transition promises by default resolve with resolved state. - // For our purposes, swap out the promise to resolve - // after the transition has been finalized. - newTransition.promise = newTransition.promise.then(function(result) { - return router.async(function() { - return finalizeTransition(newTransition, result.state); - }, "Finalize transition"); - }, null, promiseLabel("Settle transition promise when transition is finalized")); - - if (!wasTransitioning) { - trigger(this, this.state.handlerInfos, true, ['willTransition', newTransition]); - } - - return newTransition; - } catch(e) { - return new Transition(this, intent, null, e); - } - }, - - /** - Clears the current and target route handlers and triggers exit - on each of them starting at the leaf and traversing up through - its ancestors. - */ - reset: function() { - if (this.state) { - forEach(this.state.handlerInfos, function(handlerInfo) { - var handler = handlerInfo.handler; - if (handler.exit) { - handler.exit(); - } - }); - } - - this.state = new TransitionState(); - this.currentHandlerInfos = null; - }, - - activeTransition: null, - - /** - var handler = handlerInfo.handler; - The entry point for handling a change to the URL (usually - via the back and forward button). - - Returns an Array of handlers and the parameters associated - with those parameters. - - @param {String} url a URL to process - - @return {Array} an Array of `[handler, parameter]` tuples - */ - handleURL: function(url) { - // Perform a URL-based transition, but don't change - // the URL afterward, since it already happened. - var args = slice.call(arguments); - if (url.charAt(0) !== '/') { args[0] = '/' + url; } - - return doTransition(this, args).method('replaceQuery'); - }, - - /** - Hook point for updating the URL. - - @param {String} url a URL to update to - */ - updateURL: function() { - throw new Error("updateURL is not implemented"); - }, - - /** - Hook point for replacing the current URL, i.e. with replaceState - - By default this behaves the same as `updateURL` - - @param {String} url a URL to update to - */ - replaceURL: function(url) { - this.updateURL(url); - }, - - /** - Transition into the specified named route. - - If necessary, trigger the exit callback on any handlers - that are no longer represented by the target route. - - @param {String} name the name of the route - */ - transitionTo: function(name) { - return doTransition(this, arguments); - }, - - intermediateTransitionTo: function(name) { - doTransition(this, arguments, true); - }, - - refresh: function(pivotHandler) { - - - var state = this.activeTransition ? this.activeTransition.state : this.state; - var handlerInfos = state.handlerInfos; - var params = {}; - for (var i = 0, len = handlerInfos.length; i < len; ++i) { - var handlerInfo = handlerInfos[i]; - params[handlerInfo.name] = handlerInfo.params || {}; - } - - log(this, "Starting a refresh transition"); - var intent = new NamedTransitionIntent({ - name: handlerInfos[handlerInfos.length - 1].name, - pivotHandler: pivotHandler || handlerInfos[0].handler, - contexts: [], // TODO collect contexts...? - queryParams: this._changedQueryParams || state.queryParams || {} - }); - - return this.transitionByIntent(intent, false); - }, - - /** - Identical to `transitionTo` except that the current URL will be replaced - if possible. - - This method is intended primarily for use with `replaceState`. - - @param {String} name the name of the route - */ - replaceWith: function(name) { - return doTransition(this, arguments).method('replace'); - }, - - /** - Take a named route and context objects and generate a - URL. - - @param {String} name the name of the route to generate - a URL for - @param {...Object} objects a list of objects to serialize - - @return {String} a URL - */ - generate: function(handlerName) { - - var partitionedArgs = extractQueryParams(slice.call(arguments, 1)), - suppliedParams = partitionedArgs[0], - queryParams = partitionedArgs[1]; - - // Construct a TransitionIntent with the provided params - // and apply it to the present state of the router. - var intent = new NamedTransitionIntent({ name: handlerName, contexts: suppliedParams }); - var state = intent.applyToState(this.state, this.recognizer, this.getHandler); - var params = {}; - - for (var i = 0, len = state.handlerInfos.length; i < len; ++i) { - var handlerInfo = state.handlerInfos[i]; - var handlerParams = handlerInfo.params || - serialize(handlerInfo.handler, handlerInfo.context, handlerInfo.names); - merge(params, handlerParams); - } - params.queryParams = queryParams; - - return this.recognizer.generate(handlerName, params); - }, - - isActive: function(handlerName) { - - var partitionedArgs = extractQueryParams(slice.call(arguments, 1)), - contexts = partitionedArgs[0], - queryParams = partitionedArgs[1], - activeQueryParams = this.state.queryParams; - - var targetHandlerInfos = this.state.handlerInfos, - found = false, names, object, handlerInfo, handlerObj, i, len; - - if (!targetHandlerInfos.length) { return false; } - - var targetHandler = targetHandlerInfos[targetHandlerInfos.length - 1].name; - var recogHandlers = this.recognizer.handlersFor(targetHandler); - - var index = 0; - for (len = recogHandlers.length; index < len; ++index) { - handlerInfo = targetHandlerInfos[index]; - if (handlerInfo.name === handlerName) { break; } - } - - if (index === recogHandlers.length) { - // The provided route name isn't even in the route hierarchy. - return false; - } - - var state = new TransitionState(); - state.handlerInfos = targetHandlerInfos.slice(0, index + 1); - recogHandlers = recogHandlers.slice(0, index + 1); - - var intent = new NamedTransitionIntent({ - name: targetHandler, - contexts: contexts - }); - - var newState = intent.applyToHandlers(state, recogHandlers, this.getHandler, targetHandler, true, true); - - // Get a hash of QPs that will still be active on new route - var activeQPsOnNewHandler = {}; - merge(activeQPsOnNewHandler, queryParams); - for (var key in activeQueryParams) { - if (activeQueryParams.hasOwnProperty(key) && - activeQPsOnNewHandler.hasOwnProperty(key)) { - activeQPsOnNewHandler[key] = activeQueryParams[key]; - } - } - - return handlerInfosEqual(newState.handlerInfos, state.handlerInfos) && - !getChangelist(activeQPsOnNewHandler, queryParams); - }, - - trigger: function(name) { - var args = slice.call(arguments); - trigger(this, this.currentHandlerInfos, false, args); - }, - - /** - @private - - Pluggable hook for possibly running route hooks - in a try-catch escaping manner. - - @param {Function} callback the callback that will - be asynchronously called - - @return {Promise} a promise that fulfills with the - value returned from the callback - */ - async: function(callback, label) { - return new Promise(function(resolve) { - resolve(callback()); - }, label); - }, - - /** - Hook point for logging transition status updates. - - @param {String} message The message to log. - */ - log: null - }; - - /** - @private - - Takes an Array of `HandlerInfo`s, figures out which ones are - exiting, entering, or changing contexts, and calls the - proper handler hooks. - - For example, consider the following tree of handlers. Each handler is - followed by the URL segment it handles. - - ``` - |~index ("/") - | |~posts ("/posts") - | | |-showPost ("/:id") - | | |-newPost ("/new") - | | |-editPost ("/edit") - | |~about ("/about/:id") - ``` - - Consider the following transitions: - - 1. A URL transition to `/posts/1`. - 1. Triggers the `*model` callbacks on the - `index`, `posts`, and `showPost` handlers - 2. Triggers the `enter` callback on the same - 3. Triggers the `setup` callback on the same - 2. A direct transition to `newPost` - 1. Triggers the `exit` callback on `showPost` - 2. Triggers the `enter` callback on `newPost` - 3. Triggers the `setup` callback on `newPost` - 3. A direct transition to `about` with a specified - context object - 1. Triggers the `exit` callback on `newPost` - and `posts` - 2. Triggers the `serialize` callback on `about` - 3. Triggers the `enter` callback on `about` - 4. Triggers the `setup` callback on `about` - - @param {Router} transition - @param {TransitionState} newState - */ - function setupContexts(router, newState, transition) { - var partition = partitionHandlers(router.state, newState); - - forEach(partition.exited, function(handlerInfo) { - var handler = handlerInfo.handler; - delete handler.context; - if (handler.exit) { handler.exit(); } - }); - - var oldState = router.oldState = router.state; - router.state = newState; - var currentHandlerInfos = router.currentHandlerInfos = partition.unchanged.slice(); - - try { - forEach(partition.updatedContext, function(handlerInfo) { - return handlerEnteredOrUpdated(currentHandlerInfos, handlerInfo, false, transition); - }); - - forEach(partition.entered, function(handlerInfo) { - return handlerEnteredOrUpdated(currentHandlerInfos, handlerInfo, true, transition); - }); - } catch(e) { - router.state = oldState; - router.currentHandlerInfos = oldState.handlerInfos; - throw e; - } - - router.state.queryParams = finalizeQueryParamChange(router, currentHandlerInfos, newState.queryParams); - } - - - /** - @private - - Helper method used by setupContexts. Handles errors or redirects - that may happen in enter/setup. - */ - function handlerEnteredOrUpdated(currentHandlerInfos, handlerInfo, enter, transition) { - - var handler = handlerInfo.handler, - context = handlerInfo.context; - - if (enter && handler.enter) { handler.enter(transition); } - if (transition && transition.isAborted) { - throw new TransitionAborted(); - } - - handler.context = context; - if (handler.contextDidChange) { handler.contextDidChange(); } - - if (handler.setup) { handler.setup(context, transition); } - if (transition && transition.isAborted) { - throw new TransitionAborted(); - } - - currentHandlerInfos.push(handlerInfo); - - return true; - } - - - /** - @private - - This function is called when transitioning from one URL to - another to determine which handlers are no longer active, - which handlers are newly active, and which handlers remain - active but have their context changed. - - Take a list of old handlers and new handlers and partition - them into four buckets: - - * unchanged: the handler was active in both the old and - new URL, and its context remains the same - * updated context: the handler was active in both the - old and new URL, but its context changed. The handler's - `setup` method, if any, will be called with the new - context. - * exited: the handler was active in the old URL, but is - no longer active. - * entered: the handler was not active in the old URL, but - is now active. - - The PartitionedHandlers structure has four fields: - - * `updatedContext`: a list of `HandlerInfo` objects that - represent handlers that remain active but have a changed - context - * `entered`: a list of `HandlerInfo` objects that represent - handlers that are newly active - * `exited`: a list of `HandlerInfo` objects that are no - longer active. - * `unchanged`: a list of `HanderInfo` objects that remain active. - - @param {Array[HandlerInfo]} oldHandlers a list of the handler - information for the previous URL (or `[]` if this is the - first handled transition) - @param {Array[HandlerInfo]} newHandlers a list of the handler - information for the new URL - - @return {Partition} - */ - function partitionHandlers(oldState, newState) { - var oldHandlers = oldState.handlerInfos; - var newHandlers = newState.handlerInfos; - - var handlers = { - updatedContext: [], - exited: [], - entered: [], - unchanged: [] - }; - - var handlerChanged, contextChanged, queryParamsChanged, i, l; - - for (i=0, l=newHandlers.length; i= 0; --i) { - var handlerInfo = handlerInfos[i]; - merge(params, handlerInfo.params); - if (handlerInfo.handler.inaccessibleByURL) { - urlMethod = null; - } - } - - if (urlMethod) { - params.queryParams = state.queryParams; - var url = router.recognizer.generate(handlerName, params); - - if (urlMethod === 'replaceQuery') { - if (url !== inputUrl) { - router.replaceURL(url); - } - } else if (urlMethod === 'replace') { - router.replaceURL(url); - } else { - router.updateURL(url); - } - } - } - - /** - @private - - Updates the URL (if necessary) and calls `setupContexts` - to update the router's array of `currentHandlerInfos`. - */ - function finalizeTransition(transition, newState) { - - try { - log(transition.router, transition.sequence, "Resolved all models on destination route; finalizing transition."); - - var router = transition.router, - handlerInfos = newState.handlerInfos, - seq = transition.sequence; - - // Run all the necessary enter/setup/exit hooks - setupContexts(router, newState, transition); - - // Check if a redirect occurred in enter/setup - if (transition.isAborted) { - // TODO: cleaner way? distinguish b/w targetHandlerInfos? - router.state.handlerInfos = router.currentHandlerInfos; - return reject(logAbort(transition)); - } - - updateURL(transition, newState, transition.intent.url); - - transition.isActive = false; - router.activeTransition = null; - - trigger(router, router.currentHandlerInfos, true, ['didTransition']); - - if (router.didTransition) { - router.didTransition(router.currentHandlerInfos); - } - - log(router, transition.sequence, "TRANSITION COMPLETE."); - - // Resolve with the final handler. - return handlerInfos[handlerInfos.length - 1].handler; - } catch(e) { - if (!(e instanceof TransitionAborted)) { - //var erroneousHandler = handlerInfos.pop(); - var infos = transition.state.handlerInfos; - transition.trigger(true, 'error', e, transition, infos[infos.length-1].handler); - transition.abort(); - } - - throw e; - } - } - - /** - @private - - Begins and returns a Transition based on the provided - arguments. Accepts arguments in the form of both URL - transitions and named transitions. - - @param {Router} router - @param {Array[Object]} args arguments passed to transitionTo, - replaceWith, or handleURL - */ - function doTransition(router, args, isIntermediate) { - // Normalize blank transitions to root URL transitions. - var name = args[0] || '/'; - - var lastArg = args[args.length-1]; - var queryParams = {}; - if (lastArg && lastArg.hasOwnProperty('queryParams')) { - queryParams = pop.call(args).queryParams; - } - - var intent; - if (args.length === 0) { - - log(router, "Updating query params"); - - // A query param update is really just a transition - // into the route you're already on. - var handlerInfos = router.state.handlerInfos; - intent = new NamedTransitionIntent({ - name: handlerInfos[handlerInfos.length - 1].name, - contexts: [], - queryParams: queryParams - }); - - } else if (name.charAt(0) === '/') { - - log(router, "Attempting URL transition to " + name); - intent = new URLTransitionIntent({ url: name }); - - } else { - - log(router, "Attempting transition to " + name); - intent = new NamedTransitionIntent({ - name: args[0], - contexts: slice.call(args, 1), - queryParams: queryParams - }); - } - - return router.transitionByIntent(intent, isIntermediate); - } - - function handlerInfosEqual(handlerInfos, otherHandlerInfos) { - if (handlerInfos.length !== otherHandlerInfos.length) { - return false; - } - - for (var i = 0, len = handlerInfos.length; i < len; ++i) { - if (handlerInfos[i] !== otherHandlerInfos[i]) { - return false; - } - } - return true; - } - - function finalizeQueryParamChange(router, resolvedHandlers, newQueryParams) { - // We fire a finalizeQueryParamChange event which - // gives the new route hierarchy a chance to tell - // us which query params it's consuming and what - // their final values are. If a query param is - // no longer consumed in the final route hierarchy, - // its serialized segment will be removed - // from the URL. - var finalQueryParamsArray = []; - trigger(router, resolvedHandlers, true, ['finalizeQueryParamChange', newQueryParams, finalQueryParamsArray]); - - var finalQueryParams = {}; - for (var i = 0, len = finalQueryParamsArray.length; i < len; ++i) { - var qp = finalQueryParamsArray[i]; - finalQueryParams[qp.key] = qp.value; - } - return finalQueryParams; - } - - __exports__.Router = Router; - }); -define("router/transition-intent", - ["./utils","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var merge = __dependency1__.merge; - - function TransitionIntent(props) { - if (props) { - merge(this, props); - } - this.data = this.data || {}; - } - - TransitionIntent.prototype.applyToState = function(oldState) { - // Default TransitionIntent is a no-op. - return oldState; - }; - - __exports__.TransitionIntent = TransitionIntent; - }); -define("router/transition-intent/named-transition-intent", - ["../transition-intent","../transition-state","../handler-info","../utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - var TransitionIntent = __dependency1__.TransitionIntent; - var TransitionState = __dependency2__.TransitionState; - var UnresolvedHandlerInfoByParam = __dependency3__.UnresolvedHandlerInfoByParam; - var UnresolvedHandlerInfoByObject = __dependency3__.UnresolvedHandlerInfoByObject; - var isParam = __dependency4__.isParam; - var forEach = __dependency4__.forEach; - var extractQueryParams = __dependency4__.extractQueryParams; - var oCreate = __dependency4__.oCreate; - var merge = __dependency4__.merge; - - function NamedTransitionIntent(props) { - TransitionIntent.call(this, props); - } - - NamedTransitionIntent.prototype = oCreate(TransitionIntent.prototype); - NamedTransitionIntent.prototype.applyToState = function(oldState, recognizer, getHandler, isIntermediate) { - - var partitionedArgs = extractQueryParams([this.name].concat(this.contexts)), - pureArgs = partitionedArgs[0], - queryParams = partitionedArgs[1], - handlers = recognizer.handlersFor(pureArgs[0]); - - var targetRouteName = handlers[handlers.length-1].handler; - - return this.applyToHandlers(oldState, handlers, getHandler, targetRouteName, isIntermediate); - }; - - NamedTransitionIntent.prototype.applyToHandlers = function(oldState, handlers, getHandler, targetRouteName, isIntermediate, checkingIfActive) { - - var i; - var newState = new TransitionState(); - var objects = this.contexts.slice(0); - - var invalidateIndex = handlers.length; - var nonDynamicIndexes = []; - - // Pivot handlers are provided for refresh transitions - if (this.pivotHandler) { - for (i = 0; i < handlers.length; ++i) { - if (getHandler(handlers[i].handler) === this.pivotHandler) { - invalidateIndex = i; - break; - } - } - } - - var pivotHandlerFound = !this.pivotHandler; - - for (i = handlers.length - 1; i >= 0; --i) { - var result = handlers[i]; - var name = result.handler; - var handler = getHandler(name); - - var oldHandlerInfo = oldState.handlerInfos[i]; - var newHandlerInfo = null; - - if (result.names.length > 0) { - if (i >= invalidateIndex) { - newHandlerInfo = this.createParamHandlerInfo(name, handler, result.names, objects, oldHandlerInfo); - } else { - newHandlerInfo = this.getHandlerInfoForDynamicSegment(name, handler, result.names, objects, oldHandlerInfo, targetRouteName); - } - } else { - // This route has no dynamic segment. - // Therefore treat as a param-based handlerInfo - // with empty params. This will cause the `model` - // hook to be called with empty params, which is desirable. - newHandlerInfo = this.createParamHandlerInfo(name, handler, result.names, objects, oldHandlerInfo); - nonDynamicIndexes.unshift(i); - } - - if (checkingIfActive) { - // If we're performing an isActive check, we want to - // serialize URL params with the provided context, but - // ignore mismatches between old and new context. - newHandlerInfo = newHandlerInfo.becomeResolved(null, newHandlerInfo.context); - var oldContext = oldHandlerInfo && oldHandlerInfo.context; - if (result.names.length > 0 && newHandlerInfo.context === oldContext) { - // If contexts match in isActive test, assume params also match. - // This allows for flexibility in not requiring that every last - // handler provide a `serialize` method - newHandlerInfo.params = oldHandlerInfo && oldHandlerInfo.params; - } - newHandlerInfo.context = oldContext; - } - - var handlerToUse = oldHandlerInfo; - if (i >= invalidateIndex || newHandlerInfo.shouldSupercede(oldHandlerInfo)) { - invalidateIndex = Math.min(i, invalidateIndex); - handlerToUse = newHandlerInfo; - } - - if (isIntermediate && !checkingIfActive) { - handlerToUse = handlerToUse.becomeResolved(null, handlerToUse.context); - } - - newState.handlerInfos.unshift(handlerToUse); - } - - if (objects.length > 0) { - throw new Error("More context objects were passed than there are dynamic segments for the route: " + targetRouteName); - } - - if (!isIntermediate) { - this.invalidateNonDynamicHandlers(newState.handlerInfos, nonDynamicIndexes, invalidateIndex); - } - - merge(newState.queryParams, oldState.queryParams); - merge(newState.queryParams, this.queryParams || {}); - - return newState; - }; - - NamedTransitionIntent.prototype.invalidateNonDynamicHandlers = function(handlerInfos, indexes, invalidateIndex) { - forEach(indexes, function(i) { - if (i >= invalidateIndex) { - var handlerInfo = handlerInfos[i]; - handlerInfos[i] = new UnresolvedHandlerInfoByParam({ - name: handlerInfo.name, - handler: handlerInfo.handler, - params: {} - }); - } - }); - }; - - NamedTransitionIntent.prototype.getHandlerInfoForDynamicSegment = function(name, handler, names, objects, oldHandlerInfo, targetRouteName) { - - var numNames = names.length; - var objectToUse; - if (objects.length > 0) { - - // Use the objects provided for this transition. - objectToUse = objects[objects.length - 1]; - if (isParam(objectToUse)) { - return this.createParamHandlerInfo(name, handler, names, objects, oldHandlerInfo); - } else { - objects.pop(); - } - } else if (oldHandlerInfo && oldHandlerInfo.name === name) { - // Reuse the matching oldHandlerInfo - return oldHandlerInfo; - } else { - // Ideally we should throw this error to provide maximal - // information to the user that not enough context objects - // were provided, but this proves too cumbersome in Ember - // in cases where inner template helpers are evaluated - // before parent helpers un-render, in which cases this - // error somewhat prematurely fires. - //throw new Error("Not enough context objects were provided to complete a transition to " + targetRouteName + ". Specifically, the " + name + " route needs an object that can be serialized into its dynamic URL segments [" + names.join(', ') + "]"); - return oldHandlerInfo; - } - - return new UnresolvedHandlerInfoByObject({ - name: name, - handler: handler, - context: objectToUse, - names: names - }); - }; - - NamedTransitionIntent.prototype.createParamHandlerInfo = function(name, handler, names, objects, oldHandlerInfo) { - var params = {}; - - // Soak up all the provided string/numbers - var numNames = names.length; - while (numNames--) { - - // Only use old params if the names match with the new handler - var oldParams = (oldHandlerInfo && name === oldHandlerInfo.name && oldHandlerInfo.params) || {}; - - var peek = objects[objects.length - 1]; - var paramName = names[numNames]; - if (isParam(peek)) { - params[paramName] = "" + objects.pop(); - } else { - // If we're here, this means only some of the params - // were string/number params, so try and use a param - // value from a previous handler. - if (oldParams.hasOwnProperty(paramName)) { - params[paramName] = oldParams[paramName]; - } else { - throw new Error("You didn't provide enough string/numeric parameters to satisfy all of the dynamic segments for route " + name); - } - } - } - - return new UnresolvedHandlerInfoByParam({ - name: name, - handler: handler, - params: params - }); - }; - - __exports__.NamedTransitionIntent = NamedTransitionIntent; - }); -define("router/transition-intent/url-transition-intent", - ["../transition-intent","../transition-state","../handler-info","../utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __exports__) { - "use strict"; - var TransitionIntent = __dependency1__.TransitionIntent; - var TransitionState = __dependency2__.TransitionState; - var UnresolvedHandlerInfoByParam = __dependency3__.UnresolvedHandlerInfoByParam; - var oCreate = __dependency4__.oCreate; - var merge = __dependency4__.merge; - - function URLTransitionIntent(props) { - TransitionIntent.call(this, props); - } - - URLTransitionIntent.prototype = oCreate(TransitionIntent.prototype); - URLTransitionIntent.prototype.applyToState = function(oldState, recognizer, getHandler) { - var newState = new TransitionState(); - - var results = recognizer.recognize(this.url), - queryParams = {}, - i, len; - - if (!results) { - throw new UnrecognizedURLError(this.url); - } - - var statesDiffer = false; - - for (i = 0, len = results.length; i < len; ++i) { - var result = results[i]; - var name = result.handler; - var handler = getHandler(name); - - if (handler.inaccessibleByURL) { - throw new UnrecognizedURLError(this.url); - } - - var newHandlerInfo = new UnresolvedHandlerInfoByParam({ - name: name, - handler: handler, - params: result.params - }); - - var oldHandlerInfo = oldState.handlerInfos[i]; - if (statesDiffer || newHandlerInfo.shouldSupercede(oldHandlerInfo)) { - statesDiffer = true; - newState.handlerInfos[i] = newHandlerInfo; - } else { - newState.handlerInfos[i] = oldHandlerInfo; - } - } - - merge(newState.queryParams, results.queryParams); - - return newState; - }; - - /** - Promise reject reasons passed to promise rejection - handlers for failed transitions. - */ - function UnrecognizedURLError(message) { - this.message = (message || "UnrecognizedURLError"); - this.name = "UnrecognizedURLError"; - } - - __exports__.URLTransitionIntent = URLTransitionIntent; - }); -define("router/transition-state", - ["./handler-info","./utils","rsvp","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var ResolvedHandlerInfo = __dependency1__.ResolvedHandlerInfo; - var forEach = __dependency2__.forEach; - var promiseLabel = __dependency2__.promiseLabel; - var resolve = __dependency3__.resolve; - var reject = __dependency3__.reject; - - function TransitionState(other) { - this.handlerInfos = []; - this.queryParams = {}; - this.params = {}; - } - - TransitionState.prototype = { - handlerInfos: null, - queryParams: null, - params: null, - - promiseLabel: function(label) { - var targetName = ''; - forEach(this.handlerInfos, function(handlerInfo) { - if (targetName !== '') { - targetName += '.'; - } - targetName += handlerInfo.name; - }); - return promiseLabel("'" + targetName + "': " + label); - }, - - resolve: function(async, shouldContinue, payload) { - var self = this; - // First, calculate params for this state. This is useful - // information to provide to the various route hooks. - var params = this.params; - forEach(this.handlerInfos, function(handlerInfo) { - params[handlerInfo.name] = handlerInfo.params || {}; - }); - - payload = payload || {}; - payload.resolveIndex = 0; - - var currentState = this; - var wasAborted = false; - - // The prelude RSVP.resolve() asyncs us into the promise land. - return resolve(null, this.promiseLabel("Start transition")) - .then(resolveOneHandlerInfo, null, this.promiseLabel('Resolve handler'))['catch'](handleError, this.promiseLabel('Handle error')); - - function innerShouldContinue() { - return resolve(shouldContinue(), promiseLabel("Check if should continue"))['catch'](function(reason) { - // We distinguish between errors that occurred - // during resolution (e.g. beforeModel/model/afterModel), - // and aborts due to a rejecting promise from shouldContinue(). - wasAborted = true; - return reject(reason); - }, promiseLabel("Handle abort")); - } - - function handleError(error) { - // This is the only possible - // reject value of TransitionState#resolve - var handlerInfos = currentState.handlerInfos; - var errorHandlerIndex = payload.resolveIndex >= handlerInfos.length ? - handlerInfos.length - 1 : payload.resolveIndex; - return reject({ - error: error, - handlerWithError: currentState.handlerInfos[errorHandlerIndex].handler, - wasAborted: wasAborted, - state: currentState - }); - } - - function proceed(resolvedHandlerInfo) { - // Swap the previously unresolved handlerInfo with - // the resolved handlerInfo - currentState.handlerInfos[payload.resolveIndex++] = resolvedHandlerInfo; - - // Call the redirect hook. The reason we call it here - // vs. afterModel is so that redirects into child - // routes don't re-run the model hooks for this - // already-resolved route. - var handler = resolvedHandlerInfo.handler; - if (handler && handler.redirect) { - handler.redirect(resolvedHandlerInfo.context, payload); - } - - // Proceed after ensuring that the redirect hook - // didn't abort this transition by transitioning elsewhere. - return innerShouldContinue().then(resolveOneHandlerInfo, null, promiseLabel('Resolve handler')); - } - - function resolveOneHandlerInfo() { - if (payload.resolveIndex === currentState.handlerInfos.length) { - // This is is the only possible - // fulfill value of TransitionState#resolve - return { - error: null, - state: currentState - }; - } - - var handlerInfo = currentState.handlerInfos[payload.resolveIndex]; - - return handlerInfo.resolve(async, innerShouldContinue, payload) - .then(proceed, null, promiseLabel('Proceed')); - } - } - }; - - __exports__.TransitionState = TransitionState; - }); -define("router/transition", - ["rsvp","./handler-info","./utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __exports__) { - "use strict"; - var reject = __dependency1__.reject; - var resolve = __dependency1__.resolve; - var ResolvedHandlerInfo = __dependency2__.ResolvedHandlerInfo; - var trigger = __dependency3__.trigger; - var slice = __dependency3__.slice; - var log = __dependency3__.log; - var promiseLabel = __dependency3__.promiseLabel; - - /** - @private - - A Transition is a thennable (a promise-like object) that represents - an attempt to transition to another route. It can be aborted, either - explicitly via `abort` or by attempting another transition while a - previous one is still underway. An aborted transition can also - be `retry()`d later. - */ - function Transition(router, intent, state, error) { - var transition = this; - this.state = state || router.state; - this.intent = intent; - this.router = router; - this.data = this.intent && this.intent.data || {}; - this.resolvedModels = {}; - this.queryParams = {}; - - if (error) { - this.promise = reject(error); - return; - } - - if (state) { - this.params = state.params; - this.queryParams = state.queryParams; - - var len = state.handlerInfos.length; - if (len) { - this.targetName = state.handlerInfos[state.handlerInfos.length-1].name; - } - - for (var i = 0; i < len; ++i) { - var handlerInfo = state.handlerInfos[i]; - if (!(handlerInfo instanceof ResolvedHandlerInfo)) { - break; - } - this.pivotHandler = handlerInfo.handler; - } - - this.sequence = Transition.currentSequence++; - this.promise = state.resolve(router.async, checkForAbort, this)['catch'](function(result) { - if (result.wasAborted) { - return reject(logAbort(transition)); - } else { - transition.trigger('error', result.error, transition, result.handlerWithError); - transition.abort(); - return reject(result.error); - } - }, promiseLabel('Handle Abort')); - } else { - this.promise = resolve(this.state); - this.params = {}; - } - - function checkForAbort() { - if (transition.isAborted) { - return reject(undefined, promiseLabel("Transition aborted - reject")); - } - } - } - - Transition.currentSequence = 0; - - Transition.prototype = { - targetName: null, - urlMethod: 'update', - intent: null, - params: null, - pivotHandler: null, - resolveIndex: 0, - handlerInfos: null, - resolvedModels: null, - isActive: true, - state: null, - - /** - @public - - The Transition's internal promise. Calling `.then` on this property - is that same as calling `.then` on the Transition object itself, but - this property is exposed for when you want to pass around a - Transition's promise, but not the Transition object itself, since - Transition object can be externally `abort`ed, while the promise - cannot. - */ - promise: null, - - /** - @public - - Custom state can be stored on a Transition's `data` object. - This can be useful for decorating a Transition within an earlier - hook and shared with a later hook. Properties set on `data` will - be copied to new transitions generated by calling `retry` on this - transition. - */ - data: null, - - /** - @public - - A standard promise hook that resolves if the transition - succeeds and rejects if it fails/redirects/aborts. - - Forwards to the internal `promise` property which you can - use in situations where you want to pass around a thennable, - but not the Transition itself. - - @param {Function} success - @param {Function} failure - */ - then: function(success, failure) { - return this.promise.then(success, failure); - }, - - /** - @public - - Aborts the Transition. Note you can also implicitly abort a transition - by initiating another transition while a previous one is underway. - */ - abort: function() { - if (this.isAborted) { return this; } - log(this.router, this.sequence, this.targetName + ": transition was aborted"); - this.isAborted = true; - this.isActive = false; - this.router.activeTransition = null; - return this; - }, - - /** - @public - - Retries a previously-aborted transition (making sure to abort the - transition if it's still active). Returns a new transition that - represents the new attempt to transition. - */ - retry: function() { - // TODO: add tests for merged state retry()s - this.abort(); - return this.router.transitionByIntent(this.intent, false); - }, - - /** - @public - - Sets the URL-changing method to be employed at the end of a - successful transition. By default, a new Transition will just - use `updateURL`, but passing 'replace' to this method will - cause the URL to update using 'replaceWith' instead. Omitting - a parameter will disable the URL change, allowing for transitions - that don't update the URL at completion (this is also used for - handleURL, since the URL has already changed before the - transition took place). - - @param {String} method the type of URL-changing method to use - at the end of a transition. Accepted values are 'replace', - falsy values, or any other non-falsy value (which is - interpreted as an updateURL transition). - - @return {Transition} this transition - */ - method: function(method) { - this.urlMethod = method; - return this; - }, - - /** - @public - - Fires an event on the current list of resolved/resolving - handlers within this transition. Useful for firing events - on route hierarchies that haven't fully been entered yet. - - Note: This method is also aliased as `send` - - @param {Boolean} [ignoreFailure=false] a boolean specifying whether unhandled events throw an error - @param {String} name the name of the event to fire - */ - trigger: function (ignoreFailure) { - var args = slice.call(arguments); - if (typeof ignoreFailure === 'boolean') { - args.shift(); - } else { - // Throw errors on unhandled trigger events by default - ignoreFailure = false; - } - trigger(this.router, this.state.handlerInfos.slice(0, this.resolveIndex + 1), ignoreFailure, args); - }, - - /** - @public - - Transitions are aborted and their promises rejected - when redirects occur; this method returns a promise - that will follow any redirects that occur and fulfill - with the value fulfilled by any redirecting transitions - that occur. - - @return {Promise} a promise that fulfills with the same - value that the final redirecting transition fulfills with - */ - followRedirects: function() { - var router = this.router; - return this.promise['catch'](function(reason) { - if (router.activeTransition) { - return router.activeTransition.followRedirects(); - } - return reject(reason); - }); - }, - - toString: function() { - return "Transition (sequence " + this.sequence + ")"; - }, - - /** - @private - */ - log: function(message) { - log(this.router, this.sequence, message); - } - }; - - // Alias 'trigger' as 'send' - Transition.prototype.send = Transition.prototype.trigger; - - /** - @private - - Logs and returns a TransitionAborted error. - */ - function logAbort(transition) { - log(transition.router, transition.sequence, "detected abort."); - return new TransitionAborted(); - } - - function TransitionAborted(message) { - this.message = (message || "TransitionAborted"); - this.name = "TransitionAborted"; - } - - __exports__.Transition = Transition; - __exports__.logAbort = logAbort; - __exports__.TransitionAborted = TransitionAborted; - }); -define("router/utils", - ["exports"], - function(__exports__) { - "use strict"; - var slice = Array.prototype.slice; - - function isArray(test) { - return Object.prototype.toString.call(test) === "[object Array]"; - } - - function merge(hash, other) { - for (var prop in other) { - if (other.hasOwnProperty(prop)) { hash[prop] = other[prop]; } - } - } - - var oCreate = Object.create || function(proto) { - function F() {} - F.prototype = proto; - return new F(); - }; - - /** - @private - - Extracts query params from the end of an array - **/ - function extractQueryParams(array) { - var len = (array && array.length), head, queryParams; - - if(len && len > 0 && array[len - 1] && array[len - 1].hasOwnProperty('queryParams')) { - queryParams = array[len - 1].queryParams; - head = slice.call(array, 0, len - 1); - return [head, queryParams]; - } else { - return [array, null]; - } - } - - /** - @private - - Coerces query param properties and array elements into strings. - **/ - function coerceQueryParamsToString(queryParams) { - for (var key in queryParams) { - if (typeof queryParams[key] === 'number') { - queryParams[key] = '' + queryParams[key]; - } else if (isArray(queryParams[key])) { - for (var i = 0, l = queryParams[key].length; i < l; i++) { - queryParams[key][i] = '' + queryParams[key][i]; - } - } - } - } - /** - @private - */ - function log(router, sequence, msg) { - if (!router.log) { return; } - - if (arguments.length === 3) { - router.log("Transition #" + sequence + ": " + msg); - } else { - msg = sequence; - router.log(msg); - } - } - - function bind(fn, context) { - var boundArgs = arguments; - return function(value) { - var args = slice.call(boundArgs, 2); - args.push(value); - return fn.apply(context, args); - }; - } - - function isParam(object) { - return (typeof object === "string" || object instanceof String || typeof object === "number" || object instanceof Number); - } - - - function forEach(array, callback) { - for (var i=0, l=array.length; i=0; i--) { - var handlerInfo = handlerInfos[i], - handler = handlerInfo.handler; - - if (handler.events && handler.events[name]) { - if (handler.events[name].apply(handler, args) === true) { - eventWasHandled = true; - } else { - return; - } - } - } - - if (!eventWasHandled && !ignoreFailure) { - throw new Error("Nothing handled the event '" + name + "'."); - } - } - - function getChangelist(oldObject, newObject) { - var key; - var results = { - all: {}, - changed: {}, - removed: {} - }; - - merge(results.all, newObject); - - var didChange = false; - coerceQueryParamsToString(oldObject); - coerceQueryParamsToString(newObject); - - // Calculate removals - for (key in oldObject) { - if (oldObject.hasOwnProperty(key)) { - if (!newObject.hasOwnProperty(key)) { - didChange = true; - results.removed[key] = oldObject[key]; - } - } - } - - // Calculate changes - for (key in newObject) { - if (newObject.hasOwnProperty(key)) { - if (isArray(oldObject[key]) && isArray(newObject[key])) { - if (oldObject[key].length !== newObject[key].length) { - results.changed[key] = newObject[key]; - didChange = true; - } else { - for (var i = 0, l = oldObject[key].length; i < l; i++) { - if (oldObject[key][i] !== newObject[key][i]) { - results.changed[key] = newObject[key]; - didChange = true; - } - } - } - } - else { - if (oldObject[key] !== newObject[key]) { - results.changed[key] = newObject[key]; - didChange = true; - } - } - } - } - - return didChange && results; - } - - function promiseLabel(label) { - return 'Router: ' + label; - } - - __exports__.trigger = trigger; - __exports__.log = log; - __exports__.oCreate = oCreate; - __exports__.merge = merge; - __exports__.extractQueryParams = extractQueryParams; - __exports__.bind = bind; - __exports__.isParam = isParam; - __exports__.forEach = forEach; - __exports__.slice = slice; - __exports__.serialize = serialize; - __exports__.getChangelist = getChangelist; - __exports__.coerceQueryParamsToString = coerceQueryParamsToString; - __exports__.promiseLabel = promiseLabel; - }); -define("router", - ["./router/router","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var Router = __dependency1__.Router; - - __exports__.Router = Router; - }); -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ - -function DSL(name) { - this.parent = name; - this.matches = []; -} - -DSL.prototype = { - resource: function(name, options, callback) { - Ember.assert("'basic' cannot be used as a resource name.", name !== 'basic'); - - if (arguments.length === 2 && typeof options === 'function') { - callback = options; - options = {}; - } - - if (arguments.length === 1) { - options = {}; - } - - if (typeof options.path !== 'string') { - options.path = "/" + name; - } - - if (callback) { - var dsl = new DSL(name); - route(dsl, 'loading'); - route(dsl, 'error', { path: "/_unused_dummy_error_path_route_" + name + "/:error" }); - callback.call(dsl); - this.push(options.path, name, dsl.generate()); - } else { - this.push(options.path, name, null); - } - - - }, - - push: function(url, name, callback) { - var parts = name.split('.'); - if (url === "" || url === "/" || parts[parts.length-1] === "index") { this.explicitIndex = true; } - - this.matches.push([url, name, callback]); - }, - - route: function(name, options) { - Ember.assert("'basic' cannot be used as a route name.", name !== 'basic'); - - route(this, name, options); - }, - - generate: function() { - var dslMatches = this.matches; - - if (!this.explicitIndex) { - this.route("index", { path: "/" }); - } - - return function(match) { - for (var i=0, l=dslMatches.length; i " + fullName, { fullName: fullName }); - } - - return instance; -}; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ - -var routerJsModule = requireModule("router"); -var Router = routerJsModule.Router; -var Transition = routerJsModule.Transition; -var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt; -var defineProperty = Ember.defineProperty; -var slice = Array.prototype.slice; -var forEach = Ember.EnumerableUtils.forEach; - -var DefaultView = Ember._MetamorphView; -/** - The `Ember.Router` class manages the application state and URLs. Refer to - the [routing guide](http://emberjs.com/guides/routing/) for documentation. - - @class Router - @namespace Ember - @extends Ember.Object -*/ -Ember.Router = Ember.Object.extend(Ember.Evented, { - /** - The `location` property determines the type of URL's that your - application will use. - - The following location types are currently available: - - * `hash` - * `history` - * `none` - - @property location - @default 'hash' - @see {Ember.Location} - */ - location: 'hash', - - /** - Represents the URL of the root of the application, often '/'. This prefix is - assumed on all routes defined on this router. - - @property rootURL - @default '/' - */ - rootURL: '/', - - init: function() { - this.router = this.constructor.router || this.constructor.map(Ember.K); - this._activeViews = {}; - this._setupLocation(); - - if (get(this, 'namespace.LOG_TRANSITIONS_INTERNAL')) { - this.router.log = Ember.Logger.debug; - } - }, - - /** - Represents the current URL. - - @method url - @returns {String} The current URL. - */ - url: Ember.computed(function() { - return get(this, 'location').getURL(); - }), - - /** - Initializes the current router instance and sets up the change handling - event listeners used by the instances `location` implementation. - - A property named `initialURL` will be used to determine the initial URL. - If no value is found `/` will be used. - - @method startRouting - @private - */ - startRouting: function() { - this.router = this.router || this.constructor.map(Ember.K); - - var router = this.router, - location = get(this, 'location'), - container = this.container, - self = this, - initialURL = get(this, 'initialURL'); - - - // Allow the Location class to cancel the router setup while it refreshes - // the page - if (get(location, 'cancelRouterSetup')) { - return; - } - - - this._setupRouter(router, location); - - container.register('view:default', DefaultView); - container.register('view:toplevel', Ember.View.extend()); - - location.onUpdateURL(function(url) { - self.handleURL(url); - }); - - if (typeof initialURL === "undefined") { - initialURL = location.getURL(); - } - - this.handleURL(initialURL); - }, - - /** - Handles updating the paths and notifying any listeners of the URL - change. - - Triggers the router level `didTransition` hook. - - @method didTransition - @private - */ - didTransition: function(infos) { - updatePaths(this); - - this._cancelLoadingEvent(); - - this.notifyPropertyChange('url'); - - // Put this in the runloop so url will be accurate. Seems - // less surprising than didTransition being out of sync. - Ember.run.once(this, this.trigger, 'didTransition'); - - if (get(this, 'namespace').LOG_TRANSITIONS) { - Ember.Logger.log("Transitioned into '" + Ember.Router._routePath(infos) + "'"); - } - }, - - handleURL: function(url) { - return this._doTransition('handleURL', [url]); - }, - - transitionTo: function() { - return this._doTransition('transitionTo', arguments); - }, - - intermediateTransitionTo: function() { - this.router.intermediateTransitionTo.apply(this.router, arguments); - - updatePaths(this); - - var infos = this.router.currentHandlerInfos; - if (get(this, 'namespace').LOG_TRANSITIONS) { - Ember.Logger.log("Intermediate-transitioned into '" + Ember.Router._routePath(infos) + "'"); - } - }, - - replaceWith: function() { - return this._doTransition('replaceWith', arguments); - }, - - generate: function() { - var url = this.router.generate.apply(this.router, arguments); - return this.location.formatURL(url); - }, - - /** - Determines if the supplied route is currently active. - - @method isActive - @param routeName - @returns {Boolean} - @private - */ - isActive: function(routeName) { - var router = this.router; - return router.isActive.apply(router, arguments); - }, - - send: function(name, context) { - this.router.trigger.apply(this.router, arguments); - }, - - /** - Does this router instance have the given route. - - @method hasRoute - @returns {Boolean} - @private - */ - hasRoute: function(route) { - return this.router.hasRoute(route); - }, - - /** - Resets the state of the router by clearing the current route - handlers and deactivating them. - - @private - @method reset - */ - reset: function() { - this.router.reset(); - }, - - _lookupActiveView: function(templateName) { - var active = this._activeViews[templateName]; - return active && active[0]; - }, - - _connectActiveView: function(templateName, view) { - var existing = this._activeViews[templateName]; - - if (existing) { - existing[0].off('willDestroyElement', this, existing[1]); - } - - var disconnect = function() { - delete this._activeViews[templateName]; - }; - - this._activeViews[templateName] = [view, disconnect]; - view.one('willDestroyElement', this, disconnect); - }, - - _setupLocation: function() { - var location = get(this, 'location'), - rootURL = get(this, 'rootURL'); - - if (rootURL && !this.container.has('-location-setting:root-url')) { - this.container.register('-location-setting:root-url', rootURL, { instantiate: false }); - } - - if ('string' === typeof location && this.container) { - var resolvedLocation = this.container.lookup('location:' + location); - - if ('undefined' !== typeof resolvedLocation) { - location = set(this, 'location', resolvedLocation); - } else { - // Allow for deprecated registration of custom location API's - var options = {implementation: location}; - - location = set(this, 'location', Ember.Location.create(options)); - } - } - - if (rootURL && typeof rootURL === 'string') { - location.rootURL = rootURL; - } - - // ensure that initState is called AFTER the rootURL is set on - // the location instance - if (typeof location.initState === 'function') { location.initState(); } - }, - - _getHandlerFunction: function() { - var seen = {}, container = this.container, - DefaultRoute = container.lookupFactory('route:basic'), - self = this; - - return function(name) { - var routeName = 'route:' + name, - handler = container.lookup(routeName); - - if (seen[name]) { return handler; } - - seen[name] = true; - - if (!handler) { - container.register(routeName, DefaultRoute.extend()); - handler = container.lookup(routeName); - - if (get(self, 'namespace.LOG_ACTIVE_GENERATION')) { - Ember.Logger.info("generated -> " + routeName, { fullName: routeName }); - } - } - - handler.routeName = name; - return handler; - }; - }, - - _setupRouter: function(router, location) { - var lastURL, emberRouter = this; - - router.getHandler = this._getHandlerFunction(); - - var doUpdateURL = function() { - location.setURL(lastURL); - }; - - router.updateURL = function(path) { - lastURL = path; - Ember.run.once(doUpdateURL); - }; - - if (location.replaceURL) { - var doReplaceURL = function() { - location.replaceURL(lastURL); - }; - - router.replaceURL = function(path) { - lastURL = path; - Ember.run.once(doReplaceURL); - }; - } - - router.didTransition = function(infos) { - emberRouter.didTransition(infos); - }; - }, - - _doTransition: function(method, args) { - // Normalize blank route to root URL. - args = slice.call(args); - args[0] = args[0] || '/'; - - var name = args[0], self = this, - isQueryParamsOnly = false, queryParams; - - - if (!isQueryParamsOnly && name.charAt(0) !== '/') { - Ember.assert("The route " + name + " was not found", this.router.hasRoute(name)); - } - - if (queryParams) { - // router.js expects queryParams to be passed in in - // their final serialized form, so we need to translate. - - if (!name) { - // Need to determine destination route name. - var handlerInfos = this.router.activeTransition ? - this.router.activeTransition.state.handlerInfos : - this.router.state.handlerInfos; - name = handlerInfos[handlerInfos.length - 1].name; - args.unshift(name); - } - - var qpMappings = this._queryParamNamesFor(name); - - - Ember.Router._translateQueryParams(queryParams, qpMappings.translations, name); - var value; - for (var key in queryParams) { - var descopedParam = Ember.Router._descopeQueryParam(key); - if (key in qpMappings.queryParams) { - value = queryParams[key]; - delete queryParams[key]; - queryParams[qpMappings.queryParams[key]] = value; - } else if (descopedParam in qpMappings.validQueryParams) { - value = queryParams[key]; - delete queryParams[key]; - queryParams[descopedParam] = value; - } - } - } - - var transitionPromise = this.router[method].apply(this.router, args); - - transitionPromise.then(null, function(error) { - if (error && error.name === "UnrecognizedURLError") { - Ember.assert("The URL '" + error.message + "' did not match any routes in your application"); - } - }, 'Ember: Check for Router unrecognized URL error'); - - // We want to return the configurable promise object - // so that callers of this function can use `.method()` on it, - // which obviously doesn't exist for normal RSVP promises. - return transitionPromise; - }, - - _scheduleLoadingEvent: function(transition, originRoute) { - this._cancelLoadingEvent(); - this._loadingStateTimer = Ember.run.scheduleOnce('routerTransitions', this, '_fireLoadingEvent', transition, originRoute); - }, - - _fireLoadingEvent: function(transition, originRoute) { - if (!this.router.activeTransition) { - // Don't fire an event if we've since moved on from - // the transition that put us in a loading state. - return; - } - - transition.trigger(true, 'loading', transition, originRoute); - }, - - _cancelLoadingEvent: function () { - if (this._loadingStateTimer) { - Ember.run.cancel(this._loadingStateTimer); - } - this._loadingStateTimer = null; - }, - - _queryParamNamesFor: function(routeName) { - - // TODO: add caching - - var handlerInfos = this.router.recognizer.handlersFor(routeName); - var result = { queryParams: Ember.create(null), translations: Ember.create(null), validQueryParams: Ember.create(null) }; - var routerjs = this.router; - forEach(handlerInfos, function(recogHandler) { - var route = routerjs.getHandler(recogHandler.handler); - getQueryParamsForRoute(route, result); - }); - - descopeQueryParams(result.queryParams); - - for (var k in result.queryParams) { - result.validQueryParams[result.queryParams[k]] = true; - } - return result; - }, - - _queryParamNamesForSingle: function(routeName) { - - // TODO: add caching - - var result = { queryParams: Ember.create(null), translations: Ember.create(null) }; - var route = this.router.getHandler(routeName); - - getQueryParamsForRoute(route, result); - - // Descope non duplicate params. - if (routeName !== 'application') { - var allParams = this._queryParamNamesFor(routeName); - for (var k in result.queryParams) { - result.queryParams[k] = allParams.queryParams[k]; - } - } - - return result; - }, - - /** - @private - - Utility function for fetching all the current query params - values from a controller. - */ - _queryParamOverrides: function(results, queryParams, callback) { - for (var name in queryParams) { - var parts = name.split(':'); - - var controller = controllerOrProtoFor(parts[0], this.container); - Ember.assert(fmt("Could not lookup controller '%@' while setting up query params", [controller]), controller); - - // Now assign the final URL-serialized key-value pair, - // e.g. "foo[propName]": "value" - results[queryParams[name]] = get(controller, parts[1]); - - if (callback) { - // Give callback a chance to override. - callback(name, queryParams[name], name); - } - } - } -}); - -/** - @private - */ -function getQueryParamsForRoute(route, result) { - var controllerName = route.controllerName || route.routeName, - controller = controllerOrProtoFor(controllerName, route.container), - queryParams = get(controller, 'queryParams'); - - if (queryParams) { - forEach(queryParams, function(propName) { - - var parts = propName.split(':'); - - var urlKeyName; - if (parts.length > 1) { - urlKeyName = parts[1]; - } else { - // TODO: use _queryParamScope here? - if (controllerName !== 'application') { - urlKeyName = controllerName + '[' + propName + ']'; - } else { - urlKeyName = propName; - } - } - - var controllerFullname = controllerName + ':' + propName; - - result.queryParams[controllerFullname] = urlKeyName; - result.translations[parts[0]] = controllerFullname; - }); - } -} - -function controllerOrProtoFor(controllerName, container) { - var fullName = 'controller:' + controllerName; - if (container.cache.has(fullName)) { - return container.lookup(fullName); - } else { - // Controller hasn't been instantiated yet; just return its proto. - var controllerClass = container.lookupFactory(fullName); - if (controllerClass && typeof controllerClass.proto === 'function') { - return controllerClass.proto(); - } else { - return {}; - } - } -} - -function descopeQueryParams(params) { - var paramCounts = {}, - descopedParam, - k; - - // Loop through params and count the occurance of descoped param - for (k in params) { - descopedParam = Ember.Router._descopeQueryParam(params[k]); - - if (!paramCounts[descopedParam]) { - paramCounts[descopedParam] = 1; - } else { - paramCounts[descopedParam] = paramCounts[descopedParam] + 1; - } - } - - // Loop through again descoping params if the descoped key only occurs once - for (k in params) { - descopedParam = Ember.Router._descopeQueryParam(params[k]); - - if (paramCounts[descopedParam] === 1) { - params[k] = descopedParam; - } - } -} - -/** - Helper function for iterating root-ward, starting - from (but not including) the provided `originRoute`. - - Returns true if the last callback fired requested - to bubble upward. - - @private - */ -function forEachRouteAbove(originRoute, transition, callback) { - var handlerInfos = transition.state.handlerInfos, - originRouteFound = false; - - for (var i = handlerInfos.length - 1; i >= 0; --i) { - var handlerInfo = handlerInfos[i], - route = handlerInfo.handler; - - if (!originRouteFound) { - if (originRoute === route) { - originRouteFound = true; - } - continue; - } - - if (callback(route, handlerInfos[i + 1].handler) !== true) { - return false; - } - } - return true; -} - -// These get invoked when an action bubbles above ApplicationRoute -// and are not meant to be overridable. -var defaultActionHandlers = { - - willResolveModel: function(transition, originRoute) { - originRoute.router._scheduleLoadingEvent(transition, originRoute); - }, - - error: function(error, transition, originRoute) { - // Attempt to find an appropriate error substate to enter. - var router = originRoute.router; - - var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { - var childErrorRouteName = findChildRouteName(route, childRoute, 'error'); - if (childErrorRouteName) { - router.intermediateTransitionTo(childErrorRouteName, error); - return; - } - return true; - }); - - if (tryTopLevel) { - // Check for top-level error state to enter. - if (routeHasBeenDefined(originRoute.router, 'application_error')) { - router.intermediateTransitionTo('application_error', error); - return; - } - } else { - // Don't fire an assertion if we found an error substate. - return; - } - - Ember.Logger.error('Error while loading route: ' + (error && error.stack)); - }, - - loading: function(transition, originRoute) { - // Attempt to find an appropriate loading substate to enter. - var router = originRoute.router; - - var tryTopLevel = forEachRouteAbove(originRoute, transition, function(route, childRoute) { - var childLoadingRouteName = findChildRouteName(route, childRoute, 'loading'); - - if (childLoadingRouteName) { - router.intermediateTransitionTo(childLoadingRouteName); - return; - } - - // Don't bubble above pivot route. - if (transition.pivotHandler !== route) { - return true; - } - }); - - if (tryTopLevel) { - // Check for top-level loading state to enter. - if (routeHasBeenDefined(originRoute.router, 'application_loading')) { - router.intermediateTransitionTo('application_loading'); - return; - } - } - } -}; - -function findChildRouteName(parentRoute, originatingChildRoute, name) { - var router = parentRoute.router, - childName, - targetChildRouteName = originatingChildRoute.routeName.split('.').pop(), - namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.'; - - - // Second, try general loading state, e.g. 'loading' - childName = namespace + name; - if (routeHasBeenDefined(router, childName)) { - return childName; - } -} - -function routeHasBeenDefined(router, name) { - var container = router.container; - return router.hasRoute(name) && - (container.has('template:' + name) || container.has('route:' + name)); -} - -function triggerEvent(handlerInfos, ignoreFailure, args) { - var name = args.shift(); - - if (!handlerInfos) { - if (ignoreFailure) { return; } - throw new Ember.Error("Can't trigger action '" + name + "' because your app hasn't finished transitioning into its first route. To trigger an action on destination routes during a transition, you can call `.send()` on the `Transition` object passed to the `model/beforeModel/afterModel` hooks."); - } - - var eventWasHandled = false; - - for (var i = handlerInfos.length - 1; i >= 0; i--) { - var handlerInfo = handlerInfos[i], - handler = handlerInfo.handler; - - if (handler._actions && handler._actions[name]) { - if (handler._actions[name].apply(handler, args) === true) { - eventWasHandled = true; - } else { - return; - } - } - } - - if (defaultActionHandlers[name]) { - defaultActionHandlers[name].apply(null, args); - return; - } - - if (!eventWasHandled && !ignoreFailure) { - throw new Ember.Error("Nothing handled the action '" + name + "'. If you did handle the action, this error can be caused by returning true from an action handler in a controller, causing the action to bubble."); - } -} - -function updatePaths(router) { - var appController = router.container.lookup('controller:application'); - - if (!appController) { - // appController might not exist when top-level loading/error - // substates have been entered since ApplicationRoute hasn't - // actually been entered at that point. - return; - } - - var infos = router.router.currentHandlerInfos, - path = Ember.Router._routePath(infos); - - if (!('currentPath' in appController)) { - defineProperty(appController, 'currentPath'); - } - - set(appController, 'currentPath', path); - - if (!('currentRouteName' in appController)) { - defineProperty(appController, 'currentRouteName'); - } - - set(appController, 'currentRouteName', infos[infos.length - 1].name); -} - -Ember.Router.reopenClass({ - router: null, - map: function(callback) { - var router = this.router; - if (!router) { - router = new Router(); - router.callbacks = []; - router.triggerEvent = triggerEvent; - this.reopenClass({ router: router }); - } - - var dsl = Ember.RouterDSL.map(function() { - this.resource('application', { path: "/" }, function() { - for (var i=0; i < router.callbacks.length; i++) { - router.callbacks[i].call(this); - } - callback.call(this); - }); - }); - - router.callbacks.push(callback); - router.map(dsl.generate()); - return router; - }, - - _routePath: function(handlerInfos) { - var path = []; - - // We have to handle coalescing resource names that - // are prefixed with their parent's names, e.g. - // ['foo', 'foo.bar.baz'] => 'foo.bar.baz', not 'foo.foo.bar.baz' - - function intersectionMatches(a1, a2) { - for (var i = 0, len = a1.length; i < len; ++i) { - if (a1[i] !== a2[i]) { - return false; - } - } - return true; - } - - for (var i=1, l=handlerInfos.length; i 0 ? !Ember.isNone(arguments[0]) : true); - - var namePassed = !!name; - - if (typeof name === 'object' && !options) { - options = name; - name = this.routeName; - } - - options = options || {}; - - var templateName; - - if (name) { - name = name.replace(/\//g, '.'); - templateName = name; - } else { - name = this.routeName; - templateName = this.templateName || name; - } - - var viewName = options.view || this.viewName || name; - - var container = this.container, - view = container.lookup('view:' + viewName), - template = view ? view.get('template') : null; - - if (!template) { - template = container.lookup('template:' + templateName); - } - - if (!view && !template) { - Ember.assert("Could not find \"" + name + "\" template or view.", !namePassed); - if (get(this.router, 'namespace.LOG_VIEW_LOOKUPS')) { - Ember.Logger.info("Could not find \"" + name + "\" template or view. Nothing will be rendered", { fullName: 'template:' + name }); - } - return; - } - - options = normalizeOptions(this, name, template, options); - view = setupView(view, container, options); - - if (options.outlet === 'main') { this.lastRenderedTemplate = name; } - - appendView(this, view, options); - }, - - /** - Disconnects a view that has been rendered into an outlet. - - You may pass any or all of the following options to `disconnectOutlet`: - - * `outlet`: the name of the outlet to clear (default: 'main') - * `parentView`: the name of the view containing the outlet to clear - (default: the view rendered by the parent route) - - Example: - - ```js - App.ApplicationRoute = App.Route.extend({ - actions: { - showModal: function(evt) { - this.render(evt.modalName, { - outlet: 'modal', - into: 'application' - }); - }, - hideModal: function(evt) { - this.disconnectOutlet({ - outlet: 'modal', - parentView: 'application' - }); - } - } - }); - ``` - - Alternatively, you can pass the `outlet` name directly as a string. - - Example: - - ```js - hideModal: function(evt) { - this.disconnectOutlet('modal'); - } - ``` - - @method disconnectOutlet - @param {Object|String} options the options hash or outlet name - */ - disconnectOutlet: function(options) { - if (!options || typeof options === "string") { - var outletName = options; - options = {}; - options.outlet = outletName; - } - options.parentView = options.parentView ? options.parentView.replace(/\//g, '.') : parentTemplate(this); - options.outlet = options.outlet || 'main'; - - var parentView = this.router._lookupActiveView(options.parentView); - if (parentView) { parentView.disconnectOutlet(options.outlet); } - }, - - willDestroy: function() { - this.teardownViews(); - }, - - /** - @private - - @method teardownViews - */ - teardownViews: function() { - // Tear down the top level view - if (this.teardownTopLevelView) { this.teardownTopLevelView(); } - - // Tear down any outlets rendered with 'into' - var teardownOutletViews = this.teardownOutletViews || []; - a_forEach(teardownOutletViews, function(teardownOutletView) { - teardownOutletView(); - }); - - delete this.teardownTopLevelView; - delete this.teardownOutletViews; - delete this.lastRenderedTemplate; - } -}); - - - -function parentRoute(route) { - var handlerInfos = route.router.router.state.handlerInfos; - - if (!handlerInfos) { return; } - - var parent, current; - - for (var i=0, l=handlerInfos.length; i maximumContexts) - currentWhen = routeArgs[0]; - - var isActive = router.isActive.apply(router, [currentWhen].concat(contexts)); - - if (isActive) { return get(this, 'activeClass'); } - }).property('resolvedParams', 'routeArgs'), - - /** - Accessed as a classname binding to apply the `LinkView`'s `loadingClass` - CSS `class` to the element when the link is loading. - - A `LinkView` is considered loading when it has at least one - parameter whose value is currently null or undefined. During - this time, clicking the link will perform no transition and - emit a warning that the link is still in a loading state. - - @property loading - **/ - loading: Ember.computed(function computeLinkViewLoading() { - if (!get(this, 'routeArgs')) { return get(this, 'loadingClass'); } - }).property('routeArgs'), - - /** - Returns the application's main router from the container. - - @private - @property router - **/ - router: Ember.computed(function() { - return get(this, 'controller').container.lookup('router:main'); - }), - - /** - Event handler that invokes the link, activating the associated route. - - @private - @method _invoke - @param {Event} event - */ - _invoke: function(event) { - if (!isSimpleClick(event)) { return true; } - - if (this.preventDefault !== false) { event.preventDefault(); } - if (this.bubbles === false) { event.stopPropagation(); } - - if (get(this, '_isDisabled')) { return false; } - - if (get(this, 'loading')) { - Ember.Logger.warn("This link-to is in an inactive loading state because at least one of its parameters presently has a null/undefined value, or the provided route name is invalid."); - return false; - } - - var router = get(this, 'router'), - routeArgs = get(this, 'routeArgs'); - - var transition; - if (get(this, 'replace')) { - transition = router.replaceWith.apply(router, routeArgs); - } else { - transition = router.transitionTo.apply(router, routeArgs); - } - - // Schedule eager URL update, but after we've given the transition - // a chance to synchronously redirect. - - // We need to always generate the URL instead of using the href because - // the href will include any rootURL set, but the router expects a URL - // without it! Note that we don't use the first level router because it - // calls location.formatURL(), which also would add the rootURL! - var url = router.router.generate.apply(router.router, get(this, 'routeArgs')); - Ember.run.scheduleOnce('routerTransitions', this, this._eagerUpdateUrl, transition, url); - - }, - - /** - @private - */ - _eagerUpdateUrl: function(transition, href) { - if (!transition.isActive || !transition.urlMethod) { - // transition was aborted, already ran to completion, - // or it has a null url-updated method. - return; - } - - if (href.indexOf('#') === 0) { - href = href.slice(1); - } - - // Re-use the routerjs hooks set up by the Ember router. - var routerjs = get(this, 'router.router'); - if (transition.urlMethod === 'update') { - routerjs.updateURL(href); - } else if (transition.urlMethod === 'replace') { - routerjs.replaceURL(href); - } - - // Prevent later update url refire. - transition.method(null); - }, - - /** - Computed property that returns an array of the - resolved parameters passed to the `link-to` helper, - e.g.: - - ```hbs - {{link-to a b '123' c}} - ``` - - will generate a `resolvedParams` of: - - ```js - [aObject, bObject, '123', cObject] - ``` - - @private - @property - @return {Array} - */ - resolvedParams: Ember.computed(function() { - var parameters = this.parameters, - options = parameters.options, - types = options.types, - data = options.data; - - if (parameters.params.length === 0) { - var appController = this.container.lookup('controller:application'); - return [get(appController, 'currentRouteName')]; - } else { - return resolveParams(parameters.context, parameters.params, { types: types, data: data }); - } - }).property('router.url'), - - /** - Computed property that returns the current route name and - any dynamic segments. - - @private - @property - @return {Array} An array with the route name and any dynamic segments - */ - routeArgs: Ember.computed(function computeLinkViewRouteArgs() { - var resolvedParams = get(this, 'resolvedParams').slice(0), - router = get(this, 'router'), - namedRoute = resolvedParams[0]; - - if (!namedRoute) { return; } - - Ember.assert(fmt("The attempt to link-to route '%@' failed. " + - "The router did not find '%@' in its possible routes: '%@'", - [namedRoute, namedRoute, Ember.keys(router.router.recognizer.names).join("', '")]), - router.hasRoute(namedRoute)); - - //normalize route name - var handlers = router.router.recognizer.handlersFor(namedRoute); - var normalizedPath = handlers[handlers.length - 1].handler; - if (namedRoute !== normalizedPath) { - this.set('currentWhen', namedRoute); - namedRoute = handlers[handlers.length - 1].handler; - resolvedParams[0] = namedRoute; - } - - for (var i = 1, len = resolvedParams.length; i < len; ++i) { - var param = resolvedParams[i]; - if (param === null || typeof param === 'undefined') { - // If contexts aren't present, consider the linkView unloaded. - return; - } - } - - - return resolvedParams; - }).property('resolvedParams', 'queryParams'), - - queryParamsObject: null, - queryParams: Ember.computed(function computeLinkViewQueryParams() { - - var queryParamsObject = get(this, 'queryParamsObject'), - suppliedParams = {}; - - if (queryParamsObject) { - Ember.merge(suppliedParams, queryParamsObject.values); - } - - var resolvedParams = get(this, 'resolvedParams'), - router = get(this, 'router'), - routeName = resolvedParams[0], - paramsForRoute = router._queryParamNamesFor(routeName), - queryParams = paramsForRoute.queryParams, - translations = paramsForRoute.translations, - paramsForRecognizer = {}; - - // Normalize supplied params into their long-form name - // e.g. 'foo' -> 'controllername:foo' - translateQueryParams(suppliedParams, translations, routeName); - - var helperParameters = this.parameters; - router._queryParamOverrides(paramsForRecognizer, queryParams, function(name, resultsName) { - if (!(name in suppliedParams)) { return; } - - var parts = name.split(':'); - - var type = queryParamsObject.types[parts[1]]; - - var value; - if (type === 'ID') { - var normalizedPath = Ember.Handlebars.normalizePath(helperParameters.context, suppliedParams[name], helperParameters.options.data); - value = Ember.Handlebars.get(normalizedPath.root, normalizedPath.path, helperParameters.options); - } else { - value = suppliedParams[name]; - } - - delete suppliedParams[name]; - - paramsForRecognizer[resultsName] = value; - }); - - return paramsForRecognizer; - }).property('resolvedParams.[]'), - - /** - Sets the element's `href` attribute to the url for - the `LinkView`'s targeted route. - - If the `LinkView`'s `tagName` is changed to a value other - than `a`, this property will be ignored. - - @property href - **/ - href: Ember.computed(function computeLinkViewHref() { - if (get(this, 'tagName') !== 'a') { return; } - - var router = get(this, 'router'), - routeArgs = get(this, 'routeArgs'); - - return routeArgs ? router.generate.apply(router, routeArgs) : get(this, 'loadingHref'); - }).property('routeArgs'), - - /** - The default href value to use while a link-to is loading. - Only applies when tagName is 'a' - - @property loadingHref - @type String - @default # - */ - loadingHref: '#' - }); - - LinkView.toString = function() { return "LinkView"; }; - - /** - The `{{link-to}}` helper renders a link to the supplied - `routeName` passing an optionally supplied model to the - route as its `model` context of the route. The block - for `{{link-to}}` becomes the innerHTML of the rendered - element: - - ```handlebars - {{#link-to 'photoGallery'}} - Great Hamster Photos - {{/link-to}} - ``` - - ```html - - Great Hamster Photos - - ``` - - ### Supplying a tagName - By default `{{link-to}}` renders an `` element. This can - be overridden for a single use of `{{link-to}}` by supplying - a `tagName` option: - - ```handlebars - {{#link-to 'photoGallery' tagName="li"}} - Great Hamster Photos - {{/link-to}} - ``` - - ```html -
  • - Great Hamster Photos -
  • - ``` - - To override this option for your entire application, see - "Overriding Application-wide Defaults". - - ### Disabling the `link-to` helper - By default `{{link-to}}` is enabled. - any passed value to `disabled` helper property will disable the `link-to` helper. - - static use: the `disabled` option: - - ```handlebars - {{#link-to 'photoGallery' disabled=true}} - Great Hamster Photos - {{/link-to}} - ``` - - dynamic use: the `disabledWhen` option: - - ```handlebars - {{#link-to 'photoGallery' disabledWhen=controller.someProperty}} - Great Hamster Photos - {{/link-to}} - ``` - - any passed value to `disabled` will disable it except `undefined`. - to ensure that only `true` disable the `link-to` helper you can - override the global behaviour of `Ember.LinkView`. - - ```javascript - Ember.LinkView.reopen({ - disabled: Ember.computed(function(key, value) { - if (value !== undefined) { - this.set('_isDisabled', value === true); - } - return value === true ? get(this, 'disabledClass') : false; - }) - }); - ``` - - see "Overriding Application-wide Defaults" for more. - - ### Handling `href` - `{{link-to}}` will use your application's Router to - fill the element's `href` property with a url that - matches the path to the supplied `routeName` for your - routers's configured `Location` scheme, which defaults - to Ember.HashLocation. - - ### Handling current route - `{{link-to}}` will apply a CSS class name of 'active' - when the application's current route matches - the supplied routeName. For example, if the application's - current route is 'photoGallery.recent' the following - use of `{{link-to}}`: - - ```handlebars - {{#link-to 'photoGallery.recent'}} - Great Hamster Photos from the last week - {{/link-to}} - ``` - - will result in - - ```html -
    - Great Hamster Photos - - ``` - - The CSS class name used for active classes can be customized - for a single use of `{{link-to}}` by passing an `activeClass` - option: - - ```handlebars - {{#link-to 'photoGallery.recent' activeClass="current-url"}} - Great Hamster Photos from the last week - {{/link-to}} - ``` - - ```html - - Great Hamster Photos - - ``` - - To override this option for your entire application, see - "Overriding Application-wide Defaults". - - ### Supplying a model - An optional model argument can be used for routes whose - paths contain dynamic segments. This argument will become - the model context of the linked route: - - ```javascript - App.Router.map(function() { - this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); - }); - ``` - - ```handlebars - {{#link-to 'photoGallery' aPhoto}} - {{aPhoto.title}} - {{/link-to}} - ``` - - ```html - - Tomster - - ``` - - ### Supplying multiple models - For deep-linking to route paths that contain multiple - dynamic segments, multiple model arguments can be used. - As the router transitions through the route path, each - supplied model argument will become the context for the - route with the dynamic segments: - - ```javascript - App.Router.map(function() { - this.resource("photoGallery", {path: "hamster-photos/:photo_id"}, function() { - this.route("comment", {path: "comments/:comment_id"}); - }); - }); - ``` - This argument will become the model context of the linked route: - - ```handlebars - {{#link-to 'photoGallery.comment' aPhoto comment}} - {{comment.body}} - {{/link-to}} - ``` - - ```html - - A+++ would snuggle again. - - ``` - - ### Supplying an explicit dynamic segment value - If you don't have a model object available to pass to `{{link-to}}`, - an optional string or integer argument can be passed for routes whose - paths contain dynamic segments. This argument will become the value - of the dynamic segment: - - ```javascript - App.Router.map(function() { - this.resource("photoGallery", {path: "hamster-photos/:photo_id"}); - }); - ``` - - ```handlebars - {{#link-to 'photoGallery' aPhotoId}} - {{aPhoto.title}} - {{/link-to}} - ``` - - ```html - - Tomster - - ``` - - When transitioning into the linked route, the `model` hook will - be triggered with parameters including this passed identifier. - - ### Allowing Default Action - - By default the `{{link-to}}` helper prevents the default browser action - by calling `preventDefault()` as this sort of action bubbling is normally - handled internally and we do not want to take the browser to a new URL (for - example). - - If you need to override this behavior specify `preventDefault=false` in - your template: - - ```handlebars - {{#link-to 'photoGallery' aPhotoId preventDefault=false}} - {{aPhotoId.title}} - {{/link-to}} - ``` - - ### Overriding attributes - You can override any given property of the Ember.LinkView - that is generated by the `{{link-to}}` helper by passing - key/value pairs, like so: - - ```handlebars - {{#link-to aPhoto tagName='li' title='Following this link will change your life' classNames='pic sweet'}} - Uh-mazing! - {{/link-to}} - ``` - - See [Ember.LinkView](/api/classes/Ember.LinkView.html) for a - complete list of overrideable properties. Be sure to also - check out inherited properties of `LinkView`. - - ### Overriding Application-wide Defaults - ``{{link-to}}`` creates an instance of Ember.LinkView - for rendering. To override options for your entire - application, reopen Ember.LinkView and supply the - desired values: - - ``` javascript - Ember.LinkView.reopen({ - activeClass: "is-active", - tagName: 'li' - }) - ``` - - It is also possible to override the default event in - this manner: - - ``` javascript - Ember.LinkView.reopen({ - eventName: 'customEventName' - }); - ``` - - @method link-to - @for Ember.Handlebars.helpers - @param {String} routeName - @param {Object} [context]* - @param [options] {Object} Handlebars key/value pairs of options, you can override any property of Ember.LinkView - @return {String} HTML string - @see {Ember.LinkView} - */ - Ember.Handlebars.registerHelper('link-to', function linkToHelper(name) { - var options = slice.call(arguments, -1)[0], - params = slice.call(arguments, 0, -1), - hash = options.hash; - - if (params[params.length - 1] instanceof QueryParams) { - hash.queryParamsObject = params.pop(); - } - - hash.disabledBinding = hash.disabledWhen; - - if (!options.fn) { - var linkTitle = params.shift(); - var linkType = options.types.shift(); - var context = this; - if (linkType === 'ID') { - options.linkTextPath = linkTitle; - options.fn = function() { - return Ember.Handlebars.getEscaped(context, linkTitle, options); - }; - } else { - options.fn = function() { - return linkTitle; - }; - } - } - - hash.parameters = { - context: this, - options: options, - params: params - }; - - return Ember.Handlebars.helpers.view.call(this, LinkView, options); - }); - - - - /** - See [link-to](/api/classes/Ember.Handlebars.helpers.html#method_link-to) - - @method linkTo - @for Ember.Handlebars.helpers - @deprecated - @param {String} routeName - @param {Object} [context]* - @return {String} HTML string - */ - Ember.Handlebars.registerHelper('linkTo', function linkToHelper() { - Ember.warn("The 'linkTo' view helper is deprecated in favor of 'link-to'"); - return Ember.Handlebars.helpers['link-to'].apply(this, arguments); - }); -}); - - - -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ - -var get = Ember.get, set = Ember.set; -Ember.onLoad('Ember.Handlebars', function(Handlebars) { - /** - @module ember - @submodule ember-routing - */ - - Handlebars.OutletView = Ember.ContainerView.extend(Ember._Metamorph); - - /** - The `outlet` helper is a placeholder that the router will fill in with - the appropriate template based on the current state of the application. - - ``` handlebars - {{outlet}} - ``` - - By default, a template based on Ember's naming conventions will be rendered - into the `outlet` (e.g. `App.PostsRoute` will render the `posts` template). - - You can render a different template by using the `render()` method in the - route's `renderTemplate` hook. The following will render the `favoritePost` - template into the `outlet`. - - ``` javascript - App.PostsRoute = Ember.Route.extend({ - renderTemplate: function() { - this.render('favoritePost'); - } - }); - ``` - - You can create custom named outlets for more control. - - ``` handlebars - {{outlet 'favoritePost'}} - {{outlet 'posts'}} - ``` - - Then you can define what template is rendered into each outlet in your - route. - - - ``` javascript - App.PostsRoute = Ember.Route.extend({ - renderTemplate: function() { - this.render('favoritePost', { outlet: 'favoritePost' }); - this.render('posts', { outlet: 'posts' }); - } - }); - ``` - - You can specify the view that the outlet uses to contain and manage the - templates rendered into it. - - ``` handlebars - {{outlet view='sectionContainer'}} - ``` - - ``` javascript - App.SectionContainer = Ember.ContainerView.extend({ - tagName: 'section', - classNames: ['special'] - }); - ``` - - @method outlet - @for Ember.Handlebars.helpers - @param {String} property the property on the controller - that holds the view for this outlet - @return {String} HTML string - */ - Handlebars.registerHelper('outlet', function outletHelper(property, options) { - - var outletSource, - container, - viewName, - viewClass, - viewFullName; - - if (property && property.data && property.data.isRenderData) { - options = property; - property = 'main'; - } - - container = options.data.view.container; - - outletSource = options.data.view; - while (!outletSource.get('template.isTop')) { - outletSource = outletSource.get('_parentView'); - } - - // provide controller override - viewName = options.hash.view; - - if (viewName) { - viewFullName = 'view:' + viewName; - Ember.assert("Using a quoteless view parameter with {{outlet}} is not supported. Please update to quoted usage '{{outlet \"" + viewName + "\"}}.", options.hashTypes.view !== 'ID'); - Ember.assert("The view name you supplied '" + viewName + "' did not resolve to a view.", container.has(viewFullName)); - } - - viewClass = viewName ? container.lookupFactory(viewFullName) : options.hash.viewClass || Handlebars.OutletView; - - options.data.view.set('outletSource', outletSource); - options.hash.currentViewBinding = '_view.outletSource._outlets.' + property; - - return Handlebars.helpers.view.call(this, viewClass, options); - }); -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ - -var get = Ember.get, set = Ember.set; -Ember.onLoad('Ember.Handlebars', function(Handlebars) { - - /** - Calling ``{{render}}`` from within a template will insert another - template that matches the provided name. The inserted template will - access its properties on its own controller (rather than the controller - of the parent template). - - If a view class with the same name exists, the view class also will be used. - - Note: A given controller may only be used *once* in your app in this manner. - A singleton instance of the controller will be created for you. - - Example: - - ```javascript - App.NavigationController = Ember.Controller.extend({ - who: "world" - }); - ``` - - ```handlebars - - Hello, {{who}}. - ``` - - ```handelbars - -

    My great app

    - {{render "navigation"}} - ``` - - ```html -

    My great app

    -
    - Hello, world. -
    - ``` - - Optionally you may provide a second argument: a property path - that will be bound to the `model` property of the controller. - - If a `model` property path is specified, then a new instance of the - controller will be created and `{{render}}` can be used multiple times - with the same name. - - For example if you had this `author` template. - - ```handlebars -
    - Written by {{firstName}} {{lastName}}. - Total Posts: {{postCount}} -
    - ``` - - You could render it inside the `post` template using the `render` helper. - - ```handlebars -
    -

    {{title}}

    -
    {{body}}
    - {{render "author" author}} -
    - ``` - - @method render - @for Ember.Handlebars.helpers - @param {String} name - @param {Object?} contextString - @param {Hash} options - @return {String} HTML string - */ - Ember.Handlebars.registerHelper('render', function renderHelper(name, contextString, options) { - var length = arguments.length; - - var contextProvided = length === 3, - container, router, controller, view, context, lookupOptions; - - container = (options || contextString).data.keywords.controller.container; - router = container.lookup('router:main'); - - if (length === 2) { - // use the singleton controller - options = contextString; - contextString = undefined; - Ember.assert("You can only use the {{render}} helper once without a model object as its second argument, as in {{render \"post\" post}}.", !router || !router._lookupActiveView(name)); - } else if (length === 3) { - // create a new controller - context = Ember.Handlebars.get(options.contexts[1], contextString, options); - } else { - throw Ember.Error("You must pass a templateName to render"); - } - - Ember.deprecate("Using a quoteless parameter with {{render}} is deprecated. Please update to quoted usage '{{render \"" + name + "\"}}.", options.types[0] !== 'ID'); - - // # legacy namespace - name = name.replace(/\//g, '.'); - // \ legacy slash as namespace support - - - view = container.lookup('view:' + name) || container.lookup('view:default'); - - // provide controller override - var controllerName = options.hash.controller || name; - var controllerFullName = 'controller:' + controllerName; - - if (options.hash.controller) { - Ember.assert("The controller name you supplied '" + controllerName + "' did not resolve to a controller.", container.has(controllerFullName)); - } - - var parentController = options.data.keywords.controller; - - // choose name - if (length > 2) { - var factory = container.lookupFactory(controllerFullName) || - Ember.generateControllerFactory(container, controllerName, context); - - controller = factory.create({ - model: context, - parentController: parentController, - target: parentController - }); - - } else { - controller = container.lookup(controllerFullName) || - Ember.generateController(container, controllerName); - - controller.setProperties({ - target: parentController, - parentController: parentController - }); - } - - var root = options.contexts[1]; - - if (root) { - view.registerObserver(root, contextString, function() { - controller.set('model', Ember.Handlebars.get(root, contextString, options)); - }); - } - - options.hash.viewName = Ember.String.camelize(name); - - var templateName = 'template:' + name; - Ember.assert("You used `{{render '" + name + "'}}`, but '" + name + "' can not be found as either a template or a view.", container.has("view:" + name) || container.has(templateName) || options.fn); - options.hash.template = container.lookup(templateName); - - options.hash.controller = controller; - - if (router && !context) { - router._connectActiveView(name, view); - } - - Ember.Handlebars.helpers.view.call(this, view, options); - }); -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ -Ember.onLoad('Ember.Handlebars', function(Handlebars) { - - var resolveParams = Ember.Router.resolveParams, - isSimpleClick = Ember.ViewUtils.isSimpleClick; - - var EmberHandlebars = Ember.Handlebars, - handlebarsGet = EmberHandlebars.get, - SafeString = EmberHandlebars.SafeString, - forEach = Ember.ArrayPolyfills.forEach, - get = Ember.get, - a_slice = Array.prototype.slice; - - function args(options, actionName) { - var ret = []; - if (actionName) { ret.push(actionName); } - - var types = options.options.types.slice(1), - data = options.options.data; - - return ret.concat(resolveParams(options.context, options.params, { types: types, data: data })); - } - - var ActionHelper = EmberHandlebars.ActionHelper = { - registeredActions: {} - }; - - var keys = ["alt", "shift", "meta", "ctrl"]; - - var POINTER_EVENT_TYPE_REGEX = /^click|mouse|touch/; - - var isAllowedEvent = function(event, allowedKeys) { - if (typeof allowedKeys === "undefined") { - if (POINTER_EVENT_TYPE_REGEX.test(event.type)) { - return isSimpleClick(event); - } else { - allowedKeys = ''; - } - } - - if (allowedKeys.indexOf("any") >= 0) { - return true; - } - - var allowed = true; - - forEach.call(keys, function(key) { - if (event[key + "Key"] && allowedKeys.indexOf(key) === -1) { - allowed = false; - } - }); - - return allowed; - }; - - ActionHelper.registerAction = function(actionNameOrPath, options, allowedKeys) { - var actionId = ++Ember.uuid; - - ActionHelper.registeredActions[actionId] = { - eventName: options.eventName, - handler: function handleRegisteredAction(event) { - if (!isAllowedEvent(event, allowedKeys)) { return true; } - - if (options.preventDefault !== false) { - event.preventDefault(); - } - - if (options.bubbles === false) { - event.stopPropagation(); - } - - var target = options.target, - actionName; - - if (target.target) { - target = handlebarsGet(target.root, target.target, target.options); - } else { - target = target.root; - } - - - if (options.boundProperty) { - actionName = handlebarsGet(target, actionNameOrPath, options.options); - - if(typeof actionName === 'undefined' || typeof actionName === 'function') { - Ember.assert("You specified a quoteless path to the {{action}} helper '" + actionNameOrPath + "' which did not resolve to an actionName. Perhaps you meant to use a quoted actionName? (e.g. {{action '" + actionNameOrPath + "'}}).", true); - actionName = actionNameOrPath; - } - } - - if (!actionName) { - actionName = actionNameOrPath; - } - - Ember.run(function runRegisteredAction() { - if (target.send) { - target.send.apply(target, args(options.parameters, actionName)); - } else { - Ember.assert("The action '" + actionName + "' did not exist on " + target, typeof target[actionName] === 'function'); - target[actionName].apply(target, args(options.parameters)); - } - }); - } - }; - - options.view.on('willClearRender', function() { - delete ActionHelper.registeredActions[actionId]; - }); - - return actionId; - }; - - /** - The `{{action}}` helper registers an HTML element within a template for DOM - event handling and forwards that interaction to the templates's controller - or supplied `target` option (see 'Specifying a Target'). - - If the controller does not implement the event, the event is sent - to the current route, and it bubbles up the route hierarchy from there. - - User interaction with that element will invoke the supplied action name on - the appropriate target. Specifying a non-quoted action name will result in - a bound property lookup at the time the event will be triggered. - - Given the following application Handlebars template on the page - - ```handlebars -
    - click me -
    - ``` - - And application code - - ```javascript - App.ApplicationController = Ember.Controller.extend({ - actions: { - anActionName: function() { - } - } - }); - ``` - - Will result in the following rendered HTML - - ```html -
    -
    - click me -
    -
    - ``` - - Clicking "click me" will trigger the `anActionName` action of the - `App.ApplicationController`. In this case, no additional parameters will be passed. - - If you provide additional parameters to the helper: - - ```handlebars - - ``` - - Those parameters will be passed along as arguments to the JavaScript - function implementing the action. - - ### Event Propagation - - Events triggered through the action helper will automatically have - `.preventDefault()` called on them. You do not need to do so in your event - handlers. If you need to allow event propagation (to handle file inputs for - example) you can supply the `preventDefault=false` option to the `{{action}}` helper: - - ```handlebars -
    - - -
    - ``` - - To disable bubbling, pass `bubbles=false` to the helper: - - ```handlebars - - ``` - - If you need the default handler to trigger you should either register your - own event handler, or use event methods on your view class. See [Ember.View](/api/classes/Ember.View.html) - 'Responding to Browser Events' for more information. - - ### Specifying DOM event type - - By default the `{{action}}` helper registers for DOM `click` events. You can - supply an `on` option to the helper to specify a different DOM event name: - - ```handlebars -
    - click me -
    - ``` - - See `Ember.View` 'Responding to Browser Events' for a list of - acceptable DOM event names. - - NOTE: Because `{{action}}` depends on Ember's event dispatch system it will - only function if an `Ember.EventDispatcher` instance is available. An - `Ember.EventDispatcher` instance will be created when a new `Ember.Application` - is created. Having an instance of `Ember.Application` will satisfy this - requirement. - - ### Specifying whitelisted modifier keys - - By default the `{{action}}` helper will ignore click event with pressed modifier - keys. You can supply an `allowedKeys` option to specify which keys should not be ignored. - - ```handlebars -
    - click me -
    - ``` - - This way the `{{action}}` will fire when clicking with the alt key pressed down. - - Alternatively, supply "any" to the `allowedKeys` option to accept any combination of modifier keys. - - ```handlebars -
    - click me with any key pressed -
    - ``` - - ### Specifying a Target - - There are several possible target objects for `{{action}}` helpers: - - In a typical Ember application, where views are managed through use of the - `{{outlet}}` helper, actions will bubble to the current controller, then - to the current route, and then up the route hierarchy. - - Alternatively, a `target` option can be provided to the helper to change - which object will receive the method call. This option must be a path - to an object, accessible in the current context: - - ```handlebars - {{! the application template }} -
    - click me -
    - ``` - - ```javascript - App.ApplicationView = Ember.View.extend({ - actions: { - anActionName: function(){} - } - }); - - ``` - - ### Additional Parameters - - You may specify additional parameters to the `{{action}}` helper. These - parameters are passed along as the arguments to the JavaScript function - implementing the action. - - ```handlebars - {{#each person in people}} -
    - click me -
    - {{/each}} - ``` - - Clicking "click me" will trigger the `edit` method on the current controller - with the value of `person` as a parameter. - - @method action - @for Ember.Handlebars.helpers - @param {String} actionName - @param {Object} [context]* - @param {Hash} options - */ - EmberHandlebars.registerHelper('action', function actionHelper(actionName) { - var options = arguments[arguments.length - 1], - contexts = a_slice.call(arguments, 1, -1); - - var hash = options.hash, - controller = options.data.keywords.controller; - - // create a hash to pass along to registerAction - var action = { - eventName: hash.on || "click", - parameters: { - context: this, - options: options, - params: contexts - }, - view: options.data.view, - bubbles: hash.bubbles, - preventDefault: hash.preventDefault, - target: { options: options }, - boundProperty: options.types[0] === "ID" - }; - - if (hash.target) { - action.target.root = this; - action.target.target = hash.target; - } else if (controller) { - action.target.root = controller; - } - - var actionId = ActionHelper.registerAction(actionName, action, hash.allowedKeys); - return new SafeString('data-ember-action="' + actionId + '"'); - }); -}); - -})(); - - - -(function() { - -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ - -var get = Ember.get, set = Ember.set, - map = Ember.EnumerableUtils.map; - -var queuedQueryParamChanges = {}; - -Ember.ControllerMixin.reopen({ - /** - Transition the application into another route. The route may - be either a single route or route path: - - ```javascript - aController.transitionToRoute('blogPosts'); - aController.transitionToRoute('blogPosts.recentEntries'); - ``` - - Optionally supply a model for the route in question. The model - will be serialized into the URL using the `serialize` hook of - the route: - - ```javascript - aController.transitionToRoute('blogPost', aPost); - ``` - - If a literal is passed (such as a number or a string), it will - be treated as an identifier instead. In this case, the `model` - hook of the route will be triggered: - - ```javascript - aController.transitionToRoute('blogPost', 1); - ``` - - Multiple models will be applied last to first recursively up the - resource tree. - - ```javascript - App.Router.map(function() { - this.resource('blogPost', {path:':blogPostId'}, function(){ - this.resource('blogComment', {path: ':blogCommentId'}); - }); - }); - - aController.transitionToRoute('blogComment', aPost, aComment); - aController.transitionToRoute('blogComment', 1, 13); - ``` - - It is also possible to pass a URL (a string that starts with a - `/`). This is intended for testing and debugging purposes and - should rarely be used in production code. - - ```javascript - aController.transitionToRoute('/'); - aController.transitionToRoute('/blog/post/1/comment/13'); - ``` - - See also [replaceRoute](/api/classes/Ember.ControllerMixin.html#method_replaceRoute). - - @param {String} name the name of the route or a URL - @param {...Object} models the model(s) or identifier(s) to be used - while transitioning to the route. - @for Ember.ControllerMixin - @method transitionToRoute - */ - transitionToRoute: function() { - // target may be either another controller or a router - var target = get(this, 'target'), - method = target.transitionToRoute || target.transitionTo; - return method.apply(target, arguments); - }, - - /** - @deprecated - @for Ember.ControllerMixin - @method transitionTo - */ - transitionTo: function() { - Ember.deprecate("transitionTo is deprecated. Please use transitionToRoute."); - return this.transitionToRoute.apply(this, arguments); - }, - - /** - Transition into another route while replacing the current URL, if possible. - This will replace the current history entry instead of adding a new one. - Beside that, it is identical to `transitionToRoute` in all other respects. - - ```javascript - aController.replaceRoute('blogPosts'); - aController.replaceRoute('blogPosts.recentEntries'); - ``` - - Optionally supply a model for the route in question. The model - will be serialized into the URL using the `serialize` hook of - the route: - - ```javascript - aController.replaceRoute('blogPost', aPost); - ``` - - If a literal is passed (such as a number or a string), it will - be treated as an identifier instead. In this case, the `model` - hook of the route will be triggered: - - ```javascript - aController.replaceRoute('blogPost', 1); - ``` - - Multiple models will be applied last to first recursively up the - resource tree. - - ```javascript - App.Router.map(function() { - this.resource('blogPost', {path:':blogPostId'}, function(){ - this.resource('blogComment', {path: ':blogCommentId'}); - }); - }); - - aController.replaceRoute('blogComment', aPost, aComment); - aController.replaceRoute('blogComment', 1, 13); - ``` - - It is also possible to pass a URL (a string that starts with a - `/`). This is intended for testing and debugging purposes and - should rarely be used in production code. - - ```javascript - aController.replaceRoute('/'); - aController.replaceRoute('/blog/post/1/comment/13'); - ``` - - @param {String} name the name of the route or a URL - @param {...Object} models the model(s) or identifier(s) to be used - while transitioning to the route. - @for Ember.ControllerMixin - @method replaceRoute - */ - replaceRoute: function() { - // target may be either another controller or a router - var target = get(this, 'target'), - method = target.replaceRoute || target.replaceWith; - return method.apply(target, arguments); - }, - - /** - @deprecated - @for Ember.ControllerMixin - @method replaceWith - */ - replaceWith: function() { - Ember.deprecate("replaceWith is deprecated. Please use replaceRoute."); - return this.replaceRoute.apply(this, arguments); - } -}); - - -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ - -var get = Ember.get, set = Ember.set; - -Ember.View.reopen({ - - /** - Sets the private `_outlets` object on the view. - - @method init - */ - init: function() { - set(this, '_outlets', {}); - this._super(); - }, - - /** - Manually fill any of a view's `{{outlet}}` areas with the - supplied view. - - Example - - ```javascript - var MyView = Ember.View.extend({ - template: Ember.Handlebars.compile('Child view: {{outlet "main"}} ') - }); - var myView = MyView.create(); - myView.appendTo('body'); - // The html for myView now looks like: - //
    Child view:
    - - var FooView = Ember.View.extend({ - template: Ember.Handlebars.compile('

    Foo

    ') - }); - var fooView = FooView.create(); - myView.connectOutlet('main', fooView); - // The html for myView now looks like: - //
    Child view: - //

    Foo

    - //
    - ``` - @method connectOutlet - @param {String} outletName A unique name for the outlet - @param {Object} view An Ember.View - */ - connectOutlet: function(outletName, view) { - if (this._pendingDisconnections) { - delete this._pendingDisconnections[outletName]; - } - - if (this._hasEquivalentView(outletName, view)) { - view.destroy(); - return; - } - - var outlets = get(this, '_outlets'), - container = get(this, 'container'), - router = container && container.lookup('router:main'), - renderedName = get(view, 'renderedName'); - - set(outlets, outletName, view); - - if (router && renderedName) { - router._connectActiveView(renderedName, view); - } - }, - - /** - Determines if the view has already been created by checking if - the view has the same constructor, template, and context as the - view in the `_outlets` object. - - @private - @method _hasEquivalentView - @param {String} outletName The name of the outlet we are checking - @param {Object} view An Ember.View - @return {Boolean} - */ - _hasEquivalentView: function(outletName, view) { - var existingView = get(this, '_outlets.'+outletName); - return existingView && - existingView.constructor === view.constructor && - existingView.get('template') === view.get('template') && - existingView.get('context') === view.get('context'); - }, - - /** - Removes an outlet from the view. - - Example - - ```javascript - var MyView = Ember.View.extend({ - template: Ember.Handlebars.compile('Child view: {{outlet "main"}} ') - }); - var myView = MyView.create(); - myView.appendTo('body'); - // myView's html: - //
    Child view:
    - - var FooView = Ember.View.extend({ - template: Ember.Handlebars.compile('

    Foo

    ') - }); - var fooView = FooView.create(); - myView.connectOutlet('main', fooView); - // myView's html: - //
    Child view: - //

    Foo

    - //
    - - myView.disconnectOutlet('main'); - // myView's html: - //
    Child view:
    - ``` - - @method disconnectOutlet - @param {String} outletName The name of the outlet to be removed - */ - disconnectOutlet: function(outletName) { - if (!this._pendingDisconnections) { - this._pendingDisconnections = {}; - } - this._pendingDisconnections[outletName] = true; - Ember.run.once(this, '_finishDisconnections'); - }, - - /** - Gets an outlet that is pending disconnection and then - nullifys the object on the `_outlet` object. - - @private - @method _finishDisconnections - */ - _finishDisconnections: function() { - if (this.isDestroyed) return; // _outlets will be gone anyway - var outlets = get(this, '_outlets'); - var pendingDisconnections = this._pendingDisconnections; - this._pendingDisconnections = null; - - for (var outletName in pendingDisconnections) { - set(outlets, outletName, null); - } - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-views -*/ - -// Add a new named queue after the 'actions' queue (where RSVP promises -// resolve), which is used in router transitions to prevent unnecessary -// loading state entry if all context promises resolve on the -// 'actions' queue first. - -var queues = Ember.run.queues, - indexOf = Ember.ArrayPolyfills.indexOf; -queues.splice(indexOf.call(queues, 'actions') + 1, 0, 'routerTransitions'); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ - -var get = Ember.get, set = Ember.set; - -/** - Ember.Location returns an instance of the correct implementation of - the `location` API. - - ## Implementations - - You can pass an implementation name (`hash`, `history`, `none`) to force a - particular implementation to be used in your application. - - ### HashLocation - - Using `HashLocation` results in URLs with a `#` (hash sign) separating the - server side URL portion of the URL from the portion that is used by Ember. - This relies upon the `hashchange` event existing in the browser. - - Example: - - ```javascript - App.Router.map(function() { - this.resource('posts', function() { - this.route('new'); - }); - }); - - App.Router.reopen({ - location: 'hash' - }); - ``` - - This will result in a posts.new url of `/#/posts/new`. - - ### HistoryLocation - - Using `HistoryLocation` results in URLs that are indistinguishable from a - standard URL. This relies upon the browser's `history` API. - - Example: - - ```javascript - App.Router.map(function() { - this.resource('posts', function() { - this.route('new'); - }); - }); - - App.Router.reopen({ - location: 'history' - }); - ``` - - This will result in a posts.new url of `/posts/new`. - - Keep in mind that your server must serve the Ember app at all the routes you - define. - - ### AutoLocation - - Using `AutoLocation`, the router will use the best Location class supported by - the browser it is running in. - - Browsers that support the `history` API will use `HistoryLocation`, those that - do not, but still support the `hashchange` event will use `HashLocation`, and - in the rare case neither is supported will use `NoneLocation`. - - Example: - - ```javascript - App.Router.map(function() { - this.resource('posts', function() { - this.route('new'); - }); - }); - - App.Router.reopen({ - location: 'auto' - }); - ``` - - This will result in a posts.new url of `/posts/new` for modern browsers that - support the `history` api or `/#/posts/new` for older ones, like Internet - Explorer 9 and below. - - When a user visits a link to your application, they will be automatically - upgraded or downgraded to the appropriate `Location` class, with the URL - transformed accordingly, if needed. - - Keep in mind that since some of your users will use `HistoryLocation`, your - server must serve the Ember app at all the routes you define. - - ### NoneLocation - - Using `NoneLocation` causes Ember to not store the applications URL state - in the actual URL. This is generally used for testing purposes, and is one - of the changes made when calling `App.setupForTesting()`. - - ## Location API - - Each location implementation must provide the following methods: - - * implementation: returns the string name used to reference the implementation. - * getURL: returns the current URL. - * setURL(path): sets the current URL. - * replaceURL(path): replace the current URL (optional). - * onUpdateURL(callback): triggers the callback when the URL changes. - * formatURL(url): formats `url` to be placed into `href` attribute. - - Calling setURL or replaceURL will not trigger onUpdateURL callbacks. - - @class Location - @namespace Ember - @static -*/ -Ember.Location = { - /** - This is deprecated in favor of using the container to lookup the location - implementation as desired. - - For example: - - ```javascript - // Given a location registered as follows: - container.register('location:history-test', HistoryTestLocation); - - // You could create a new instance via: - container.lookup('location:history-test'); - ``` - - @method create - @param {Object} options - @return {Object} an instance of an implementation of the `location` API - @deprecated Use the container to lookup the location implementation that you - need. - */ - create: function(options) { - var implementation = options && options.implementation; - Ember.assert("Ember.Location.create: you must specify a 'implementation' option", !!implementation); - - var implementationClass = this.implementations[implementation]; - Ember.assert("Ember.Location.create: " + implementation + " is not a valid implementation", !!implementationClass); - - return implementationClass.create.apply(implementationClass, arguments); - }, - - /** - This is deprecated in favor of using the container to register the - location implementation as desired. - - Example: - - ```javascript - Application.initializer({ - name: "history-test-location", - - initialize: function(container, application) { - application.register('location:history-test', HistoryTestLocation); - } - }); - ``` - - @method registerImplementation - @param {String} name - @param {Object} implementation of the `location` API - @deprecated Register your custom location implementation with the - container directly. - */ - registerImplementation: function(name, implementation) { - Ember.deprecate('Using the Ember.Location.registerImplementation is no longer supported. Register your custom location implementation with the container instead.', false); - - this.implementations[name] = implementation; - }, - - implementations: {}, - _location: window.location, - - /** - Returns the current `location.hash` by parsing location.href since browsers - inconsistently URL-decode `location.hash`. - - https://bugzilla.mozilla.org/show_bug.cgi?id=483304 - - @private - @method getHash - */ - _getHash: function () { - // AutoLocation has it at _location, HashLocation at .location. - // Being nice and not changing - var href = (this._location || this.location).href, - hashIndex = href.indexOf('#'); - - if (hashIndex === -1) { - return ''; - } else { - return href.substr(hashIndex); - } - } -}; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ - -var get = Ember.get, set = Ember.set; - -/** - Ember.NoneLocation does not interact with the browser. It is useful for - testing, or when you need to manage state with your Router, but temporarily - don't want it to muck with the URL (for example when you embed your - application in a larger page). - - @class NoneLocation - @namespace Ember - @extends Ember.Object -*/ -Ember.NoneLocation = Ember.Object.extend({ - implementation: 'none', - path: '', - - /** - Returns the current path. - - @private - @method getURL - @return {String} path - */ - getURL: function() { - return get(this, 'path'); - }, - - /** - Set the path and remembers what was set. Using this method - to change the path will not invoke the `updateURL` callback. - - @private - @method setURL - @param path {String} - */ - setURL: function(path) { - set(this, 'path', path); - }, - - /** - Register a callback to be invoked when the path changes. These - callbacks will execute when the user presses the back or forward - button, but not after `setURL` is invoked. - - @private - @method onUpdateURL - @param callback {Function} - */ - onUpdateURL: function(callback) { - this.updateCallback = callback; - }, - - /** - Sets the path and calls the `updateURL` callback. - - @private - @method handleURL - @param callback {Function} - */ - handleURL: function(url) { - set(this, 'path', url); - this.updateCallback(url); - }, - - /** - Given a URL, formats it to be placed into the page as part - of an element's `href` attribute. - - This is used, for example, when using the {{action}} helper - to generate a URL based on an event. - - @private - @method formatURL - @param url {String} - @return {String} url - */ - formatURL: function(url) { - // The return value is not overly meaningful, but we do not want to throw - // errors when test code renders templates containing {{action href=true}} - // helpers. - return url; - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ - -var get = Ember.get, set = Ember.set; - -/** - `Ember.HashLocation` implements the location API using the browser's - hash. At present, it relies on a `hashchange` event existing in the - browser. - - @class HashLocation - @namespace Ember - @extends Ember.Object -*/ -Ember.HashLocation = Ember.Object.extend({ - implementation: 'hash', - - init: function() { - set(this, 'location', get(this, '_location') || window.location); - }, - - /** - @private - - Returns normalized location.hash - - @method getHash - */ - getHash: Ember.Location._getHash, - - /** - Returns the current `location.hash`, minus the '#' at the front. - - @private - @method getURL - */ - getURL: function() { - return this.getHash().substr(1); - }, - - /** - Set the `location.hash` and remembers what was set. This prevents - `onUpdateURL` callbacks from triggering when the hash was set by - `HashLocation`. - - @private - @method setURL - @param path {String} - */ - setURL: function(path) { - get(this, 'location').hash = path; - set(this, 'lastSetURL', path); - }, - - /** - Uses location.replace to update the url without a page reload - or history modification. - - @private - @method replaceURL - @param path {String} - */ - replaceURL: function(path) { - get(this, 'location').replace('#' + path); - set(this, 'lastSetURL', path); - }, - - /** - Register a callback to be invoked when the hash changes. These - callbacks will execute when the user presses the back or forward - button, but not after `setURL` is invoked. - - @private - @method onUpdateURL - @param callback {Function} - */ - onUpdateURL: function(callback) { - var self = this; - var guid = Ember.guidFor(this); - - Ember.$(window).on('hashchange.ember-location-'+guid, function() { - Ember.run(function() { - var path = self.getURL(); - if (get(self, 'lastSetURL') === path) { return; } - - set(self, 'lastSetURL', null); - - callback(path); - }); - }); - }, - - /** - Given a URL, formats it to be placed into the page as part - of an element's `href` attribute. - - This is used, for example, when using the {{action}} helper - to generate a URL based on an event. - - @private - @method formatURL - @param url {String} - */ - formatURL: function(url) { - return '#'+url; - }, - - /** - Cleans up the HashLocation event listener. - - @private - @method willDestroy - */ - willDestroy: function() { - var guid = Ember.guidFor(this); - - Ember.$(window).off('hashchange.ember-location-'+guid); - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-routing -*/ - -var get = Ember.get, set = Ember.set; -var popstateFired = false; -var supportsHistoryState = window.history && 'state' in window.history; - -/** - Ember.HistoryLocation implements the location API using the browser's - history.pushState API. - - @class HistoryLocation - @namespace Ember - @extends Ember.Object -*/ -Ember.HistoryLocation = Ember.Object.extend({ - implementation: 'history', - - init: function() { - set(this, 'location', get(this, 'location') || window.location); - set(this, 'baseURL', Ember.$('base').attr('href') || ''); - }, - - /** - Used to set state on first call to setURL - - @private - @method initState - */ - initState: function() { - set(this, 'history', get(this, 'history') || window.history); - this.replaceState(this.formatURL(this.getURL())); - }, - - /** - Will be pre-pended to path upon state change - - @property rootURL - @default '/' - */ - rootURL: '/', - - /** - Returns the current `location.pathname` without `rootURL` or `baseURL` - - @private - @method getURL - @return url {String} - */ - getURL: function() { - var rootURL = get(this, 'rootURL'), - location = get(this, 'location'), - path = location.pathname, - baseURL = get(this, 'baseURL'); - - rootURL = rootURL.replace(/\/$/, ''); - baseURL = baseURL.replace(/\/$/, ''); - var url = path.replace(baseURL, '').replace(rootURL, ''); - - - return url; - }, - - /** - Uses `history.pushState` to update the url without a page reload. - - @private - @method setURL - @param path {String} - */ - setURL: function(path) { - var state = this.getState(); - path = this.formatURL(path); - - if (!state || state.path !== path) { - this.pushState(path); - } - }, - - /** - Uses `history.replaceState` to update the url without a page reload - or history modification. - - @private - @method replaceURL - @param path {String} - */ - replaceURL: function(path) { - var state = this.getState(); - path = this.formatURL(path); - - if (!state || state.path !== path) { - this.replaceState(path); - } - }, - - /** - Get the current `history.state`. Checks for if a polyfill is - required and if so fetches this._historyState. The state returned - from getState may be null if an iframe has changed a window's - history. - - @private - @method getState - @return state {Object} - */ - getState: function() { - return supportsHistoryState ? get(this, 'history').state : this._historyState; - }, - - /** - Pushes a new state. - - @private - @method pushState - @param path {String} - */ - pushState: function(path) { - var state = { path: path }; - - get(this, 'history').pushState(state, null, path); - - // store state if browser doesn't support `history.state` - if (!supportsHistoryState) { - this._historyState = state; - } - - // used for webkit workaround - this._previousURL = this.getURL(); - }, - - /** - Replaces the current state. - - @private - @method replaceState - @param path {String} - */ - replaceState: function(path) { - var state = { path: path }; - - get(this, 'history').replaceState(state, null, path); - - // store state if browser doesn't support `history.state` - if (!supportsHistoryState) { - this._historyState = state; - } - - // used for webkit workaround - this._previousURL = this.getURL(); - }, - - /** - Register a callback to be invoked whenever the browser - history changes, including using forward and back buttons. - - @private - @method onUpdateURL - @param callback {Function} - */ - onUpdateURL: function(callback) { - var guid = Ember.guidFor(this), - self = this; - - Ember.$(window).on('popstate.ember-location-'+guid, function(e) { - // Ignore initial page load popstate event in Chrome - if (!popstateFired) { - popstateFired = true; - if (self.getURL() === self._previousURL) { return; } - } - callback(self.getURL()); - }); - }, - - /** - Used when using `{{action}}` helper. The url is always appended to the rootURL. - - @private - @method formatURL - @param url {String} - @return formatted url {String} - */ - formatURL: function(url) { - var rootURL = get(this, 'rootURL'), - baseURL = get(this, 'baseURL'); - - if (url !== '') { - rootURL = rootURL.replace(/\/$/, ''); - baseURL = baseURL.replace(/\/$/, ''); - } else if(baseURL.match(/^\//) && rootURL.match(/^\//)) { - baseURL = baseURL.replace(/\/$/, ''); - } - - return baseURL + rootURL + url; - }, - - /** - Cleans up the HistoryLocation event listener. - - @private - @method willDestroy - */ - willDestroy: function() { - var guid = Ember.guidFor(this); - - Ember.$(window).off('popstate.ember-location-'+guid); - } -}); - -})(); - - - -(function() { - - /** - @module ember - @submodule ember-routing - */ - - var get = Ember.get, set = Ember.set, - HistoryLocation = Ember.HistoryLocation, - HashLocation = Ember.HashLocation, - NoneLocation = Ember.NoneLocation, - EmberLocation = Ember.Location; - - /** - Ember.AutoLocation will select the best location option based off browser - support with the priority order: history, hash, none. - - Clean pushState paths accessed by hashchange-only browsers will be redirected - to the hash-equivalent and vice versa so future transitions are consistent. - - Keep in mind that since some of your users will use `HistoryLocation`, your - server must serve the Ember app at all the routes you define. - - @class AutoLocation - @namespace Ember - @static - */ - var AutoLocation = Ember.AutoLocation = { - - /** - @private - - This property is used by router:main to know whether to cancel the routing - setup process, which is needed while we redirect the browser. - - @property cancelRouterSetup - @default false - */ - cancelRouterSetup: false, - - /** - @private - - Will be pre-pended to path upon state change. - - @property rootURL - @default '/' - */ - rootURL: '/', - - /** - @private - - Attached for mocking in tests - - @property _window - @default window - */ - _window: window, - - /** - @private - - Attached for mocking in tests - - @property location - @default window.location - */ - _location: window.location, - - /** - @private - - Attached for mocking in tests - - @property _history - @default window.history - */ - _history: window.history, - - /** - @private - - Attached for mocking in tests - - @property _HistoryLocation - @default Ember.HistoryLocation - */ - _HistoryLocation: HistoryLocation, - - /** - @private - - Attached for mocking in tests - - @property _HashLocation - @default Ember.HashLocation - */ - _HashLocation: HashLocation, - - /** - @private - - Attached for mocking in tests - - @property _NoneLocation - @default Ember.NoneLocation - */ - _NoneLocation: NoneLocation, - - /** - @private - - Returns location.origin or builds it if device doesn't support it. - - @method _getOrigin - */ - _getOrigin: function () { - var location = this._location, - origin = location.origin; - - // Older browsers, especially IE, don't have origin - if (!origin) { - origin = location.protocol + '//' + location.hostname; - - if (location.port) { - origin += ':' + location.port; - } - } - - return origin; - }, - - /** - @private - - We assume that if the history object has a pushState method, the host should - support HistoryLocation. - - @method _getSupportsHistory - */ - _getSupportsHistory: function () { - // Boosted from Modernizr: https://github.com/Modernizr/Modernizr/blob/master/feature-detects/history.js - // The stock browser on Android 2.2 & 2.3 returns positive on history support - // Unfortunately support is really buggy and there is no clean way to detect - // these bugs, so we fall back to a user agent sniff :( - var userAgent = this._window.navigator.userAgent; - - // We only want Android 2, stock browser, and not Chrome which identifies - // itself as 'Mobile Safari' as well - if (userAgent.indexOf('Android 2') !== -1 && - userAgent.indexOf('Mobile Safari') !== -1 && - userAgent.indexOf('Chrome') === -1) { - return false; - } - - return !!(this._history && 'pushState' in this._history); - }, - - /** - @private - - IE8 running in IE7 compatibility mode gives false positive, so we must also - check documentMode. - - @method _getSupportsHashChange - */ - _getSupportsHashChange: function () { - var window = this._window, - documentMode = window.document.documentMode; - - return ('onhashchange' in window && (documentMode === undefined || documentMode > 7 )); - }, - - /** - @private - - Redirects the browser using location.replace, prepending the locatin.origin - to prevent phishing attempts - - @method _replacePath - */ - _replacePath: function (path) { - this._location.replace(this._getOrigin() + path); - }, - - /** - @private - @method _getRootURL - */ - _getRootURL: function () { - return this.rootURL; - }, - - /** - @private - - Returns the current `location.pathname`, normalized for IE inconsistencies. - - @method _getPath - */ - _getPath: function () { - var pathname = this._location.pathname; - // Various versions of IE/Opera don't always return a leading slash - if (pathname.charAt(0) !== '/') { - pathname = '/' + pathname; - } - - return pathname; - }, - - /** - @private - - Returns normalized location.hash as an alias to Ember.Location._getHash - - @method _getHash - */ - _getHash: EmberLocation._getHash, - - /** - @private - - Returns location.search - - @method _getQuery - */ - _getQuery: function () { - return this._location.search; - }, - - /** - @private - - Returns the full pathname including query and hash - - @method _getFullPath - */ - _getFullPath: function () { - return this._getPath() + this._getQuery() + this._getHash(); - }, - - /** - @private - - Returns the current path as it should appear for HistoryLocation supported - browsers. This may very well differ from the real current path (e.g. if it - starts off as a hashed URL) - - @method _getHistoryPath - */ - _getHistoryPath: function () { - var rootURL = this._getRootURL(), - path = this._getPath(), - hash = this._getHash(), - query = this._getQuery(), - rootURLIndex = path.indexOf(rootURL), - routeHash, hashParts; - - Ember.assert('Path ' + path + ' does not start with the provided rootURL ' + rootURL, rootURLIndex === 0); - - // By convention, Ember.js routes using HashLocation are required to start - // with `#/`. Anything else should NOT be considered a route and should - // be passed straight through, without transformation. - if (hash.substr(0, 2) === '#/') { - // There could be extra hash segments after the route - hashParts = hash.substr(1).split('#'); - // The first one is always the route url - routeHash = hashParts.shift(); - - // If the path already has a trailing slash, remove the one - // from the hashed route so we don't double up. - if (path.slice(-1) === '/') { - routeHash = routeHash.substr(1); - } - - // This is the "expected" final order - path += routeHash; - path += query; - - if (hashParts.length) { - path += '#' + hashParts.join('#'); - } - } else { - path += query; - path += hash; - } - - return path; - }, - - /** - @private - - Returns the current path as it should appear for HashLocation supported - browsers. This may very well differ from the real current path. - - @method _getHashPath - */ - _getHashPath: function () { - var rootURL = this._getRootURL(), - path = rootURL, - historyPath = this._getHistoryPath(), - routePath = historyPath.substr(rootURL.length); - - if (routePath !== '') { - if (routePath.charAt(0) !== '/') { - routePath = '/' + routePath; - } - - path += '#' + routePath; - } - - return path; - }, - - /** - Selects the best location option based off browser support and returns an - instance of that Location class. - - @see Ember.AutoLocation - @method create - */ - create: function (options) { - if (options && options.rootURL) { - Ember.assert('rootURL must end with a trailing forward slash e.g. "/app/"', options.rootURL.charAt(options.rootURL.length-1) === '/'); - this.rootURL = options.rootURL; - } - - var historyPath, hashPath, - cancelRouterSetup = false, - implementationClass = this._NoneLocation, - currentPath = this._getFullPath(); - - if (this._getSupportsHistory()) { - historyPath = this._getHistoryPath(); - - // Since we support history paths, let's be sure we're using them else - // switch the location over to it. - if (currentPath === historyPath) { - implementationClass = this._HistoryLocation; - } else { - cancelRouterSetup = true; - this._replacePath(historyPath); - } - - } else if (this._getSupportsHashChange()) { - hashPath = this._getHashPath(); - - // Be sure we're using a hashed path, otherwise let's switch over it to so - // we start off clean and consistent. We'll count an index path with no - // hash as "good enough" as well. - if (currentPath === hashPath || (currentPath === '/' && hashPath === '/#/')) { - implementationClass = this._HashLocation; - } else { - // Our URL isn't in the expected hash-supported format, so we want to - // cancel the router setup and replace the URL to start off clean - cancelRouterSetup = true; - this._replacePath(hashPath); - } - } - - var implementation = implementationClass.create.apply(implementationClass, arguments); - - if (cancelRouterSetup) { - set(implementation, 'cancelRouterSetup', true); - } - - return implementation; - } - }; - -})(); - - - -(function() { - -})(); - - - -(function() { -/** -Ember Routing - -@module ember -@submodule ember-routing -@requires ember-views -*/ - -})(); - -(function() { -function visit(vertex, fn, visited, path) { - var name = vertex.name, - vertices = vertex.incoming, - names = vertex.incomingNames, - len = names.length, - i; - if (!visited) { - visited = {}; - } - if (!path) { - path = []; - } - if (visited.hasOwnProperty(name)) { - return; - } - path.push(name); - visited[name] = true; - for (i = 0; i < len; i++) { - visit(vertices[names[i]], fn, visited, path); - } - fn(vertex, path); - path.pop(); -} - -function DAG() { - this.names = []; - this.vertices = {}; -} - -DAG.prototype.add = function(name) { - if (!name) { return; } - if (this.vertices.hasOwnProperty(name)) { - return this.vertices[name]; - } - var vertex = { - name: name, incoming: {}, incomingNames: [], hasOutgoing: false, value: null - }; - this.vertices[name] = vertex; - this.names.push(name); - return vertex; -}; - -DAG.prototype.map = function(name, value) { - this.add(name).value = value; -}; - -DAG.prototype.addEdge = function(fromName, toName) { - if (!fromName || !toName || fromName === toName) { - return; - } - var from = this.add(fromName), to = this.add(toName); - if (to.incoming.hasOwnProperty(fromName)) { - return; - } - function checkCycle(vertex, path) { - if (vertex.name === toName) { - throw new Ember.Error("cycle detected: " + toName + " <- " + path.join(" <- ")); - } - } - visit(from, checkCycle); - from.hasOutgoing = true; - to.incoming[fromName] = from; - to.incomingNames.push(fromName); -}; - -DAG.prototype.topsort = function(fn) { - var visited = {}, - vertices = this.vertices, - names = this.names, - len = names.length, - i, vertex; - for (i = 0; i < len; i++) { - vertex = vertices[names[i]]; - if (!vertex.hasOutgoing) { - visit(vertex, fn, 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); - } - } - } -}; - -Ember.DAG = DAG; - -})(); - - - -(function() { -/** -@module ember -@submodule ember-application -*/ - -var get = Ember.get, - classify = Ember.String.classify, - capitalize = Ember.String.capitalize, - decamelize = Ember.String.decamelize; - -Ember.Resolver = Ember.Object.extend({ - /** - This will be set to the Application instance when it is - created. - - @property namespace - */ - namespace: null, - normalize: function(fullName) { - throw new Error("Invalid call to `resolver.normalize(fullName)`. Please override the 'normalize' method in subclass of `Ember.AbstractResolver` to prevent falling through to this error."); - }, - resolve: function(fullName) { - throw new Error("Invalid call to `resolver.resolve(parsedName)`. Please override the 'resolve' method in subclass of `Ember.AbstractResolver` to prevent falling through to this error."); - }, - parseName: function(parsedName) { - throw new Error("Invalid call to `resolver.resolveByType(parsedName)`. Please override the 'resolveByType' method in subclass of `Ember.AbstractResolver` to prevent falling through to this error."); - }, - lookupDescription: function(fullName) { - throw new Error("Invalid call to `resolver.lookupDescription(fullName)`. Please override the 'lookupDescription' method in subclass of `Ember.AbstractResolver` to prevent falling through to this error."); - }, - makeToString: function(factory, fullName) { - throw new Error("Invalid call to `resolver.makeToString(factory, fullName)`. Please override the 'makeToString' method in subclass of `Ember.AbstractResolver` to prevent falling through to this error."); - }, - resolveOther: function(parsedName) { - throw new Error("Invalid call to `resolver.resolveDefault(parsedName)`. Please override the 'resolveDefault' method in subclass of `Ember.AbstractResolver` to prevent falling through to this error."); - } -}); - - - -/** - The DefaultResolver defines the default lookup rules to resolve - container lookups before consulting the container for registered - items: - - * templates are looked up on `Ember.TEMPLATES` - * other names are looked up on the application after converting - the name. For example, `controller:post` looks up - `App.PostController` by default. - * there are some nuances (see examples below) - - ### How Resolving Works - - The container calls this object's `resolve` method with the - `fullName` argument. - - It first parses the fullName into an object using `parseName`. - - Then it checks for the presence of a type-specific instance - method of the form `resolve[Type]` and calls it if it exists. - For example if it was resolving 'template:post', it would call - the `resolveTemplate` method. - - Its last resort is to call the `resolveOther` method. - - The methods of this object are designed to be easy to override - in a subclass. For example, you could enhance how a template - is resolved like so: - - ```javascript - App = Ember.Application.create({ - Resolver: Ember.DefaultResolver.extend({ - resolveTemplate: function(parsedName) { - var resolvedTemplate = this._super(parsedName); - if (resolvedTemplate) { return resolvedTemplate; } - return Ember.TEMPLATES['not_found']; - } - }) - }); - ``` - - Some examples of how names are resolved: - - ``` - 'template:post' //=> Ember.TEMPLATES['post'] - 'template:posts/byline' //=> Ember.TEMPLATES['posts/byline'] - 'template:posts.byline' //=> Ember.TEMPLATES['posts/byline'] - 'template:blogPost' //=> Ember.TEMPLATES['blogPost'] - // OR - // Ember.TEMPLATES['blog_post'] - 'controller:post' //=> App.PostController - 'controller:posts.index' //=> App.PostsIndexController - 'controller:blog/post' //=> Blog.PostController - 'controller:basic' //=> Ember.Controller - 'route:post' //=> App.PostRoute - 'route:posts.index' //=> App.PostsIndexRoute - 'route:blog/post' //=> Blog.PostRoute - 'route:basic' //=> Ember.Route - 'view:post' //=> App.PostView - 'view:posts.index' //=> App.PostsIndexView - 'view:blog/post' //=> Blog.PostView - 'view:basic' //=> Ember.View - 'foo:post' //=> App.PostFoo - 'model:post' //=> App.Post - ``` - - @class DefaultResolver - @namespace Ember - @extends Ember.Object -*/ -Ember.DefaultResolver = Ember.Object.extend({ - /** - This will be set to the Application instance when it is - created. - - @property namespace - */ - namespace: null, - - normalize: function(fullName) { - var split = fullName.split(':', 2), - type = split[0], - name = split[1]; - - Ember.assert("Tried to normalize a container name without a colon (:) in it. You probably tried to lookup a name that did not contain a type, a colon, and a name. A proper lookup name would be `view:post`.", split.length === 2); - - if (type !== 'template') { - var result = name; - - if (result.indexOf('.') > -1) { - result = result.replace(/\.(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); - } - - if (name.indexOf('_') > -1) { - result = result.replace(/_(.)/g, function(m) { return m.charAt(1).toUpperCase(); }); - } - - return type + ':' + result; - } else { - return fullName; - } - }, - - - /** - This method is called via the container's resolver method. - It parses the provided `fullName` and then looks up and - returns the appropriate template or class. - - @method resolve - @param {String} fullName the lookup string - @return {Object} the resolved factory - */ - resolve: function(fullName) { - var parsedName = this.parseName(fullName), - resolveMethodName = parsedName.resolveMethodName; - - if (!(parsedName.name && parsedName.type)) { - throw new TypeError("Invalid fullName: `" + fullName + "`, must be of the form `type:name` "); - } - - if (this[resolveMethodName]) { - var resolved = this[resolveMethodName](parsedName); - if (resolved) { return resolved; } - } - return this.resolveOther(parsedName); - }, - /** - Convert the string name of the form "type:name" to - a Javascript object with the parsed aspects of the name - broken out. - - @protected - @param {String} fullName the lookup string - @method parseName - */ - parseName: function(fullName) { - var nameParts = fullName.split(":"), - type = nameParts[0], fullNameWithoutType = nameParts[1], - name = fullNameWithoutType, - namespace = get(this, 'namespace'), - root = namespace; - - if (type !== 'template' && name.indexOf('/') !== -1) { - var parts = name.split('/'); - name = parts[parts.length - 1]; - var namespaceName = capitalize(parts.slice(0, -1).join('.')); - root = Ember.Namespace.byName(namespaceName); - - Ember.assert('You are looking for a ' + name + ' ' + type + ' in the ' + namespaceName + ' namespace, but the namespace could not be found', root); - } - - return { - fullName: fullName, - type: type, - fullNameWithoutType: fullNameWithoutType, - name: name, - root: root, - resolveMethodName: "resolve" + classify(type) - }; - }, - - /** - Returns a human-readable description for a fullName. Used by the - Application namespace in assertions to describe the - precise name of the class that Ember is looking for, rather than - container keys. - - @protected - @param {String} fullName the lookup string - @method lookupDescription - */ - lookupDescription: function(fullName) { - var parsedName = this.parseName(fullName); - - if (parsedName.type === 'template') { - return "template at " + parsedName.fullNameWithoutType.replace(/\./g, '/'); - } - - var description = parsedName.root + "." + classify(parsedName.name); - if (parsedName.type !== 'model') { description += classify(parsedName.type); } - - return description; - }, - - makeToString: function(factory, fullName) { - return factory.toString(); - }, - /** - Given a parseName object (output from `parseName`), apply - the conventions expected by `Ember.Router` - - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method useRouterNaming - */ - useRouterNaming: function(parsedName) { - parsedName.name = parsedName.name.replace(/\./g, '_'); - if (parsedName.name === 'basic') { - parsedName.name = ''; - } - }, - /** - Look up the template in Ember.TEMPLATES - - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveTemplate - */ - resolveTemplate: function(parsedName) { - var templateName = parsedName.fullNameWithoutType.replace(/\./g, '/'); - - if (Ember.TEMPLATES[templateName]) { - return Ember.TEMPLATES[templateName]; - } - - templateName = decamelize(templateName); - if (Ember.TEMPLATES[templateName]) { - return Ember.TEMPLATES[templateName]; - } - }, - /** - Lookup the view using `resolveOther` - - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveView - */ - resolveView: function(parsedName) { - this.useRouterNaming(parsedName); - return this.resolveOther(parsedName); - }, - /** - Lookup the controller using `resolveOther` - - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveController - */ - resolveController: function(parsedName) { - this.useRouterNaming(parsedName); - return this.resolveOther(parsedName); - }, - /** - Lookup the route using `resolveOther` - - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveRoute - */ - resolveRoute: function(parsedName) { - this.useRouterNaming(parsedName); - return this.resolveOther(parsedName); - }, - - /** - Lookup the model on the Application namespace - - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveModel - */ - resolveModel: function(parsedName) { - var className = classify(parsedName.name), - factory = get(parsedName.root, className); - - if (factory) { return factory; } - }, - /** - Look up the specified object (from parsedName) on the appropriate - namespace (usually on the Application) - - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveHelper - */ - resolveHelper: function(parsedName) { - return this.resolveOther(parsedName) || Ember.Handlebars.helpers[parsedName.fullNameWithoutType]; - }, - /** - Look up the specified object (from parsedName) on the appropriate - namespace (usually on the Application) - - @protected - @param {Object} parsedName a parseName object with the parsed - fullName lookup string - @method resolveOther - */ - resolveOther: function(parsedName) { - var className = classify(parsedName.name) + classify(parsedName.type), - factory = get(parsedName.root, className); - if (factory) { return factory; } - } -}); - -})(); - - - -(function() { -/** -@module ember -@submodule ember-application -*/ - -var get = Ember.get, set = Ember.set; - -function DeprecatedContainer(container) { - this._container = container; -} - -DeprecatedContainer.deprecate = function(method) { - return function() { - var container = this._container; - - Ember.deprecate('Using the defaultContainer is no longer supported. [defaultContainer#' + method + '] see: http://git.io/EKPpnA', false); - return container[method].apply(container, arguments); - }; -}; - -DeprecatedContainer.prototype = { - _container: null, - lookup: DeprecatedContainer.deprecate('lookup'), - resolve: DeprecatedContainer.deprecate('resolve'), - register: DeprecatedContainer.deprecate('register') -}; - -/** - An instance of `Ember.Application` is the starting point for every Ember - application. It helps to instantiate, initialize and coordinate the many - objects that make up your app. - - Each Ember app has one and only one `Ember.Application` object. In fact, the - very first thing you should do in your application is create the instance: - - ```javascript - window.App = Ember.Application.create(); - ``` - - Typically, the application object is the only global variable. All other - classes in your app should be properties on the `Ember.Application` instance, - which highlights its first role: a global namespace. - - For example, if you define a view class, it might look like this: - - ```javascript - App.MyView = Ember.View.extend(); - ``` - - By default, calling `Ember.Application.create()` will automatically initialize - your application by calling the `Ember.Application.initialize()` method. If - you need to delay initialization, you can call your app's `deferReadiness()` - method. When you are ready for your app to be initialized, call its - `advanceReadiness()` method. - - You can define a `ready` method on the `Ember.Application` instance, which - will be run by Ember when the application is initialized. - - Because `Ember.Application` inherits from `Ember.Namespace`, any classes - you create will have useful string representations when calling `toString()`. - See the `Ember.Namespace` documentation for more information. - - While you can think of your `Ember.Application` as a container that holds the - other classes in your application, there are several other responsibilities - going on under-the-hood that you may want to understand. - - ### Event Delegation - - Ember uses a technique called _event delegation_. This allows the framework - to set up a global, shared event listener instead of requiring each view to - do it manually. For example, instead of each view registering its own - `mousedown` listener on its associated element, Ember sets up a `mousedown` - listener on the `body`. - - If a `mousedown` event occurs, Ember will look at the target of the event and - start walking up the DOM node tree, finding corresponding views and invoking - their `mouseDown` method as it goes. - - `Ember.Application` has a number of default events that it listens for, as - well as a mapping from lowercase events to camel-cased view method names. For - example, the `keypress` event causes the `keyPress` method on the view to be - called, the `dblclick` event causes `doubleClick` to be called, and so on. - - If there is a bubbling browser event that Ember does not listen for by - default, you can specify custom events and their corresponding view method - names by setting the application's `customEvents` property: - - ```javascript - App = Ember.Application.create({ - customEvents: { - // add support for the paste event - paste: "paste" - } - }); - ``` - - By default, the application sets up these event listeners on the document - body. However, in cases where you are embedding an Ember application inside - an existing page, you may want it to set up the listeners on an element - inside the body. - - For example, if only events inside a DOM element with the ID of `ember-app` - should be delegated, set your application's `rootElement` property: - - ```javascript - window.App = Ember.Application.create({ - rootElement: '#ember-app' - }); - ``` - - The `rootElement` can be either a DOM element or a jQuery-compatible selector - string. Note that *views appended to the DOM outside the root element will - not receive events.* If you specify a custom root element, make sure you only - append views inside it! - - To learn more about the advantages of event delegation and the Ember view - layer, and a list of the event listeners that are setup by default, visit the - [Ember View Layer guide](http://emberjs.com/guides/understanding-ember/the-view-layer/#toc_event-delegation). - - ### Initializers - - Libraries on top of Ember can add initializers, like so: - - ```javascript - Ember.Application.initializer({ - name: 'api-adapter', - - initialize: function(container, application) { - application.register('api-adapter:main', ApiAdapter); - } - }); - ``` - - Initializers provide an opportunity to access the container, which - organizes the different components of an Ember application. Additionally - they provide a chance to access the instantiated application. Beyond - being used for libraries, initializers are also a great way to organize - dependency injection or setup in your own application. - - ### Routing - - In addition to creating your application's router, `Ember.Application` is - also responsible for telling the router when to start routing. Transitions - between routes can be logged with the `LOG_TRANSITIONS` flag, and more - detailed intra-transition logging can be logged with - the `LOG_TRANSITIONS_INTERNAL` flag: - - ```javascript - window.App = Ember.Application.create({ - LOG_TRANSITIONS: true, // basic logging of successful transitions - LOG_TRANSITIONS_INTERNAL: true // detailed logging of all routing steps - }); - ``` - - By default, the router will begin trying to translate the current URL into - application state once the browser emits the `DOMContentReady` event. If you - need to defer routing, you can call the application's `deferReadiness()` - method. Once routing can begin, call the `advanceReadiness()` method. - - If there is any setup required before routing begins, you can implement a - `ready()` method on your app that will be invoked immediately before routing - begins. - ``` - - @class Application - @namespace Ember - @extends Ember.Namespace -*/ - -var Application = Ember.Application = Ember.Namespace.extend(Ember.DeferredMixin, { - - /** - The root DOM element of the Application. This can be specified as an - element or a - [jQuery-compatible selector string](http://api.jquery.com/category/selectors/). - - This is the element that will be passed to the Application's, - `eventDispatcher`, which sets up the listeners for event delegation. Every - view in your application should be a child of the element you specify here. - - @property rootElement - @type DOMElement - @default 'body' - */ - rootElement: 'body', - - /** - The `Ember.EventDispatcher` responsible for delegating events to this - application's views. - - The event dispatcher is created by the application at initialization time - and sets up event listeners on the DOM element described by the - application's `rootElement` property. - - See the documentation for `Ember.EventDispatcher` for more information. - - @property eventDispatcher - @type Ember.EventDispatcher - @default null - */ - eventDispatcher: null, - - /** - The DOM events for which the event dispatcher should listen. - - By default, the application's `Ember.EventDispatcher` listens - for a set of standard DOM events, such as `mousedown` and - `keyup`, and delegates them to your application's `Ember.View` - instances. - - If you would like additional bubbling events to be delegated to your - views, set your `Ember.Application`'s `customEvents` property - to a hash containing the DOM event name as the key and the - corresponding view method name as the value. For example: - - ```javascript - App = Ember.Application.create({ - customEvents: { - // add support for the paste event - paste: "paste" - } - }); - ``` - - @property customEvents - @type Object - @default null - */ - customEvents: null, - - // Start off the number of deferrals at 1. This will be - // decremented by the Application's own `initialize` method. - _readinessDeferrals: 1, - - init: function() { - if (!this.$) { this.$ = Ember.$; } - this.__container__ = this.buildContainer(); - - this.Router = this.defaultRouter(); - - this._super(); - - this.scheduleInitialize(); - - Ember.libraries.registerCoreLibrary('Handlebars', Ember.Handlebars.VERSION); - Ember.libraries.registerCoreLibrary('jQuery', Ember.$().jquery); - - if ( Ember.LOG_VERSION ) { - Ember.LOG_VERSION = false; // we only need to see this once per Application#init - var maxNameLength = Math.max.apply(this, Ember.A(Ember.libraries).mapBy("name.length")); - - Ember.debug('-------------------------------'); - Ember.libraries.each(function(name, version) { - var spaces = new Array(maxNameLength - name.length + 1).join(" "); - Ember.debug([name, spaces, ' : ', version].join("")); - }); - Ember.debug('-------------------------------'); - } - }, - - /** - Build the container for the current application. - - Also register a default application view in case the application - itself does not. - - @private - @method buildContainer - @return {Ember.Container} the configured container - */ - buildContainer: function() { - var container = this.__container__ = Application.buildContainer(this); - - return container; - }, - - /** - If the application has not opted out of routing and has not explicitly - defined a router, supply a default router for the application author - to configure. - - This allows application developers to do: - - ```javascript - var App = Ember.Application.create(); - - App.Router.map(function() { - this.resource('posts'); - }); - ``` - - @private - @method defaultRouter - @return {Ember.Router} the default router - */ - - defaultRouter: function() { - if (this.Router === false) { return; } - var container = this.__container__; - - if (this.Router) { - container.unregister('router:main'); - container.register('router:main', this.Router); - } - - return container.lookupFactory('router:main'); - }, - - /** - Automatically initialize the application once the DOM has - become ready. - - The initialization itself is scheduled on the actions queue - which ensures that application loading finishes before - booting. - - If you are asynchronously loading code, you should call - `deferReadiness()` to defer booting, and then call - `advanceReadiness()` once all of your code has finished - loading. - - @private - @method scheduleInitialize - */ - scheduleInitialize: function() { - var self = this; - - if (!this.$ || this.$.isReady) { - Ember.run.schedule('actions', self, '_initialize'); - } else { - this.$().ready(function runInitialize() { - Ember.run(self, '_initialize'); - }); - } - }, - - /** - Use this to defer readiness until some condition is true. - - Example: - - ```javascript - App = Ember.Application.create(); - App.deferReadiness(); - - jQuery.getJSON("/auth-token", function(token) { - App.token = token; - App.advanceReadiness(); - }); - ``` - - This allows you to perform asynchronous setup logic and defer - booting your application until the setup has finished. - - However, if the setup requires a loading UI, it might be better - to use the router for this purpose. - - @method deferReadiness - */ - deferReadiness: function() { - Ember.assert("You must call deferReadiness on an instance of Ember.Application", this instanceof Ember.Application); - Ember.assert("You cannot defer readiness since the `ready()` hook has already been called.", this._readinessDeferrals > 0); - this._readinessDeferrals++; - }, - - /** - Call `advanceReadiness` after any asynchronous setup logic has completed. - Each call to `deferReadiness` must be matched by a call to `advanceReadiness` - or the application will never become ready and routing will not begin. - - @method advanceReadiness - @see {Ember.Application#deferReadiness} - */ - advanceReadiness: function() { - Ember.assert("You must call advanceReadiness on an instance of Ember.Application", this instanceof Ember.Application); - this._readinessDeferrals--; - - if (this._readinessDeferrals === 0) { - Ember.run.once(this, this.didBecomeReady); - } - }, - - /** - Registers a factory that can be used for dependency injection (with - `App.inject`) or for service lookup. Each factory is registered with - a full name including two parts: `type:name`. - - A simple example: - - ```javascript - var App = Ember.Application.create(); - App.Orange = Ember.Object.extend(); - App.register('fruit:favorite', App.Orange); - ``` - - Ember will resolve factories from the `App` namespace automatically. - For example `App.CarsController` will be discovered and returned if - an application requests `controller:cars`. - - An example of registering a controller with a non-standard name: - - ```javascript - var App = Ember.Application.create(), - Session = Ember.Controller.extend(); - - App.register('controller:session', Session); - - // The Session controller can now be treated like a normal controller, - // despite its non-standard name. - App.ApplicationController = Ember.Controller.extend({ - needs: ['session'] - }); - ``` - - Registered factories are **instantiated** by having `create` - called on them. Additionally they are **singletons**, each time - they are looked up they return the same instance. - - Some examples modifying that default behavior: - - ```javascript - var App = Ember.Application.create(); - - App.Person = Ember.Object.extend(); - App.Orange = Ember.Object.extend(); - App.Email = Ember.Object.extend(); - App.session = Ember.Object.create(); - - App.register('model:user', App.Person, {singleton: false }); - App.register('fruit:favorite', App.Orange); - App.register('communication:main', App.Email, {singleton: false}); - App.register('session', App.session, {instantiate: false}); - ``` - - @method register - @param fullName {String} type:name (e.g., 'model:user') - @param factory {Function} (e.g., App.Person) - @param options {Object} (optional) disable instantiation or singleton usage - **/ - register: function() { - var container = this.__container__; - container.register.apply(container, arguments); - }, - - /** - Define a dependency injection onto a specific factory or all factories - of a type. - - When Ember instantiates a controller, view, or other framework component - it can attach a dependency to that component. This is often used to - provide services to a set of framework components. - - An example of providing a session object to all controllers: - - ```javascript - var App = Ember.Application.create(), - Session = Ember.Object.extend({ isAuthenticated: false }); - - // A factory must be registered before it can be injected - App.register('session:main', Session); - - // Inject 'session:main' onto all factories of the type 'controller' - // with the name 'session' - App.inject('controller', 'session', 'session:main'); - - App.IndexController = Ember.Controller.extend({ - isLoggedIn: Ember.computed.alias('session.isAuthenticated') - }); - ``` - - Injections can also be performed on specific factories. - - ```javascript - App.inject(, , ) - App.inject('route', 'source', 'source:main') - App.inject('route:application', 'email', 'model:email') - ``` - - It is important to note that injections can only be performed on - classes that are instantiated by Ember itself. Instantiating a class - directly (via `create` or `new`) bypasses the dependency injection - system. - - Ember-Data instantiates its models in a unique manner, and consequently - injections onto models (or all models) will not work as expected. Injections - on models can be enabled by setting `Ember.MODEL_FACTORY_INJECTIONS` - to `true`. - - @method inject - @param factoryNameOrType {String} - @param property {String} - @param injectionName {String} - **/ - inject: function() { - var container = this.__container__; - container.injection.apply(container, arguments); - }, - - /** - Calling initialize manually is not supported. - - Please see Ember.Application#advanceReadiness and - Ember.Application#deferReadiness. - - @private - @deprecated - @method initialize - **/ - initialize: function() { - Ember.deprecate('Calling initialize manually is not supported. Please see Ember.Application#advanceReadiness and Ember.Application#deferReadiness'); - }, - - /** - Initialize the application. This happens automatically. - - Run any initializers and run the application load hook. These hooks may - choose to defer readiness. For example, an authentication hook might want - to defer readiness until the auth token has been retrieved. - - @private - @method _initialize - */ - _initialize: function() { - if (this.isDestroyed) { return; } - - // At this point, the App.Router must already be assigned - if (this.Router) { - var container = this.__container__; - container.unregister('router:main'); - container.register('router:main', this.Router); - } - - this.runInitializers(); - Ember.runLoadHooks('application', this); - - // At this point, any initializers or load hooks that would have wanted - // to defer readiness have fired. In general, advancing readiness here - // will proceed to didBecomeReady. - this.advanceReadiness(); - - return this; - }, - - /** - Reset the application. This is typically used only in tests. It cleans up - the application in the following order: - - 1. Deactivate existing routes - 2. Destroy all objects in the container - 3. Create a new application container - 4. Re-route to the existing url - - Typical Example: - - ```javascript - - var App; - - Ember.run(function() { - App = Ember.Application.create(); - }); - - module("acceptance test", { - setup: function() { - App.reset(); - } - }); - - test("first test", function() { - // App is freshly reset - }); - - test("first test", function() { - // App is again freshly reset - }); - ``` - - Advanced Example: - - Occasionally you may want to prevent the app from initializing during - setup. This could enable extra configuration, or enable asserting prior - to the app becoming ready. - - ```javascript - - var App; - - Ember.run(function() { - App = Ember.Application.create(); - }); - - module("acceptance test", { - setup: function() { - Ember.run(function() { - App.reset(); - App.deferReadiness(); - }); - } - }); - - test("first test", function() { - ok(true, 'something before app is initialized'); - - Ember.run(function() { - App.advanceReadiness(); - }); - ok(true, 'something after app is initialized'); - }); - ``` - - @method reset - **/ - reset: function() { - this._readinessDeferrals = 1; - - function handleReset() { - var router = this.__container__.lookup('router:main'); - router.reset(); - - Ember.run(this.__container__, 'destroy'); - - this.buildContainer(); - - Ember.run.schedule('actions', this, function() { - this._initialize(); - }); - } - - Ember.run.join(this, handleReset); - }, - - /** - @private - @method runInitializers - */ - runInitializers: function() { - var initializers = get(this.constructor, 'initializers'), - container = this.__container__, - graph = new Ember.DAG(), - namespace = this, - name, initializer; - - for (name in initializers) { - initializer = initializers[name]; - graph.addEdges(initializer.name, initializer.initialize, initializer.before, initializer.after); - } - - graph.topsort(function (vertex) { - var initializer = vertex.value; - Ember.assert("No application initializer named '"+vertex.name+"'", initializer); - initializer(container, namespace); - }); - }, - - /** - @private - @method didBecomeReady - */ - didBecomeReady: function() { - this.setupEventDispatcher(); - this.ready(); // user hook - this.startRouting(); - - if (!Ember.testing) { - // Eagerly name all classes that are already loaded - Ember.Namespace.processAll(); - Ember.BOOTED = true; - } - - this.resolve(this); - }, - - /** - Setup up the event dispatcher to receive events on the - application's `rootElement` with any registered - `customEvents`. - - @private - @method setupEventDispatcher - */ - setupEventDispatcher: function() { - var customEvents = get(this, 'customEvents'), - rootElement = get(this, 'rootElement'), - dispatcher = this.__container__.lookup('event_dispatcher:main'); - - set(this, 'eventDispatcher', dispatcher); - dispatcher.setup(customEvents, rootElement); - }, - - /** - trigger a new call to `route` whenever the URL changes. - If the application has a router, use it to route to the current URL, and - - @private - @method startRouting - @property router {Ember.Router} - */ - startRouting: function() { - var router = this.__container__.lookup('router:main'); - if (!router) { return; } - - router.startRouting(); - }, - - handleURL: function(url) { - var router = this.__container__.lookup('router:main'); - - router.handleURL(url); - }, - - /** - Called when the Application has become ready. - The call will be delayed until the DOM has become ready. - - @event ready - */ - ready: Ember.K, - - /** - @deprecated Use 'Resolver' instead - Set this to provide an alternate class to `Ember.DefaultResolver` - - - @property resolver - */ - resolver: null, - - /** - Set this to provide an alternate class to `Ember.DefaultResolver` - - @property resolver - */ - Resolver: null, - - willDestroy: function() { - Ember.BOOTED = false; - // Ensure deactivation of routes before objects are destroyed - this.__container__.lookup('router:main').reset(); - - this.__container__.destroy(); - }, - - initializer: function(options) { - this.constructor.initializer(options); - } -}); - -Ember.Application.reopenClass({ - initializers: {}, - initializer: function(initializer) { - // If this is the first initializer being added to a subclass, we are going to reopen the class - // to make sure we have a new `initializers` object, which extends from the parent class' using - // prototypal inheritance. Without this, attempting to add initializers to the subclass would - // pollute the parent class as well as other subclasses. - if (this.superclass.initializers !== undefined && this.superclass.initializers === this.initializers) { - this.reopenClass({ - initializers: Ember.create(this.initializers) - }); - } - - Ember.assert("The initializer '" + initializer.name + "' has already been registered", !this.initializers[initializer.name]); - Ember.assert("An initializer cannot be registered with both a before and an after", !(initializer.before && initializer.after)); - Ember.assert("An initializer cannot be registered without an initialize function", Ember.canInvoke(initializer, 'initialize')); - - this.initializers[initializer.name] = initializer; - }, - - /** - This creates a container with the default Ember naming conventions. - - It also configures the container: - - * registered views are created every time they are looked up (they are - not singletons) - * registered templates are not factories; the registered value is - returned directly. - * the router receives the application as its `namespace` property - * all controllers receive the router as their `target` and `controllers` - properties - * all controllers receive the application as their `namespace` property - * the application view receives the application controller as its - `controller` property - * the application view receives the application template as its - `defaultTemplate` property - - @private - @method buildContainer - @static - @param {Ember.Application} namespace the application to build the - container for. - @return {Ember.Container} the built container - */ - buildContainer: function(namespace) { - var container = new Ember.Container(); - - Ember.Container.defaultContainer = new DeprecatedContainer(container); - - container.set = Ember.set; - container.resolver = resolverFor(namespace); - container.normalize = container.resolver.normalize; - container.describe = container.resolver.describe; - container.makeToString = container.resolver.makeToString; - - container.optionsForType('component', { singleton: false }); - container.optionsForType('view', { singleton: false }); - container.optionsForType('template', { instantiate: false }); - container.optionsForType('helper', { instantiate: false }); - - container.register('application:main', namespace, { instantiate: false }); - - container.register('controller:basic', Ember.Controller, { instantiate: false }); - container.register('controller:object', Ember.ObjectController, { instantiate: false }); - container.register('controller:array', Ember.ArrayController, { instantiate: false }); - container.register('route:basic', Ember.Route, { instantiate: false }); - container.register('event_dispatcher:main', Ember.EventDispatcher); - - container.register('router:main', Ember.Router); - container.injection('router:main', 'namespace', 'application:main'); - - - container.register('location:auto', Ember.AutoLocation); - - - container.register('location:hash', Ember.HashLocation); - container.register('location:history', Ember.HistoryLocation); - container.register('location:none', Ember.NoneLocation); - - container.injection('controller', 'target', 'router:main'); - container.injection('controller', 'namespace', 'application:main'); - - container.injection('route', 'router', 'router:main'); - container.injection('location', 'rootURL', '-location-setting:root-url'); - - // DEBUGGING - container.register('resolver-for-debugging:main', container.resolver.__resolver__, { instantiate: false }); - container.injection('container-debug-adapter:main', 'resolver', 'resolver-for-debugging:main'); - container.injection('data-adapter:main', 'containerDebugAdapter', 'container-debug-adapter:main'); - // Custom resolver authors may want to register their own ContainerDebugAdapter with this key - container.register('container-debug-adapter:main', Ember.ContainerDebugAdapter); - - return container; - } -}); - -/** - This function defines the default lookup rules for container lookups: - - * templates are looked up on `Ember.TEMPLATES` - * other names are looked up on the application after classifying the name. - For example, `controller:post` looks up `App.PostController` by default. - * if the default lookup fails, look for registered classes on the container - - This allows the application to register default injections in the container - that could be overridden by the normal naming convention. - - @private - @method resolverFor - @param {Ember.Namespace} namespace the namespace to look for classes - @return {*} the resolved value for a given lookup -*/ -function resolverFor(namespace) { - if (namespace.get('resolver')) { - Ember.deprecate('Application.resolver is deprecated in favor of Application.Resolver', false); - } - - var ResolverClass = namespace.get('resolver') || namespace.get('Resolver') || Ember.DefaultResolver; - var resolver = ResolverClass.create({ - namespace: namespace - }); - - function resolve(fullName) { - return resolver.resolve(fullName); - } - - resolve.describe = function(fullName) { - return resolver.lookupDescription(fullName); - }; - - resolve.makeToString = function(factory, fullName) { - return resolver.makeToString(factory, fullName); - }; - - resolve.normalize = function(fullName) { - if (resolver.normalize) { - return resolver.normalize(fullName); - } else { - Ember.deprecate('The Resolver should now provide a \'normalize\' function', false); - return fullName; - } - }; - - resolve.__resolver__ = resolver; - - return resolve; -} - -Ember.runLoadHooks('Ember.Application', Ember.Application); - -})(); - - - -(function() { - -})(); - - - -(function() { -/** -@module ember -@submodule ember-application -*/ - -var get = Ember.get, set = Ember.set; - -function verifyNeedsDependencies(controller, container, needs) { - var dependency, i, l, missing = []; - - for (i=0, l=needs.length; i 1 ? 'they' : 'it') + " could not be found"); - } -} - -var defaultControllersComputedProperty = Ember.computed(function() { - var controller = this; - - return { - needs: get(controller, 'needs'), - container: get(controller, 'container'), - unknownProperty: function(controllerName) { - var needs = this.needs, - dependency, i, l; - for (i=0, l=needs.length; i 0) { - Ember.assert(' `' + Ember.inspect(this) + ' specifies `needs`, but does ' + - "not have a container. Please ensure this controller was " + - "instantiated with a container.", - this.container || Ember.meta(this, false).descs.controllers !== defaultControllersComputedProperty); - - if (this.container) { - verifyNeedsDependencies(this, this.container, needs); - } - - // if needs then initialize controllers proxy - get(this, 'controllers'); - } - - this._super.apply(this, arguments); - }, - - /** - @method controllerFor - @see {Ember.Route#controllerFor} - @deprecated Use `needs` instead - */ - controllerFor: function(controllerName) { - Ember.deprecate("Controller#controllerFor is deprecated, please use Controller#needs instead"); - return Ember.controllerFor(get(this, 'container'), controllerName); - }, - - /** - Stores the instances of other controllers available from within - this controller. Any controller listed by name in the `needs` - property will be accessible by name through this property. - - ```javascript - App.CommentsController = Ember.ArrayController.extend({ - needs: ['post'], - postTitle: function(){ - var currentPost = this.get('controllers.post'); // instance of App.PostController - return currentPost.get('title'); - }.property('controllers.post.title') - }); - ``` - - @see {Ember.ControllerMixin#needs} - @property {Object} controllers - @default null - */ - controllers: defaultControllersComputedProperty -}); - -})(); - - - -(function() { - -})(); - - - -(function() { -/** -Ember Application - -@module ember -@submodule ember-application -@requires ember-views, ember-routing -*/ - -})(); - -(function() { -/** -@module ember -@submodule ember-extension-support -*/ -/** - The `ContainerDebugAdapter` helps the container and resolver interface - with tools that debug Ember such as the - [Ember Extension](https://github.com/tildeio/ember-extension) - for Chrome and Firefox. - - This class can be extended by a custom resolver implementer - to override some of the methods with library-specific code. - - The methods likely to be overridden are: - - * `canCatalogEntriesByType` - * `catalogEntriesByType` - - The adapter will need to be registered - in the application's container as `container-debug-adapter:main` - - Example: - - ```javascript - Application.initializer({ - name: "containerDebugAdapter", - - initialize: function(container, application) { - application.register('container-debug-adapter:main', require('app/container-debug-adapter')); - } - }); - ``` - - @class ContainerDebugAdapter - @namespace Ember - @extends Ember.Object -*/ -Ember.ContainerDebugAdapter = Ember.Object.extend({ - /** - The container of the application being debugged. - This property will be injected - on creation. - - @property container - @default null - */ - container: null, - - /** - The resolver instance of the application - being debugged. This property will be injected - on creation. - - @property resolver - @default null - */ - resolver: null, - - /** - Returns true if it is possible to catalog a list of available - classes in the resolver for a given type. - - @method canCatalogEntriesByType - @param {string} type The type. e.g. "model", "controller", "route" - @return {boolean} whether a list is available for this type. - */ - canCatalogEntriesByType: function(type) { - if (type === 'model' || type === 'template') return false; - return true; - }, - - /** - Returns the available classes a given type. - - @method catalogEntriesByType - @param {string} type The type. e.g. "model", "controller", "route" - @return {Array} An array of strings. - */ - catalogEntriesByType: function(type) { - var namespaces = Ember.A(Ember.Namespace.NAMESPACES), types = Ember.A(), self = this; - var typeSuffixRegex = new RegExp(Ember.String.classify(type) + "$"); - - namespaces.forEach(function(namespace) { - if (namespace !== Ember) { - for (var key in namespace) { - if (!namespace.hasOwnProperty(key)) { continue; } - if (typeSuffixRegex.test(key)) { - var klass = namespace[key]; - if (Ember.typeOf(klass) === 'class') { - types.push(Ember.String.dasherize(key.replace(typeSuffixRegex, ''))); - } - } - } - } - }); - return types; - } -}); - - -})(); - - - -(function() { -/** -@module ember -@submodule ember-extension-support -*/ -/** - The `DataAdapter` helps a data persistence library - interface with tools that debug Ember such - as the [Ember Extension](https://github.com/tildeio/ember-extension) - for Chrome and Firefox. - - This class will be extended by a persistence library - which will override some of the methods with - library-specific code. - - The methods likely to be overridden are: - - * `getFilters` - * `detect` - * `columnsForType` - * `getRecords` - * `getRecordColumnValues` - * `getRecordKeywords` - * `getRecordFilterValues` - * `getRecordColor` - * `observeRecord` - - The adapter will need to be registered - in the application's container as `dataAdapter:main` - - Example: - - ```javascript - Application.initializer({ - name: "data-adapter", - - initialize: function(container, application) { - application.register('data-adapter:main', DS.DataAdapter); - } - }); - ``` - - @class DataAdapter - @namespace Ember - @extends Ember.Object -*/ -Ember.DataAdapter = Ember.Object.extend({ - init: function() { - this._super(); - this.releaseMethods = Ember.A(); - }, - - /** - The container of the application being debugged. - This property will be injected - on creation. - - @property container - @default null - */ - container: null, - - - /** - The container-debug-adapter which is used - to list all models. - - @property containerDebugAdapter - @default undefined - **/ - containerDebugAdapter: undefined, - - /** - Number of attributes to send - as columns. (Enough to make the record - identifiable). - - @private - @property attributeLimit - @default 3 - */ - attributeLimit: 3, - - /** - Stores all methods that clear observers. - These methods will be called on destruction. - - @private - @property releaseMethods - */ - releaseMethods: Ember.A(), - - /** - Specifies how records can be filtered. - Records returned will need to have a `filterValues` - property with a key for every name in the returned array. - - @public - @method getFilters - @return {Array} List of objects defining filters. - The object should have a `name` and `desc` property. - */ - getFilters: function() { - return Ember.A(); - }, - - /** - Fetch the model types and observe them for changes. - - @public - @method watchModelTypes - - @param {Function} typesAdded Callback to call to add types. - Takes an array of objects containing wrapped types (returned from `wrapModelType`). - - @param {Function} typesUpdated Callback to call when a type has changed. - Takes an array of objects containing wrapped types. - - @return {Function} Method to call to remove all observers - */ - watchModelTypes: function(typesAdded, typesUpdated) { - var modelTypes = this.getModelTypes(), - self = this, typesToSend, releaseMethods = Ember.A(); - - typesToSend = modelTypes.map(function(type) { - var klass = type.klass; - var wrapped = self.wrapModelType(klass, type.name); - releaseMethods.push(self.observeModelType(klass, typesUpdated)); - return wrapped; - }); - - typesAdded(typesToSend); - - var release = function() { - releaseMethods.forEach(function(fn) { fn(); }); - self.releaseMethods.removeObject(release); - }; - this.releaseMethods.pushObject(release); - return release; - }, - - _nameToClass: function(type) { - if (typeof type === 'string') { - type = this.container.lookupFactory('model:' + type); - } - return type; - }, - - /** - Fetch the records of a given type and observe them for changes. - - @public - @method watchRecords - - @param {Function} recordsAdded Callback to call to add records. - Takes an array of objects containing wrapped records. - The object should have the following properties: - columnValues: {Object} key and value of a table cell - object: {Object} the actual record object - - @param {Function} recordsUpdated Callback to call when a record has changed. - Takes an array of objects containing wrapped records. - - @param {Function} recordsRemoved Callback to call when a record has removed. - Takes the following parameters: - index: the array index where the records were removed - count: the number of records removed - - @return {Function} Method to call to remove all observers - */ - watchRecords: function(type, recordsAdded, recordsUpdated, recordsRemoved) { - var self = this, releaseMethods = Ember.A(), records = this.getRecords(type), release; - - var recordUpdated = function(updatedRecord) { - recordsUpdated([updatedRecord]); - }; - - var recordsToSend = records.map(function(record) { - releaseMethods.push(self.observeRecord(record, recordUpdated)); - return self.wrapRecord(record); - }); - - - var contentDidChange = function(array, idx, removedCount, addedCount) { - for (var i = idx; i < idx + addedCount; i++) { - var record = array.objectAt(i); - var wrapped = self.wrapRecord(record); - releaseMethods.push(self.observeRecord(record, recordUpdated)); - recordsAdded([wrapped]); - } - - if (removedCount) { - recordsRemoved(idx, removedCount); - } - }; - - var observer = { didChange: contentDidChange, willChange: Ember.K }; - records.addArrayObserver(self, observer); - - release = function() { - releaseMethods.forEach(function(fn) { fn(); }); - records.removeArrayObserver(self, observer); - self.releaseMethods.removeObject(release); - }; - - recordsAdded(recordsToSend); - - this.releaseMethods.pushObject(release); - return release; - }, - - /** - Clear all observers before destruction - @private - */ - willDestroy: function() { - this._super(); - this.releaseMethods.forEach(function(fn) { - fn(); - }); - }, - - /** - Detect whether a class is a model. - - Test that against the model class - of your persistence library - - @private - @method detect - @param {Class} klass The class to test - @return boolean Whether the class is a model class or not - */ - detect: function(klass) { - return false; - }, - - /** - Get the columns for a given model type. - - @private - @method columnsForType - @param {Class} type The model type - @return {Array} An array of columns of the following format: - name: {String} name of the column - desc: {String} Humanized description (what would show in a table column name) - */ - columnsForType: function(type) { - return Ember.A(); - }, - - /** - Adds observers to a model type class. - - @private - @method observeModelType - @param {Class} type The model type class - @param {Function} typesUpdated Called when a type is modified. - @return {Function} The function to call to remove observers - */ - - observeModelType: function(type, typesUpdated) { - var self = this, records = this.getRecords(type); - - var onChange = function() { - typesUpdated([self.wrapModelType(type)]); - }; - var observer = { - didChange: function() { - Ember.run.scheduleOnce('actions', this, onChange); - }, - willChange: Ember.K - }; - - records.addArrayObserver(this, observer); - - var release = function() { - records.removeArrayObserver(self, observer); - }; - - return release; - }, - - - /** - Wraps a given model type and observes changes to it. - - @private - @method wrapModelType - @param {Class} type A model class - @param {String} Optional name of the class - @return {Object} contains the wrapped type and the function to remove observers - Format: - type: {Object} the wrapped type - The wrapped type has the following format: - name: {String} name of the type - count: {Integer} number of records available - columns: {Columns} array of columns to describe the record - object: {Class} the actual Model type class - release: {Function} The function to remove observers - */ - wrapModelType: function(type, name) { - var release, records = this.getRecords(type), - typeToSend, self = this; - - typeToSend = { - name: name || type.toString(), - count: Ember.get(records, 'length'), - columns: this.columnsForType(type), - object: type - }; - - - return typeToSend; - }, - - - /** - Fetches all models defined in the application. - - @private - @method getModelTypes - @return {Array} Array of model types - */ - getModelTypes: function() { - var types, self = this, - containerDebugAdapter = this.get('containerDebugAdapter'); - - if (containerDebugAdapter.canCatalogEntriesByType('model')) { - types = containerDebugAdapter.catalogEntriesByType('model'); - } else { - types = this._getObjectsOnNamespaces(); - } - // New adapters return strings instead of classes - return types.map(function(name) { - return { - klass: self._nameToClass(name), - name: name - }; - }).filter(function(type) { - return self.detect(type.klass); - }); - }, - - /** - Loops over all namespaces and all objects - attached to them - - @private - @method _getObjectsOnNamespaces - @return {Array} Array of model type strings - */ - _getObjectsOnNamespaces: function() { - var namespaces = Ember.A(Ember.Namespace.NAMESPACES), types = Ember.A(); - - namespaces.forEach(function(namespace) { - for (var key in namespace) { - if (!namespace.hasOwnProperty(key)) { continue; } - var name = Ember.String.dasherize(key); - if (!(namespace instanceof Ember.Application) && namespace.toString()) { - name = namespace + '/' + name; - } - types.push(name); - } - }); - return types; - }, - - /** - Fetches all loaded records for a given type. - - @private - @method getRecords - @return {Array} An array of records. - This array will be observed for changes, - so it should update when new records are added/removed. - */ - getRecords: function(type) { - return Ember.A(); - }, - - /** - Wraps a record and observers changes to it. - - @private - @method wrapRecord - @param {Object} record The record instance. - @return {Object} The wrapped record. Format: - columnValues: {Array} - searchKeywords: {Array} - */ - wrapRecord: function(record) { - var recordToSend = { object: record }, columnValues = {}, self = this; - - recordToSend.columnValues = this.getRecordColumnValues(record); - recordToSend.searchKeywords = this.getRecordKeywords(record); - recordToSend.filterValues = this.getRecordFilterValues(record); - recordToSend.color = this.getRecordColor(record); - - return recordToSend; - }, - - /** - Gets the values for each column. - - @private - @method getRecordColumnValues - @return {Object} Keys should match column names defined - by the model type. - */ - getRecordColumnValues: function(record) { - return {}; - }, - - /** - Returns keywords to match when searching records. - - @private - @method getRecordKeywords - @return {Array} Relevant keywords for search. - */ - getRecordKeywords: function(record) { - return Ember.A(); - }, - - /** - Returns the values of filters defined by `getFilters`. - - @private - @method getRecordFilterValues - @param {Object} record The record instance - @return {Object} The filter values - */ - getRecordFilterValues: function(record) { - return {}; - }, - - /** - Each record can have a color that represents its state. - - @private - @method getRecordColor - @param {Object} record The record instance - @return {String} The record's color - Possible options: black, red, blue, green - */ - getRecordColor: function(record) { - return null; - }, - - /** - Observes all relevant properties and re-sends the wrapped record - when a change occurs. - - @private - @method observerRecord - @param {Object} record The record instance - @param {Function} recordUpdated The callback to call when a record is updated. - @return {Function} The function to call to remove all observers. - */ - observeRecord: function(record, recordUpdated) { - return function(){}; - } - -}); - - -})(); - - - -(function() { -/** -Ember Extension Support - -@module ember -@submodule ember-extension-support -@requires ember-application -*/ - -})(); - -define("container/container", - ["container/inheriting_dict","exports"], - function(__dependency1__, __exports__) { - "use strict"; - var InheritingDict = __dependency1__["default"]; - - // 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) { - 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) { - 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 oppertunity 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) { - validateFullName(fullName); - - var normalizedName = this.normalize(fullName); - var cached = this.resolveCache.get(normalizedName); - - if (cached) { return cached; } - - var resolved = this.resolver(normalizedName) || this.registry.get(normalizedName); - - this.resolveCache.set(normalizedName, resolved); - - return resolved; - }, - - /** - 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'); - twitter 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) { - 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) { - 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) { - 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) { - 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); - } - - 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); - } - - 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, l=this.children.length; i') - .css({ position: 'absolute', left: '-1000px', top: '-1000px' }) - .appendTo('body') - .on('click', handler) - .trigger('click') - .remove(); -} - -$(function() { - /* - Determine whether a checkbox checked using jQuery's "click" method will have - the correct value for its checked property. - - If we determine that the current jQuery version exhibits this behavior, - patch it to work correctly as in the commit for the actual fix: - https://github.com/jquery/jquery/commit/1fb2f92. - */ - testCheckboxClick(function() { - if (!this.checked && !$.event.special.click) { - $.event.special.click = { - // For checkbox, fire native event so checked state will be right - trigger: function() { - if ($.nodeName( this, "input" ) && this.type === "checkbox" && this.click) { - this.click(); - return false; - } - } - }; - } - }); - - // Try again to verify that the patch took effect or blow up. - testCheckboxClick(function() { - Ember.warn("clicked checkboxes should be checked! the jQuery patch didn't work", this.checked); - }); -}); - -})(); - - - -(function() { -/** - @module ember - @submodule ember-testing -*/ - -var Test = Ember.Test; - -/** - The primary purpose of this class is to create hooks that can be implemented - by an adapter for various test frameworks. - - @class Adapter - @namespace Ember.Test -*/ -Test.Adapter = Ember.Object.extend({ - /** - This callback will be called whenever an async operation is about to start. - - Override this to call your framework's methods that handle async - operations. - - @public - @method asyncStart - */ - asyncStart: Ember.K, - - /** - This callback will be called whenever an async operation has completed. - - @public - @method asyncEnd - */ - asyncEnd: Ember.K, - - /** - Override this method with your testing framework's false assertion. - This function is called whenever an exception occurs causing the testing - promise to fail. - - QUnit example: - - ```javascript - exception: function(error) { - ok(false, error); - }; - ``` - - @public - @method exception - @param {String} error The exception to be raised. - */ - exception: function(error) { - throw error; - } -}); - -/** - This class implements the methods defined by Ember.Test.Adapter for the - QUnit testing framework. - - @class QUnitAdapter - @namespace Ember.Test - @extends Ember.Test.Adapter -*/ -Test.QUnitAdapter = Test.Adapter.extend({ - asyncStart: function() { - stop(); - }, - asyncEnd: function() { - start(); - }, - exception: function(error) { - ok(false, Ember.inspect(error)); - } -}); - -})(); - - - -(function() { -/** -* @module ember -* @submodule ember-testing -*/ - -var get = Ember.get, - Test = Ember.Test, - helper = Test.registerHelper, - asyncHelper = Test.registerAsyncHelper, - countAsync = 0; - -function currentRouteName(app){ - var appController = app.__container__.lookup('controller:application'); - - return get(appController, 'currentRouteName'); -} - -function currentPath(app){ - var appController = app.__container__.lookup('controller:application'); - - return get(appController, 'currentPath'); -} - -function currentURL(app){ - var router = app.__container__.lookup('router:main'); - - return get(router, 'location').getURL(); -} - -function visit(app, url) { - var router = app.__container__.lookup('router:main'); - router.location.setURL(url); - - if (app._readinessDeferrals > 0) { - router['initialURL'] = url; - Ember.run(app, 'advanceReadiness'); - delete router['initialURL']; - } else { - Ember.run(app, app.handleURL, url); - } - - return wait(app); -} - -function click(app, selector, context) { - var $el = findWithAssert(app, selector, context); - Ember.run($el, 'mousedown'); - - if ($el.is(':input')) { - var type = $el.prop('type'); - if (type !== 'checkbox' && type !== 'radio' && type !== 'hidden') { - Ember.run($el, function(){ - // Firefox does not trigger the `focusin` event if the window - // does not have focus. If the document doesn't have focus just - // use trigger('focusin') instead. - if (!document.hasFocus || document.hasFocus()) { - this.focus(); - } else { - this.trigger('focusin'); - } - }); - } - } - - Ember.run($el, 'mouseup'); - Ember.run($el, 'click'); - - return wait(app); -} - -function triggerEvent(app, selector, context, type, options){ - if (arguments.length === 3) { - type = context; - context = null; - } - - if (typeof options === 'undefined') { - options = {}; - } - - var $el = findWithAssert(app, selector, context); - - var event = Ember.$.Event(type, options); - - Ember.run($el, 'trigger', event); - - return wait(app); -} - -function keyEvent(app, selector, context, type, keyCode) { - if (typeof keyCode === 'undefined') { - keyCode = type; - type = context; - context = null; - } - - return triggerEvent(app, selector, context, type, { keyCode: keyCode, which: keyCode }); -} - -function fillIn(app, selector, context, text) { - var $el; - if (typeof text === 'undefined') { - text = context; - context = null; - } - $el = findWithAssert(app, selector, context); - Ember.run(function() { - $el.val(text).change(); - }); - return wait(app); -} - -function findWithAssert(app, selector, context) { - var $el = find(app, selector, context); - if ($el.length === 0) { - throw new Ember.Error("Element " + selector + " not found."); - } - return $el; -} - -function find(app, selector, context) { - var $el; - context = context || get(app, 'rootElement'); - $el = app.$(selector, context); - - return $el; -} - -function andThen(app, callback) { - return wait(app, callback(app)); -} - -function wait(app, value) { - return Test.promise(function(resolve) { - // If this is the first async promise, kick off the async test - if (++countAsync === 1) { - Test.adapter.asyncStart(); - } - - // Every 10ms, poll for the async thing to have finished - var watcher = setInterval(function() { - // 1. If the router is loading, keep polling - var routerIsLoading = !!app.__container__.lookup('router:main').router.activeTransition; - if (routerIsLoading) { return; } - - // 2. If there are pending Ajax requests, keep polling - if (Test.pendingAjaxRequests) { return; } - - // 3. If there are scheduled timers or we are inside of a run loop, keep polling - if (Ember.run.hasScheduledTimers() || Ember.run.currentRunLoop) { return; } - if (Test.waiters && Test.waiters.any(function(waiter) { - var context = waiter[0]; - var callback = waiter[1]; - return !callback.call(context); - })) { return; } - // Stop polling - clearInterval(watcher); - - // If this is the last async promise, end the async test - if (--countAsync === 0) { - Test.adapter.asyncEnd(); - } - - // Synchronously resolve the promise - Ember.run(null, resolve, value); - }, 10); - }); - -} - - -/** -* Loads a route, sets up any controllers, and renders any templates associated -* with the route as though a real user had triggered the route change while -* using your app. -* -* Example: -* -* ```javascript -* visit('posts/index').then(function() { -* // assert something -* }); -* ``` -* -* @method visit -* @param {String} url the name of the route -* @return {RSVP.Promise} -*/ -asyncHelper('visit', visit); - -/** -* Clicks an element and triggers any actions triggered by the element's `click` -* event. -* -* Example: -* -* ```javascript -* click('.some-jQuery-selector').then(function() { -* // assert something -* }); -* ``` -* -* @method click -* @param {String} selector jQuery selector for finding element on the DOM -* @return {RSVP.Promise} -*/ -asyncHelper('click', click); - -/** -* Simulates a key event, e.g. `keypress`, `keydown`, `keyup` with the desired keyCode -* -* Example: -* -* ```javascript -* keyEvent('.some-jQuery-selector', 'keypress', 13).then(function() { -* // assert something -* }); -* ``` -* -* @method keyEvent -* @param {String} selector jQuery selector for finding element on the DOM -* @param {String} type the type of key event, e.g. `keypress`, `keydown`, `keyup` -* @param {Number} keyCode the keyCode of the simulated key event -* @return {RSVP.Promise} -*/ -asyncHelper('keyEvent', keyEvent); - -/** -* Fills in an input element with some text. -* -* Example: -* -* ```javascript -* fillIn('#email', 'you@example.com').then(function() { -* // assert something -* }); -* ``` -* -* @method fillIn -* @param {String} selector jQuery selector finding an input element on the DOM -* to fill text with -* @param {String} text text to place inside the input element -* @return {RSVP.Promise} -*/ -asyncHelper('fillIn', fillIn); - -/** -* Finds an element in the context of the app's container element. A simple alias -* for `app.$(selector)`. -* -* Example: -* -* ```javascript -* var $el = find('.my-selector'); -* ``` -* -* @method find -* @param {String} selector jQuery string selector for element lookup -* @return {Object} jQuery object representing the results of the query -*/ -helper('find', find); - -/** -* Like `find`, but throws an error if the element selector returns no results. -* -* Example: -* -* ```javascript -* var $el = findWithAssert('.doesnt-exist'); // throws error -* ``` -* -* @method findWithAssert -* @param {String} selector jQuery selector string for finding an element within -* the DOM -* @return {Object} jQuery object representing the results of the query -* @throws {Error} throws error if jQuery object returned has a length of 0 -*/ -helper('findWithAssert', findWithAssert); - -/** - Causes the run loop to process any pending events. This is used to ensure that - any async operations from other helpers (or your assertions) have been processed. - - This is most often used as the return value for the helper functions (see 'click', - 'fillIn','visit',etc). - - Example: - - ```javascript - Ember.Test.registerAsyncHelper('loginUser', function(app, username, password) { - visit('secured/path/here') - .fillIn('#username', username) - .fillIn('#password', username) - .click('.submit') - - return wait(); - }); - - @method wait - @param {Object} value The value to be returned. - @return {RSVP.Promise} -*/ -asyncHelper('wait', wait); -asyncHelper('andThen', andThen); - - - - /** - Returns the currently active route name. - - Example: - - ```javascript - function validateRouteName(){ - equal(currentRouteName(), 'some.path', "correct route was transitioned into."); - } - - visit('/some/path').then(validateRouteName) - ``` - - @method currentRouteName - @return {Object} The name of the currently active route. - */ - helper('currentRouteName', currentRouteName); - - /** - Returns the current path. - - Example: - - ```javascript - function validateURL(){ - equal(currentPath(), 'some.path.index', "correct path was transitioned into."); - } - - click('#some-link-id').then(validateURL); - ``` - - @method currentPath - @return {Object} The currently active path. - */ - helper('currentPath', currentPath); - - /** - Returns the current URL. - - Example: - - ```javascript - function validateURL(){ - equal(currentURL(), '/some/path', "correct URL was transitioned into."); - } - - click('#some-link-id').then(validateURL); - ``` - - @method currentURL - @return {Object} The currently active URL. - */ - helper('currentURL', currentURL); - - - - /** - Triggers the given event on the element identified by the provided selector. - - Example: - - ```javascript - triggerEvent('#some-elem-id', 'blur'); - ``` - - This is actually used internally by the `keyEvent` helper like so: - - ```javascript - triggerEvent('#some-elem-id', 'keypress', { keyCode: 13 }); - ``` - - @method triggerEvent - @param {String} selector jQuery selector for finding element on the DOM - @param {String} type The event type to be triggered. - @param {String} options The options to be passed to jQuery.Event. - @return {RSVP.Promise} - */ - asyncHelper('triggerEvent', triggerEvent); - - -})(); - - - -(function() { -/** - Ember Testing - - @module ember - @submodule ember-testing - @requires ember-application -*/ - -})(); - -(function() { -/** -Ember - -@module ember -*/ - -function throwWithMessage(msg) { - return function() { - throw new Ember.Error(msg); - }; -} - -function generateRemovedClass(className) { - var msg = " has been moved into a plugin: https://github.com/emberjs/ember-states"; - - return { - extend: throwWithMessage(className + msg), - create: throwWithMessage(className + msg) - }; -} - -Ember.StateManager = generateRemovedClass("Ember.StateManager"); - -/** - This was exported to ember-states plugin for v 1.0.0 release. See: https://github.com/emberjs/ember-states - - @class StateManager - @namespace Ember -*/ - -Ember.State = generateRemovedClass("Ember.State"); - -/** - This was exported to ember-states plugin for v 1.0.0 release. See: https://github.com/emberjs/ember-states - - @class State - @namespace Ember -*/ - -})(); - - -})(); diff --git a/ui/javascripts/libs/ember-1.5.1.min.js b/ui/javascripts/libs/ember-1.5.1.min.js new file mode 100644 index 0000000000..a9383a689e --- /dev/null +++ b/ui/javascripts/libs/ember-1.5.1.min.js @@ -0,0 +1,29 @@ +/*! + * @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.5.1 + */ +!function(){if("undefined"==typeof Ember&&(Ember={},"undefined"!=typeof window&&(window.Em=window.Ember=Em=Ember)),Ember.ENV||(Ember.ENV="undefined"!=typeof EmberENV?EmberENV:"undefined"!=typeof ENV?ENV:{}),"MANDATORY_SETTER"in Ember.ENV||(Ember.ENV.MANDATORY_SETTER=!0),Ember.assert=function(e,t){if(!t)throw new Ember.Error("Assertion Failed: "+e)},Ember.warn=function(e,t){t||(Ember.Logger.warn("WARNING: "+e),"trace"in Ember.Logger&&Ember.Logger.trace())},Ember.debug=function(e){Ember.Logger.debug("DEBUG: "+e)},Ember.deprecate=function(e,t){if(!t){if(Ember.ENV.RAISE_ON_DEPRECATION)throw new Ember.Error(e);var r;try{__fail__.fail()}catch(n){r=n}if(Ember.LOG_STACKTRACE_ON_DEPRECATION&&r.stack){var i,o="";r.arguments?(i=r.stack.replace(/^\s+at\s+/gm,"").replace(/^([^\(]+?)([\n$])/gm,"{anonymous}($1)$2").replace(/^Object.\s*\(([^\)]+)\)/gm,"{anonymous}($1)").split("\n"),i.shift()):i=r.stack.replace(/(?:\n@:0)?\s+$/m,"").replace(/^\(/gm,"{anonymous}(").split("\n"),o="\n "+i.slice(2).join("\n "),e+=o}Ember.Logger.warn("DEPRECATION: "+e)}},Ember.deprecateFunc=function(e,t){return function(){return Ember.deprecate(e),t.apply(this,arguments)}},Ember.runInDebug=function(e){e()},!Ember.testing){var e="undefined"!=typeof InstallTrigger,t=!!window.chrome&&!window.opera;"undefined"!=typeof window&&(e||t)&&window.addEventListener&&window.addEventListener("load",function(){if(document.documentElement&&document.documentElement.dataset&&!document.documentElement.dataset.emberExtension){var r;t?r="https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi":e&&(r="https://addons.mozilla.org/en-US/firefox/addon/ember-inspector/"),Ember.debug("For more advanced debugging, install the Ember Inspector from "+r)}},!1)}}(),/*! + * @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.5.1 + */ +function(){var e,t,r,n;!function(){var i={},o={};e=function(e,t,r){i[e]={deps:t,callback:r}},n=r=t=function(e){function r(t){if("."!==t.charAt(0))return t;for(var r=t.split("/"),n=e.split("/").slice(0,-1),i=0,o=r.length;o>i;i++){var a=r[i];if(".."===a)n.pop();else{if("."===a)continue;n.push(a)}}return n.join("/")}if(n._eak_seen=i,o[e])return o[e];if(o[e]={},!i[e])throw new Error("Could not find module "+e);for(var a,s=i[e],u=s.deps,l=s.callback,c=[],h=0,m=u.length;m>h;h++)c.push("exports"===u[h]?a={}:t(r(u[h])));var p=l.apply(this,c);return o[e]=a||p}}(),function(){"undefined"==typeof Ember&&(Ember={});{var e=(Ember.imports=Ember.imports||this,Ember.exports=Ember.exports||this);Ember.lookup=Ember.lookup||this}e.Em=e.Ember=Em=Ember,Ember.isNamespace=!0,Ember.toString=function(){return"Ember"},Ember.VERSION="1.5.1",Ember.ENV||(Ember.ENV="undefined"!=typeof EmberENV?EmberENV:"undefined"!=typeof ENV?ENV:{}),Ember.config=Ember.config||{},"undefined"==typeof Ember.ENV.DISABLE_RANGE_API&&(Ember.ENV.DISABLE_RANGE_API=!0),"undefined"==typeof MetamorphENV&&(e.MetamorphENV={}),MetamorphENV.DISABLE_RANGE_API=Ember.ENV.DISABLE_RANGE_API,Ember.FEATURES=Ember.ENV.FEATURES||{},Ember.FEATURES.isEnabled=function(e){var t=Ember.FEATURES[e];return Ember.ENV.ENABLE_ALL_FEATURES?!0:t===!0||t===!1||void 0===t?t:Ember.ENV.ENABLE_OPTIONAL_FEATURES?!0:!1},Ember.EXTEND_PROTOTYPES=Ember.ENV.EXTEND_PROTOTYPES,"undefined"==typeof Ember.EXTEND_PROTOTYPES&&(Ember.EXTEND_PROTOTYPES=!0),Ember.LOG_STACKTRACE_ON_DEPRECATION=Ember.ENV.LOG_STACKTRACE_ON_DEPRECATION!==!1,Ember.SHIM_ES5=Ember.ENV.SHIM_ES5===!1?!1:Ember.EXTEND_PROTOTYPES,Ember.LOG_VERSION=Ember.ENV.LOG_VERSION===!1?!1:!0,Ember.K=function(){return this},"undefined"==typeof Ember.assert&&(Ember.assert=Ember.K),"undefined"==typeof Ember.warn&&(Ember.warn=Ember.K),"undefined"==typeof Ember.debug&&(Ember.debug=Ember.K),"undefined"==typeof Ember.runInDebug&&(Ember.runInDebug=Ember.K),"undefined"==typeof Ember.deprecate&&(Ember.deprecate=Ember.K),"undefined"==typeof Ember.deprecateFunc&&(Ember.deprecateFunc=function(e,t){return t}),Ember.uuid=0,Ember.merge=function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r]);return e},Ember.isNone=function(e){return null===e||void 0===e},Ember.none=Ember.deprecateFunc("Ember.none is deprecated. Please use Ember.isNone instead.",Ember.isNone),Ember.isEmpty=function(e){return Ember.isNone(e)||0===e.length&&"function"!=typeof e||"object"==typeof e&&0===Ember.get(e,"length")},Ember.empty=Ember.deprecateFunc("Ember.empty is deprecated. Please use Ember.isEmpty instead.",Ember.isEmpty),Ember.isBlank=function(e){return Ember.isEmpty(e)||"string"==typeof e&&null===e.match(/\S/)}}(),function(){var e=Ember.platform={};if(Ember.create=Object.create,Ember.create&&2!==Ember.create({a:1},{a:{value:2}}).a&&(Ember.create=null),!Ember.create||Ember.ENV.STUB_OBJECT_CREATE){var t=function(){};Ember.create=function(e,r){if(t.prototype=e,e=new t,r){t.prototype=e;for(var n in r)t.prototype[n]=r[n].value;e=new t}return t.prototype=null,e},Ember.create.isSimulated=!0}var r,n,i=Object.defineProperty;if(i)try{i({},"a",{get:function(){}})}catch(o){i=null}i&&(r=function(){var e={};return i(e,"a",{configurable:!0,enumerable:!0,get:function(){},set:function(){}}),i(e,"a",{configurable:!0,enumerable:!0,writable:!0,value:!0}),e.a===!0}(),n=function(){try{return i(document.createElement("div"),"definePropertyOnDOM",{}),!0}catch(e){}return!1}(),r?n||(i=function(e,t,r){var n;return n="object"==typeof Node?e instanceof Node:"object"==typeof e&&"number"==typeof e.nodeType&&"string"==typeof e.nodeName,n?e[t]=r.value:Object.defineProperty(e,t,r)}):i=null),e.defineProperty=i,e.hasPropertyAccessors=!0,e.defineProperty||(e.hasPropertyAccessors=!1,e.defineProperty=function(e,t,r){r.get||(e[t]=r.value)},e.defineProperty.isSimulated=!0),Ember.ENV.MANDATORY_SETTER&&!e.hasPropertyAccessors&&(Ember.ENV.MANDATORY_SETTER=!1)}(),function(){var e=function(e){return e&&Function.prototype.toString.call(e).indexOf("[native code]")>-1},t=e(Array.prototype.map)?Array.prototype.map:function(e){if(void 0===this||null===this)throw new TypeError;var t=Object(this),r=t.length>>>0;if("function"!=typeof e)throw new TypeError;for(var n=new Array(r),i=arguments[1],o=0;r>o;o++)o in t&&(n[o]=e.call(i,t[o],o,t));return n},r=e(Array.prototype.forEach)?Array.prototype.forEach:function(e){if(void 0===this||null===this)throw new TypeError;var t=Object(this),r=t.length>>>0;if("function"!=typeof e)throw new TypeError;for(var n=arguments[1],i=0;r>i;i++)i in t&&e.call(n,t[i],i,t)},n=e(Array.prototype.indexOf)?Array.prototype.indexOf:function(e,t){null===t||void 0===t?t=0:0>t&&(t=Math.max(0,this.length+t));for(var r=t,n=this.length;n>r;r++)if(this[r]===e)return r;return-1},i=e(Array.prototype.filter)?Array.prototype.filter:function(e,t){var r,n,i=[],o=this.length;for(r=0;o>r;r++)this.hasOwnProperty(r)&&(n=this[r],e.call(t,n,r,this)&&i.push(n));return i};Ember.ArrayPolyfills={map:t,forEach:r,filter:i,indexOf:n},Ember.SHIM_ES5&&(Array.prototype.map||(Array.prototype.map=t),Array.prototype.forEach||(Array.prototype.forEach=r),Array.prototype.filter||(Array.prototype.filter=i),Array.prototype.indexOf||(Array.prototype.indexOf=n))}(),function(){var e=["description","fileName","lineNumber","message","name","number","stack"];Ember.Error=function(){var t=Error.apply(this,arguments);Error.captureStackTrace&&Error.captureStackTrace(this,Ember.Error);for(var r=0;rs;s++){if(i=t[s],o=a[i]){if(o.__ember_source__!==e){if(!r)return void 0;o=a[i]=n(o),o.__ember_source__=e}}else{if(!r)return void 0;o=a[i]={__ember_source__:e}}a=o}return o},Ember.wrap=function(e,t){function r(){var r,n=this.__nextSuper;return this.__nextSuper=t,r=e.apply(this,arguments),this.__nextSuper=n,r}return r.wrappedFunction=e,r.__ember_observes__=e.__ember_observes__,r.__ember_observesBefore__=e.__ember_observesBefore__,r.__ember_listens__=e.__ember_listens__,r},Ember.isArray=function(e){return!e||e.setInterval?!1:Array.isArray&&Array.isArray(e)?!0:Ember.Array&&Ember.Array.detect(e)?!0:void 0!==e.length&&"object"==typeof e?!0:!1},Ember.makeArray=function(e){return null===e||void 0===e?[]:Ember.isArray(e)?e:[e]},Ember.canInvoke=t,Ember.tryInvoke=function(e,r,n){return t(e,r)?e[r].apply(e,n||[]):void 0};var d=function(){var e=0;try{try{}finally{throw e++,new Error("needsFinallyFixTest")}}catch(t){}return 1!==e}();Ember.tryFinally=d?function(e,t,r){var n,i,o;r=r||this;try{n=e.call(r)}finally{try{i=t.call(r)}catch(a){o=a}}if(o)throw o;return void 0===i?n:i}:function(e,t,r){var n,i;r=r||this;try{n=e.call(r)}finally{i=t.call(r)}return void 0===i?n:i},Ember.tryCatchFinally=d?function(e,t,r,n){var i,o,a;n=n||this;try{i=e.call(n)}catch(s){i=t.call(n,s)}finally{try{o=r.call(n)}catch(u){a=u}}if(a)throw a;return void 0===o?i:o}:function(e,t,r,n){var i,o;n=n||this;try{i=e.call(n)}catch(a){i=t.call(n,a)}finally{o=r.call(n)}return void 0===o?i:o};var f={},b="Boolean Number String Function Array Date RegExp Object".split(" ");Ember.ArrayPolyfills.forEach.call(b,function(e){f["[object "+e+"]"]=e.toLowerCase()});var E=Object.prototype.toString;Ember.typeOf=function(e){var t;return t=null===e||void 0===e?String(e):f[E.call(e)]||"object","function"===t?Ember.Object&&Ember.Object.detect(e)&&(t="class"):"object"===t&&(e instanceof Error?t="error":Ember.Object&&e instanceof Ember.Object?t="instance":e instanceof Date&&(t="date")),t},Ember.inspect=function(e){var t=Ember.typeOf(e);if("array"===t)return"["+e+"]";if("object"!==t)return e+"";var r,n=[];for(var i in e)if(e.hasOwnProperty(i)){if(r=e[i],"toString"===r)continue;"function"===Ember.typeOf(r)&&(r="function() { ... }"),n.push(i+": "+r)}return"{"+n.join(", ")+"}"}}(),function(){Ember.Instrumentation={};var e=[],t={},r=function(r){for(var n,i=[],o=0,a=e.length;a>o;o++)n=e[o],n.regex.test(r)&&i.push(n.object);return t[r]=i,i},n=function(){var e="undefined"!=typeof window?window.performance||{}:{},t=e.now||e.mozNow||e.webkitNow||e.msNow||e.oNow;return t?t.bind(e):function(){return+new Date}}();Ember.Instrumentation.instrument=function(e,i,o,a){function s(){for(d=0,f=m.length;f>d;d++)p=m[d],b[d]=p.before(e,n(),i);return o.call(a)}function u(e){i=i||{},i.exception=e}function l(){for(d=0,f=m.length;f>d;d++)p=m[d],p.after(e,n(),i,b[d]);Ember.STRUCTURED_PROFILE&&console.timeEnd(c)}var c,h,m=t[e];if(Ember.STRUCTURED_PROFILE&&(c=e+": "+i.object,console.time(c)),m||(m=r(e)),0===m.length)return h=o.call(a),Ember.STRUCTURED_PROFILE&&console.timeEnd(c),h;var p,d,f,b=[];return Ember.tryCatchFinally(s,u,l)},Ember.Instrumentation.subscribe=function(r,n){for(var i,o=r.split("."),a=[],s=0,u=o.length;u>s;s++)i=o[s],a.push("*"===i?"[^\\.]*":i);a=a.join("\\."),a+="(\\..*)?";var l={pattern:r,regex:new RegExp("^"+a+"$"),object:n};return e.push(l),t={},l},Ember.Instrumentation.unsubscribe=function(r){for(var n,i=0,o=e.length;o>i;i++)e[i]===r&&(n=i);e.splice(n,1),t={}},Ember.Instrumentation.reset=function(){e=[],t={}},Ember.instrument=Ember.Instrumentation.instrument,Ember.subscribe=Ember.Instrumentation.subscribe}(),function(){var e,t,r,n,i;e=Array.prototype.map||Ember.ArrayPolyfills.map,t=Array.prototype.forEach||Ember.ArrayPolyfills.forEach,r=Array.prototype.indexOf||Ember.ArrayPolyfills.indexOf,i=Array.prototype.filter||Ember.ArrayPolyfills.filter,n=Array.prototype.splice;var o=Ember.EnumerableUtils={map:function(t,r,n){return t.map?t.map.call(t,r,n):e.call(t,r,n)},forEach:function(e,r,n){return e.forEach?e.forEach.call(e,r,n):t.call(e,r,n)},filter:function(e,t,r){return e.filter?e.filter.call(e,t,r):i.call(e,t,r)},indexOf:function(e,t,n){return e.indexOf?e.indexOf.call(e,t,n):r.call(e,t,n)},indexesOf:function(e,t){return void 0===t?[]:o.map(t,function(t){return o.indexOf(e,t)})},addObject:function(e,t){var r=o.indexOf(e,t);-1===r&&e.push(t)},removeObject:function(e,t){var r=o.indexOf(e,t);-1!==r&&e.splice(r,1)},_replace:function(e,t,r,i){for(var o,a,s=[].concat(i),u=[],l=6e4,c=t,h=r;s.length;)a=h>l?l:h,0>=a&&(a=0),o=s.splice(0,l),o=[c,a].concat(o),c+=l,h-=a,u=u.concat(n.apply(e,o));return u},replace:function(e,t,r,n){return e.replace?e.replace(t,r,n):o._replace(e,t,r,n)},intersection:function(e,t){var r=[];return o.forEach(e,function(e){o.indexOf(t,e)>=0&&r.push(e)}),r}}}(),function(){var e,t=Ember.META_KEY,r=Ember.ENV.MANDATORY_SETTER,n=/^([A-Z$]|([0-9][A-Z$])).*[\.\*]/,i=/^this[\.\*]/,o=/^([^\.\*]+)/;e=function(e,n){if(""===n)return e;if(n||"string"!=typeof e||(n=e,e=null),Ember.assert("Cannot call get with "+n+" key.",!!n),Ember.assert("Cannot call get with '"+n+"' on an undefined object.",void 0!==e),null===e||-1!==n.indexOf("."))return s(e,n);var i,o=e[t],a=o&&o.descs[n];return a?a.get(e,n):(i=r&&o&&o.watching[n]>0?o.values[n]:e[n],void 0!==i||"object"!=typeof e||n in e||"function"!=typeof e.unknownProperty?i:e.unknownProperty(n))},Ember.config.overrideAccessors&&(Ember.get=e,Ember.config.overrideAccessors(),e=Ember.get);var a=Ember.normalizeTuple=function(t,r){var a,s=i.test(r),u=!s&&n.test(r);if((!t||u)&&(t=Ember.lookup),s&&(r=r.slice(5)),t===Ember.lookup&&(a=r.match(o)[0],t=e(t,a),r=r.slice(a.length+1)),!r||0===r.length)throw new Ember.Error("Path cannot be empty");return[t,r]},s=Ember._getPath=function(t,r){var n,o,s,u,l;if(null===t&&-1===r.indexOf("."))return e(Ember.lookup,r);for(n=i.test(r),(!t||n)&&(s=a(t,r),t=s[0],r=s[1],s.length=0),o=r.split("."),l=o.length,u=0;null!=t&&l>u;u++)if(t=e(t,o[u],!0),t&&t.isDestroyed)return void 0;return t};Ember.getWithDefault=function(t,r,n){var i=e(t,r);return void 0===i?n:i},Ember.get=e}(),function(){function e(e,t,r){for(var n=-1,i=e.length-3;i>=0;i-=3)if(t===e[i]&&r===e[i+1]){n=i;break}return n}function t(e,t){var r,n=p(e,!0);return n.listeners||(n.listeners={}),n.hasOwnProperty("listeners")||(n.listeners=m(n.listeners)),r=n.listeners[t],r&&!n.listeners.hasOwnProperty(t)?r=n.listeners[t]=n.listeners[t].slice():r||(r=n.listeners[t]=[]),r}function r(t,r,n){var i=t[d],o=i&&i.listeners&&i.listeners[r];if(o)for(var a=o.length-3;a>=0;a-=3){var s=o[a],u=o[a+1],l=o[a+2],c=e(n,s,u);-1===c&&n.push(s,u,l)}}function n(t,r,n){var i=t[d],o=i&&i.listeners&&i.listeners[r],a=[];if(o){for(var s=o.length-3;s>=0;s-=3){var u=o[s],l=o[s+1],c=o[s+2],h=e(n,u,l);-1===h&&(n.push(u,l,c),a.push(u,l,c))}return a}}function i(r,n,i,o,a){Ember.assert("You must pass at least an object and event name to Ember.addListener",!!r&&!!n),o||"function"!=typeof i||(o=i,i=null);var s=t(r,n),u=e(s,i,o),l=0;a&&(l|=b),-1===u&&(s.push(i,o,l),"function"==typeof r.didAddListener&&r.didAddListener(n,i,o))}function o(r,n,i,o){function a(i,o){var a=t(r,n),s=e(a,i,o);-1!==s&&(a.splice(s,3),"function"==typeof r.didRemoveListener&&r.didRemoveListener(n,i,o))}if(Ember.assert("You must pass at least an object and event name to Ember.removeListener",!!r&&!!n),o||"function"!=typeof i||(o=i,i=null),o)a(i,o);else{var s=r[d],u=s&&s.listeners&&s.listeners[n];if(!u)return;for(var l=u.length-3;l>=0;l-=3)a(u[l],u[l+1])}}function a(r,n,i,o,a){function s(){return a.call(i)}function u(){-1!==c&&(l[c+2]&=~E)}o||"function"!=typeof i||(o=i,i=null);var l=t(r,n),c=e(l,i,o);return-1!==c&&(l[c+2]|=E),Ember.tryFinally(s,u)}function s(r,n,i,o,a){function s(){return a.call(i)}function u(){for(var e=0,t=p.length;t>e;e++){var r=p[e];d[e][r+2]&=~E}}o||"function"!=typeof i||(o=i,i=null);var l,c,h,m,p=[],d=[];for(h=0,m=n.length;m>h;h++){l=n[h],c=t(r,l);var f=e(c,i,o);-1!==f&&(c[f+2]|=E,p.push(f),d.push(c))}return Ember.tryFinally(s,u)}function u(e){var t=e[d].listeners,r=[];if(t)for(var n in t)t[n]&&r.push(n);return r}function l(e,t,r,n){if(e!==Ember&&"function"==typeof e.sendEvent&&e.sendEvent(t,r),!n){var i=e[d];n=i&&i.listeners&&i.listeners[t]}if(n){for(var a=n.length-3;a>=0;a-=3){var s=n[a],u=n[a+1],l=n[a+2];u&&(l&E||(l&b&&o(e,t,s,u),s||(s=e),"string"==typeof u&&(u=s[u]),r?u.apply(s,r):u.call(s)))}return!0}}function c(e,t){var r=e[d],n=r&&r.listeners&&r.listeners[t];return!(!n||!n.length)}function h(e,t){var r=[],n=e[d],i=n&&n.listeners&&n.listeners[t];if(!i)return r;for(var o=0,a=i.length;a>o;o+=3){var s=i[o],u=i[o+1];r.push([s,u])}return r}var m=Ember.create,p=Ember.meta,d=Ember.META_KEY,f=[].slice,b=1,E=2;Ember.on=function(){var e=f.call(arguments,-1)[0],t=f.call(arguments,0,-1);return e.__ember_listens__=t,e},Ember.addListener=i,Ember.removeListener=o,Ember._suspendListener=a,Ember._suspendListeners=s,Ember.sendEvent=l,Ember.hasListeners=c,Ember.watchedEvents=u,Ember.listenersFor=h,Ember.listenersDiff=n,Ember.listenersUnion=r}(),function(){var e=Ember.guidFor,t=Ember.sendEvent,r=Ember._ObserverSet=function(){this.clear()};r.prototype.add=function(t,r,n){var i,o=this.observerSet,a=this.observers,s=e(t),u=o[s];return u||(o[s]=u={}),i=u[r],void 0===i&&(i=a.push({sender:t,keyName:r,eventName:n,listeners:[]})-1,u[r]=i),a[i].listeners},r.prototype.flush=function(){var e,r,n,i,o=this.observers;for(this.clear(),e=0,r=o.length;r>e;++e)n=o[e],i=n.sender,i.isDestroying||i.isDestroyed||t(i,n.eventName,[i,n.keyName],n.listeners)},r.prototype.clear=function(){this.observerSet={},this.observers=[]}}(),function(){function e(e,t){var n=e[h],i=n&&n.watching[t]>0||"length"===t,a=n&&n.proto,s=n&&n.descs[t];i&&a!==e&&(s&&s.willChange&&s.willChange(e,t),r(e,t,n),o(e,t,n),l(e,t))}function t(e,t){var r=e[h],i=r&&r.watching[t]>0||"length"===t,o=r&&r.proto,s=r&&r.descs[t];o!==e&&(s&&s.didChange&&s.didChange(e,t),(i||"length"===t)&&(n(e,t,r),a(e,t,r,!1),c(e,t)))}function r(t,r,n){if(!t.isDestroying){var o=w,a=!o;a&&(o=w={}),i(e,t,r,o,n),a&&(w=null)}}function n(e,r,n){if(!e.isDestroying){var o=_,a=!o;a&&(o=_={}),i(t,e,r,o,n),a&&(_=null)}}function i(e,t,r,n,i){var o=m(t);if(n[o]||(n[o]={}),!n[o][r]){n[o][r]=!0;var a=i.deps;if(a=a&&a[r])for(var s in a){var u=i.descs[s];u&&u._suspended===t||e(t,s)}}}function o(t,r,n){if(n.hasOwnProperty("chainWatchers")&&n.chainWatchers[r]){var i,o,a=n.chainWatchers[r],s=[];for(i=0,o=a.length;o>i;i++)a[i].willChange(s);for(i=0,o=s.length;o>i;i+=2)e(s[i],s[i+1])}}function a(e,r,n,i){if(n&&n.hasOwnProperty("chainWatchers")&&n.chainWatchers[r]){var o,a,s=n.chainWatchers[r],u=i?null:[];for(o=0,a=s.length;a>o;o++)s[o].didChange(u);if(!i)for(o=0,a=u.length;a>o;o+=2)t(u[o],u[o+1])}}function s(){y++}function u(){y--,0>=y&&(v.clear(),g.flush())}function l(e,t){if(!e.isDestroying){var r,n,i=t+":before";y?(r=v.add(e,t,i),n=b(e,i,r),d(e,i,[e,t],n)):d(e,i,[e,t])}}function c(e,t){if(!e.isDestroying){var r,n=t+":change";y?(r=g.add(e,t,n),f(e,n,r)):d(e,n,[e,t])}}var h=Ember.META_KEY,m=Ember.guidFor,p=Ember.tryFinally,d=Ember.sendEvent,f=Ember.listenersUnion,b=Ember.listenersDiff,E=Ember._ObserverSet,v=new E,g=new E,y=0;Ember.propertyWillChange=e,Ember.propertyDidChange=t;var w,_;Ember.overrideChains=function(e,t,r){a(e,t,r,!0)},Ember.beginPropertyChanges=s,Ember.endPropertyChanges=u,Ember.changeProperties=function(e,t){s(),p(e,u,t)}}(),function(){function e(e,t,r,n){var a;if(a=t.slice(t.lastIndexOf(".")+1),t=t===a?a:t.slice(0,t.length-(a.length+1)),"this"!==t&&(e=i(e,t)),!a||0===a.length)throw new Ember.Error("Property set failed: You passed an empty path");if(!e){if(n)return;throw new Ember.Error('Property set failed: object in path "'+t+'" could not be found or was destroyed.')}return o(e,a,r)}var t=Ember.META_KEY,r=Ember.ENV.MANDATORY_SETTER,n=/^([A-Z$]|([0-9][A-Z$]))/,i=Ember._getPath,o=function(i,o,a,s){if("string"==typeof i&&(Ember.assert("Path '"+i+"' must be global if no obj is given.",n.test(i)),a=o,o=i,i=null),Ember.assert("Cannot call set with "+o+" key.",!!o),!i||-1!==o.indexOf("."))return e(i,o,a,s);Ember.assert("You need to provide an object and key to `set`.",!!i&&void 0!==o),Ember.assert("calling set on destroyed object",!i.isDestroyed);var u,l,c=i[t],h=c&&c.descs[o];return h?h.set(i,o,a):(u="object"==typeof i&&!(o in i),u&&"function"==typeof i.setUnknownProperty?i.setUnknownProperty(o,a):c&&c.watching[o]>0?(l=r?c.values[o]:i[o],a!==l&&(Ember.propertyWillChange(i,o),r?(void 0!==l||o in i)&&i.propertyIsEnumerable(o)?c.values[o]=a:Ember.defineProperty(i,o,null,a):i[o]=a,Ember.propertyDidChange(i,o))):i[o]=a),a};Ember.config.overrideAccessors&&(Ember.set=o,Ember.config.overrideAccessors(),o=Ember.set),Ember.set=o,Ember.trySet=function(e,t,r){return o(e,t,r,!0)}}(),function(){var e=Ember.set,t=Ember.guidFor,r=Ember.ArrayPolyfills.indexOf,n=function(e){var t={};for(var r in e)e.hasOwnProperty(r)&&(t[r]=e[r]);return t},i=function(e,t){var r=e.keys.copy(),i=n(e.values);return t.keys=r,t.values=i,t.length=e.length,t},o=Ember.OrderedSet=function(){this.clear()};o.create=function(){return new o},o.prototype={clear:function(){this.presenceSet={},this.list=[]},add:function(e){var r=t(e),n=this.presenceSet,i=this.list;r in n||(n[r]=!0,i.push(e))},remove:function(e){var n=t(e),i=this.presenceSet,o=this.list;delete i[n];var a=r.call(o,e);a>-1&&o.splice(a,1)},isEmpty:function(){return 0===this.list.length},has:function(e){var r=t(e),n=this.presenceSet;return r in n},forEach:function(e,t){for(var r=this.toArray(),n=0,i=r.length;i>n;n++)e.call(t,r[n])},toArray:function(){return this.list.slice()},copy:function(){var e=new o;return e.presenceSet=n(this.presenceSet),e.list=this.toArray(),e}};var a=Ember.Map=function(){this.keys=Ember.OrderedSet.create(),this.values={}};a.create=function(){return new a},a.prototype={length:0,get:function(e){var r=this.values,n=t(e);return r[n]},set:function(r,n){var i=this.keys,o=this.values,a=t(r);i.add(r),o[a]=n,e(this,"length",i.list.length)},remove:function(r){var n=this.keys,i=this.values,o=t(r);return i.hasOwnProperty(o)?(n.remove(r),delete i[o],e(this,"length",n.list.length),!0):!1},has:function(e){var r=this.values,n=t(e);return r.hasOwnProperty(n)},forEach:function(e,r){var n=this.keys,i=this.values;n.forEach(function(n){var o=t(n);e.call(r,n,i[o])})},copy:function(){return i(this,new a)}};var s=Ember.MapWithDefault=function(e){a.call(this),this.defaultValue=e.defaultValue};s.create=function(e){return e?new s(e):new a},s.prototype=Ember.create(a.prototype),s.prototype.get=function(e){var t=this.has(e);if(t)return a.prototype.get.call(this,e);var r=this.defaultValue(e);return this.set(e,r),r},s.prototype.copy=function(){return i(this,new s({defaultValue:this.defaultValue}))}}(),function(){function e(e){var t,r;Ember.imports.console?t=Ember.imports.console:"undefined"!=typeof console&&(t=console);var n="object"==typeof t?t[e]:null;return n?"function"==typeof n.apply?(r=function(){n.apply(t,arguments)},r.displayName="console."+e,r):function(){var e=Array.prototype.join.call(arguments,", ");n(e)}:void 0}function t(e,t){if(!e)try{throw new Ember.Error("assertion failed: "+t)}catch(r){setTimeout(function(){throw r},0)}}Ember.Logger={log:e("log")||Ember.K,warn:e("warn")||Ember.K,error:e("error")||Ember.K,info:e("info")||Ember.K,debug:e("debug")||e("info")||Ember.K,assert:e("assert")||t}}(),function(){var e=Ember.META_KEY,t=Ember.meta,r=Ember.platform.defineProperty,n=Ember.ENV.MANDATORY_SETTER;Ember.Descriptor=function(){};var i=Ember.MANDATORY_SETTER_FUNCTION=function(){Ember.assert("You must use Ember.set() to access this property (of "+this+")",!1)},o=Ember.DEFAULT_GETTER_FUNCTION=function(t){return function(){var r=this[e];return r&&r.values[t]}};if(Ember.defineProperty=function(e,s,u,l,c){var h,m,p,d;return c||(c=t(e)),h=c.descs,m=c.descs[s],p=c.watching[s]>0,m instanceof Ember.Descriptor&&m.teardown(e,s),u instanceof Ember.Descriptor?(d=u,h[s]=u,n&&p?r(e,s,{configurable:!0,enumerable:!0,writable:!0,value:void 0}):e[s]=void 0,Ember.FEATURES.isEnabled("composable-computed-properties")&&u.func&&u._dependentCPs&&a(e,u._dependentCPs,c)):(h[s]=void 0,null==u?(d=l,n&&p?(c.values[s]=l,r(e,s,{configurable:!0,enumerable:!0,set:i,get:o(s)})):e[s]=l):(d=u,r(e,s,u))),p&&Ember.overrideChains(e,s,c),e.didDefineProperty&&e.didDefineProperty(e,s,d),this},Ember.FEATURES.isEnabled("composable-computed-properties"))var a=function(e,t,r){for(var n,i,o=t.length,s=0;o>s;++s)n=t[s],i=n.implicitCPKey,Ember.defineProperty(e,i,n,void 0,r),n._dependentCPs&&a(e,n._dependentCPs,r)}}(),function(){var e=Ember.get;Ember.getProperties=function(t){var r={},n=arguments,i=1;2===arguments.length&&"array"===Ember.typeOf(arguments[1])&&(i=0,n=arguments[1]);for(var o=n.length;o>i;i++)r[n[i]]=e(t,n[i]);return r}}(),function(){var e=Ember.changeProperties,t=Ember.set;Ember.setProperties=function(r,n){return e(function(){for(var e in n)n.hasOwnProperty(e)&&t(r,e,n[e])}),r}}(),function(){var e=Ember.meta,t=Ember.typeOf,r=Ember.ENV.MANDATORY_SETTER,n=Ember.platform.defineProperty;Ember.watchKey=function(i,o,a){if("length"!==o||"array"!==t(i)){var s=a||e(i),u=s.watching;u[o]?u[o]=(u[o]||0)+1:(u[o]=1,"function"==typeof i.willWatchProperty&&i.willWatchProperty(o),r&&o in i&&(s.values[o]=i[o],n(i,o,{configurable:!0,enumerable:i.propertyIsEnumerable(o),set:Ember.MANDATORY_SETTER_FUNCTION,get:Ember.DEFAULT_GETTER_FUNCTION(o)})))}},Ember.unwatchKey=function(t,i,o){var a=o||e(t),s=a.watching;1===s[i]?(s[i]=0,"function"==typeof t.didUnwatchProperty&&t.didUnwatchProperty(i),r&&i in t&&n(t,i,{configurable:!0,enumerable:t.propertyIsEnumerable(i),set:function(e){n(t,i,{configurable:!0,writable:!0,enumerable:!0,value:e}),delete a.values[i]},get:Ember.DEFAULT_GETTER_FUNCTION(i)})):s[i]>1&&s[i]--}}(),function(){function e(e){return e.match(c)[0]}function t(e,t,r){if(e&&"object"==typeof e){var i=n(e),o=i.chainWatchers;i.hasOwnProperty("chainWatchers")||(o=i.chainWatchers={}),o[t]||(o[t]=[]),o[t].push(r),u(e,t,i)}}function r(e,t){if(!e)return void 0;var r=e[h];if(r&&r.proto===e)return void 0;if("@each"===t)return i(e,t);var n=r&&r.descs[t];return n&&n._cacheable?t in r.cache?r.cache[t]:void 0:i(e,t)}var n=Ember.meta,i=Ember.get,o=Ember.normalizeTuple,a=Ember.ArrayPolyfills.forEach,s=Ember.warn,u=Ember.watchKey,l=Ember.unwatchKey,c=/^([^\.\*]+)/,h=Ember.META_KEY,m=[];Ember.flushPendingChains=function(){if(0!==m.length){var e=m;m=[],a.call(e,function(e){e[0].add(e[1])}),s("Watching an undefined global, Ember expects watched globals to be setup by the time the run loop is flushed, check for typos",0===m.length)}};var p=Ember.removeChainWatcher=function(e,t,r){if(e&&"object"==typeof e){var n=e[h];if(!n||n.hasOwnProperty("chainWatchers")){var i=n&&n.chainWatchers;if(i&&i[t]){i=i[t];for(var o=0,a=i.length;a>o;o++)i[o]===r&&i.splice(o,1)}l(e,t,n)}}},d=Ember._ChainNode=function(e,r,n){this._parent=e,this._key=r,this._watching=void 0===n,this._value=n,this._paths={},this._watching&&(this._object=e.value(),this._object&&t(this._object,this._key,this)),this._parent&&"@each"===this._parent._key&&this.value()},f=d.prototype;f.value=function(){if(void 0===this._value&&this._watching){var e=this._parent.value();this._value=r(e,this._key)}return this._value},f.destroy=function(){if(this._watching){var e=this._object;e&&p(e,this._key,this),this._watching=!1}},f.copy=function(e){var t,r=new d(null,null,e),n=this._paths;for(t in n)n[t]<=0||r.add(t);return r},f.add=function(t){var r,n,i,a,s;if(s=this._paths,s[t]=(s[t]||0)+1,r=this.value(),n=o(r,t),n[0]&&n[0]===r)t=n[1],i=e(t),t=t.slice(i.length+1);else{if(!n[0])return m.push([this,t]),void(n.length=0);a=n[0],i=t.slice(0,0-(n[1].length+1)),t=n[1]}n.length=0,this.chain(i,t,a)},f.remove=function(t){var r,n,i,a,s;s=this._paths,s[t]>0&&s[t]--,r=this.value(),n=o(r,t),n[0]===r?(t=n[1],i=e(t),t=t.slice(i.length+1)):(a=n[0],i=t.slice(0,0-(n[1].length+1)),t=n[1]),n.length=0,this.unchain(i,t)},f.count=0,f.chain=function(t,r,n){var i,o=this._chains;o||(o=this._chains={}),i=o[t],i||(i=o[t]=new d(this,t,n)),i.count++,r&&r.length>0&&(t=e(r),r=r.slice(t.length+1),i.chain(t,r))},f.unchain=function(t,r){var n=this._chains,i=n[t];r&&r.length>1&&(t=e(r),r=r.slice(t.length+1),i.unchain(t,r)),i.count--,i.count<=0&&(delete n[i._key],i.destroy())},f.willChange=function(e){var t=this._chains;if(t)for(var r in t)t.hasOwnProperty(r)&&t[r].willChange(e);this._parent&&this._parent.chainWillChange(this,this._key,1,e)},f.chainWillChange=function(e,t,r,n){this._key&&(t=this._key+"."+t),this._parent?this._parent.chainWillChange(this,t,r+1,n):(r>1&&n.push(this.value(),t),t="this."+t,this._paths[t]>0&&n.push(this.value(),t))},f.chainDidChange=function(e,t,r,n){this._key&&(t=this._key+"."+t),this._parent?this._parent.chainDidChange(this,t,r+1,n):(r>1&&n.push(this.value(),t),t="this."+t,this._paths[t]>0&&n.push(this.value(),t))},f.didChange=function(e){if(this._watching){var r=this._parent.value();r!==this._object&&(p(this._object,this._key,this),this._object=r,t(r,this._key,this)),this._value=void 0,this._parent&&"@each"===this._parent._key&&this.value()}var n=this._chains;if(n)for(var i in n)n.hasOwnProperty(i)&&n[i].didChange(e);null!==e&&this._parent&&this._parent.chainDidChange(this,this._key,1,e)},Ember.finishChains=function(e){var t=e[h],r=t&&t.chains;r&&(r.value()!==e?n(e).chains=r=r.copy(e):r.didChange(null))}}(),function(){var e=Ember.EnumerableUtils.forEach,t=/^((?:[^\.]*\.)*)\{(.*)\}$/;Ember.expandProperties=function(r,n){var i,o,a;(i=t.exec(r))?(o=i[1],a=i[2],e(a.split(","),function(e){n(o+e)})):n(r)}}(),function(){function e(e,r){var i=r||t(e),o=i.chains;return o?o.value()!==e&&(o=i.chains=o.copy(e)):o=i.chains=new n(null,null,e),o}var t=Ember.meta,r=Ember.typeOf,n=Ember._ChainNode;Ember.watchPath=function(n,i,o){if("length"!==i||"array"!==r(n)){var a=o||t(n),s=a.watching;s[i]?s[i]=(s[i]||0)+1:(s[i]=1,e(n,a).add(i))}},Ember.unwatchPath=function(r,n,i){var o=i||t(r),a=o.watching;1===a[n]?(a[n]=0,e(r,o).remove(n)):a[n]>1&&a[n]--}}(),function(){function e(e){return"*"===e||!c.test(e)}var t=(Ember.meta,Ember.GUID_KEY),r=Ember.META_KEY,n=Ember.removeChainWatcher,i=Ember.watchKey,o=Ember.unwatchKey,a=Ember.watchPath,s=Ember.unwatchPath,u=Ember.typeOf,l=Ember.generateGuid,c=/[\.\*]/;Ember.watch=function(t,r,n){("length"!==r||"array"!==u(t))&&(e(r)?i(t,r,n):a(t,r,n))},Ember.isWatching=function(e,t){var n=e[r];return(n&&n.watching[t])>0},Ember.watch.flushPending=Ember.flushPendingChains,Ember.unwatch=function(t,r,n){("length"!==r||"array"!==u(t))&&(e(r)?o(t,r,n):s(t,r,n))},Ember.rewatch=function(e){var n=e[r],i=n&&n.chains;t in e&&!e.hasOwnProperty(t)&&l(e),i&&i.value()!==e&&(n.chains=i.copy(e))};var h=[];Ember.destroy=function(e){var t,i,o,a,s=e[r];if(s&&(e[r]=null,t=s.chains))for(h.push(t);h.length>0;){if(t=h.pop(),i=t._chains)for(o in i)i.hasOwnProperty(o)&&h.push(i[o]);t._watching&&(a=t._object,a&&n(a,t._key,t))}}}(),function(){function e(e,t){var r=e[t];return r?e.hasOwnProperty(t)||(r=e[t]=h(r)):r=e[t]={},r}function t(t){return e(t,"deps")}function r(r,n,i,o){var a,s,u,l,c,h=r._dependentKeys;if(h)for(a=t(o),s=0,u=h.length;u>s;s++)l=h[s],c=e(a,l),c[i]=(c[i]||0)+1,p(n,l,o)}function n(r,n,i,o){var a,s,u,l,c,h=r._dependentKeys;if(h)for(a=t(o),s=0,u=h.length;u>s;s++)l=h[s],c=e(a,l),c[i]=(c[i]||0)-1,d(n,l,o)}function i(e,t){this.func=e,Ember.FEATURES.isEnabled("composable-computed-properties")?P(this,t&&t.dependentKeys):this._dependentKeys=t&&t.dependentKeys,this._cacheable=t&&void 0!==t.cacheable?t.cacheable:!0,this._readOnly=t&&(void 0!==t.readOnly||!!t.readOnly)}function o(e){for(var t=0,r=e.length;r>t;t++)e[t].didChange(null)}function a(e,t){for(var r={},n=0;nr;r++)f(arguments[r],t);return Ember.FEATURES.isEnabled("composable-computed-properties")?P(this,e):this._dependentKeys=e,this},b.meta=function(e){return 0===arguments.length?this._meta||{}:(this._meta=e,this)},b.didChange=function(e,t){if(this._cacheable&&this._suspended!==e){var r=l(e);t in r.cache&&(delete r.cache[t],n(this,e,t,r))}},b.get=function(e,t){var n,i,a,s;if(this._cacheable){if(a=l(e),i=a.cache,t in i)return i[t];n=i[t]=this.func.call(e,t),s=a.chainWatchers&&a.chainWatchers[t],s&&o(s),r(this,e,t,a)}else n=this.func.call(e,t);return n},b.set=function(e,t,n){var i,o,a,s=this._cacheable,u=this.func,c=l(e,s),h=c.watching[t],m=this._suspended,p=!1,d=c.cache;if(this._readOnly)throw new Ember.Error('Cannot set read-only property "'+t+'" on object: '+Ember.inspect(e)); +this._suspended=e;try{if(s&&d.hasOwnProperty(t)&&(o=d[t],p=!0),i=u.wrappedFunction?u.wrappedFunction.length:u.length,3===i)a=u.call(e,t,n,o);else{if(2!==i)return Ember.defineProperty(e,t,null,o),void Ember.set(e,t,n);a=u.call(e,t,n)}if(p&&o===a)return;h&&Ember.propertyWillChange(e,t),p&&delete d[t],s&&(p||r(this,e,t,c),d[t]=a),h&&Ember.propertyDidChange(e,t)}finally{this._suspended=m}return a},b.teardown=function(e,t){var r=l(e);return t in r.cache&&n(this,e,t,r),this._cacheable&&delete r.cache[t],null},Ember.computed=function(e){var t;if(arguments.length>1&&(t=c.call(arguments,0,-1),e=c.call(arguments,-1)[0]),"function"!=typeof e)throw new Ember.Error("Computed Property declared without a property function");var r=new i(e);return t&&r.property.apply(r,t),r},Ember.cacheFor=function(e,t){var r=e[m],n=r&&r.cache;return n&&t in n?n[t]:void 0};var E,v;if(Ember.FEATURES.isEnabled("composable-computed-properties")){var g=Ember.guidFor,y=Ember.EnumerableUtils.map,w=Ember.EnumerableUtils.filter,_=(Ember.typeOf,function(e){return[g(e)].concat(e._dependentKeys).join("_").replace(/\./g,"_DOT_")}),C=function(e){return e instanceof Ember.ComputedProperty?_(e):e},O=function(e){return y(e,function(e){return C(e)})},A=function(e){return w(e,function(e){return e instanceof Ember.ComputedProperty})},P=function(e,t){t?(e._dependentKeys=O(t),e._dependentCPs=A(t)):e._dependentKeys=e._dependentCPs=[],e.implicitCPKey=_(e)};Ember.computed.normalizeDependentKey=C,Ember.computed.normalizeDependentKeys=O,E=function(e,t){Ember.computed[e]=function(e){var r=O(c.call(arguments));return Ember.computed(e,function(){return t.apply(this,r)})}}}Ember.FEATURES.isEnabled("composable-computed-properties")?v=function(e,t){Ember.computed[e]=function(){var e=c.call(arguments),r=O(e),n=Ember.computed(function(){return t.apply(this,[a(this,r)])});return n.property.apply(n,e)}}:(E=function(e,t){Ember.computed[e]=function(e){var r=c.call(arguments);return Ember.computed(e,function(){return t.apply(this,r)})}},v=function(e,t){Ember.computed[e]=function(){var e=c.call(arguments),r=Ember.computed(function(){return t.apply(this,[a(this,e)])});return r.property.apply(r,e)}}),Ember.FEATURES.isEnabled("composable-computed-properties")&&(Ember.computed.literal=function(e){return Ember.computed(function(){return e})}),E("empty",function(e){return Ember.isEmpty(s(this,e))}),E("notEmpty",function(e){return!Ember.isEmpty(s(this,e))}),E("none",function(e){return Ember.isNone(s(this,e))}),E("not",function(e){return!s(this,e)}),E("bool",function(e){return!!s(this,e)}),E("match",function(e,t){var r=s(this,e);return"string"==typeof r?t.test(r):!1}),E("equal",function(e,t){return s(this,e)===t}),E("gt",function(e,t){return s(this,e)>t}),E("gte",function(e,t){return s(this,e)>=t}),E("lt",function(e,t){return s(this,e)1?(u(this,e,r),r):s(this,e)})},Ember.computed.oneWay=function(e){return Ember.computed(e,function(){return s(this,e)})},Ember.computed.readOnly=function(e){return Ember.computed(e,function(){return s(this,e)}).readOnly()},Ember.computed.defaultTo=function(e){return Ember.computed(function(t,r,n){return 1===arguments.length?null!=n?n:s(this,e):null!=r?r:s(this,e)})}}(),function(){function e(e){return e+r}function t(e){return e+n}var r=":change",n=":before";Ember.addObserver=function(t,r,n,i){return Ember.addListener(t,e(r),n,i),Ember.watch(t,r),this},Ember.observersFor=function(t,r){return Ember.listenersFor(t,e(r))},Ember.removeObserver=function(t,r,n,i){return Ember.unwatch(t,r),Ember.removeListener(t,e(r),n,i),this},Ember.addBeforeObserver=function(e,r,n,i){return Ember.addListener(e,t(r),n,i),Ember.watch(e,r),this},Ember._suspendBeforeObserver=function(e,r,n,i,o){return Ember._suspendListener(e,t(r),n,i,o)},Ember._suspendObserver=function(t,r,n,i,o){return Ember._suspendListener(t,e(r),n,i,o)};var i=Ember.ArrayPolyfills.map;Ember._suspendBeforeObservers=function(e,r,n,o,a){var s=i.call(r,t);return Ember._suspendListeners(e,s,n,o,a)},Ember._suspendObservers=function(t,r,n,o,a){var s=i.call(r,e);return Ember._suspendListeners(t,s,n,o,a)},Ember.beforeObserversFor=function(e,r){return Ember.listenersFor(e,t(r))},Ember.removeBeforeObserver=function(e,r,n,i){return Ember.unwatch(e,r),Ember.removeListener(e,t(r),n,i),this}}(),function(){e("backburner/queue",["exports"],function(e){"use strict";function t(e,t,r){this.daq=e,this.name=t,this.options=r,this._queue=[]}t.prototype={daq:null,name:null,options:null,_queue:null,push:function(e,t,r,n){var i=this._queue;return i.push(e,t,r,n),{queue:this,target:e,method:t}},pushUnique:function(e,t,r,n){var i,o,a,s,u=this._queue;for(a=0,s=u.length;s>a;a+=4)if(i=u[a],o=u[a+1],i===e&&o===t)return u[a+2]=r,u[a+3]=n,{queue:this,target:e,method:t};return this._queue.push(e,t,r,n),{queue:this,target:e,method:t}},flush:function(){var e,t,r,n,i,o=this._queue,a=this.options,s=a&&a.before,u=a&&a.after,l=o.length;for(l&&s&&s(),i=0;l>i;i+=4)e=o[i],t=o[i+1],r=o[i+2],n=o[i+3],r&&r.length>0?t.apply(e,r):t.call(e);l&&u&&u(),o.length>l?(this._queue=o.slice(l),this.flush()):this._queue.length=0},cancel:function(e){var t,r,n,i,o=this._queue;for(n=0,i=o.length;i>n;n+=4)if(t=o[n],r=o[n+1],t===e.target&&r===e.method)return o.splice(n,4),!0;if(o=this._queueBeingFlushed)for(n=0,i=o.length;i>n;n+=4)if(t=o[n],r=o[n+1],t===e.target&&r===e.method)return o[n+1]=null,!0}},e.Queue=t}),e("backburner/deferred_action_queues",["backburner/queue","exports"],function(e,t){"use strict";function r(e,t){var r=this.queues={};this.queueNames=e=e||[];for(var n,o=0,a=e.length;a>o;o++)n=e[o],r[n]=new i(this,n,t[n])}function n(e,t){for(var r,n,i=0,o=t;o>=i;i++)if(r=e.queueNames[i],n=e.queues[r],n._queue.length)return i;return-1}var i=e.Queue;r.prototype={queueNames:null,queues:null,schedule:function(e,t,r,n,i,o){var a=this.queues,s=a[e];if(!s)throw new Error("You attempted to schedule an action in a queue ("+e+") that doesn't exist");return i?s.pushUnique(t,r,n,o):s.push(t,r,n,o)},flush:function(){for(var e,t,r,i,o=this.queues,a=this.queueNames,s=0,u=a.length;u>s;){e=a[s],t=o[e],r=t._queueBeingFlushed=t._queue.slice(),t._queue=[];var l,c,h,m,p=t.options,d=p&&p.before,f=p&&p.after,b=0,E=r.length;for(E&&d&&d();E>b;)l=r[b],c=r[b+1],h=r[b+2],m=r[b+3],"string"==typeof c&&(c=l[c]),c&&(h&&h.length>0?c.apply(l,h):c.call(l)),b+=4;t._queueBeingFlushed=null,E&&f&&f(),-1===(i=n(this,s))?s++:s=i}}},t.DeferredActionQueues=r}),e("backburner",["backburner/deferred_action_queues","exports"],function(e,t){"use strict";function r(e){return"number"==typeof e||g.test(e)}function n(e,t){this.queueNames=e,this.options=t||{},this.options.defaultQueue||(this.options.defaultQueue=e[0]),this.instanceStack=[]}function i(e){e.begin(),l=v.setTimeout(function(){l=null,e.end()})}function o(e,t,r){(!c||h>t)&&(c&&clearTimeout(c),c=v.setTimeout(function(){c=null,h=null,a(e)},r),h=t)}function a(e){var t,r,n,i,a=+new Date;e.run(function(){for(n=0,i=E.length;i>n&&(t=E[n],!(t>a));n+=2);for(r=E.splice(0,n),n=1,i=r.length;i>n;n+=2)e.schedule(e.options.defaultQueue,null,r[n])}),E.length&&o(e,E[0],E[0]-a)}function s(e,t){for(var r,n=-1,i=0,o=b.length;o>i;i++)if(r=b[i],r[0]===e&&r[1]===t){n=i;break}return n}function u(e,t){for(var r,n=-1,i=0,o=f.length;o>i;i++)if(r=f[i],r[0]===e&&r[1]===t){n=i;break}return n}var l,c,h,m=e.DeferredActionQueues,p=[].slice,d=[].pop,f=[],b=[],E=[],v=this,g=/\d+/;n.prototype={queueNames:null,options:null,currentInstance:null,instanceStack:null,begin:function(){var e=this.options&&this.options.onBegin,t=this.currentInstance;t&&this.instanceStack.push(t),this.currentInstance=new m(this.queueNames,this.options),e&&e(this.currentInstance,t)},end:function(){var e=this.options&&this.options.onEnd,t=this.currentInstance,r=null;try{t.flush()}finally{this.currentInstance=null,this.instanceStack.length&&(r=this.instanceStack.pop(),this.currentInstance=r),e&&e(t,r)}},run:function(e,t){var r;this.begin(),t||(t=e,e=null),"string"==typeof t&&(t=e[t]);var n=!1;try{r=arguments.length>2?t.apply(e,p.call(arguments,2)):t.call(e)}finally{n||(n=!0,this.end())}return r},defer:function(e,t,r){r||(r=t,t=null),"string"==typeof r&&(r=t[r]);var n=this.DEBUG?new Error:void 0,o=arguments.length>3?p.call(arguments,3):void 0;return this.currentInstance||i(this),this.currentInstance.schedule(e,t,r,o,!1,n)},deferOnce:function(e,t,r){r||(r=t,t=null),"string"==typeof r&&(r=t[r]);var n=this.DEBUG?new Error:void 0,o=arguments.length>3?p.call(arguments,3):void 0;return this.currentInstance||i(this),this.currentInstance.schedule(e,t,r,o,!0,n)},setTimeout:function(){function e(){t.apply(i,l)}var t,n,i,a,s,u,l=p.call(arguments),c=l.length,h=this;if(0!==c){if(1===c)t=l.shift(),n=0;else if(2===c)a=l[0],s=l[1],"function"==typeof s||"function"==typeof a[s]?(i=l.shift(),t=l.shift(),n=0):r(s)?(t=l.shift(),n=l.shift()):(t=l.shift(),n=0);else{var m=l[l.length-1];r(m)&&(n=l.pop()),a=l[0],u=l[1],"function"==typeof u||"string"==typeof u&&null!==a&&u in a?(i=l.shift(),t=l.shift()):t=l.shift()}var d=+new Date+parseInt(n,10);"string"==typeof t&&(t=i[t]);var f,b;for(f=0,b=E.length;b>f&&!(d-1?f[i]:(o=v.setTimeout(function(){l||a.run.apply(a,s);var r=u(e,t);r>-1&&f.splice(r,1)},r),l&&a.run.apply(a,s),n=[e,t,o],f.push(n),n)},debounce:function(e,t){var r,n,i,o,a=this,u=arguments,l=d.call(u);return"number"==typeof l||"string"==typeof l?(r=l,l=!1):r=d.call(u),r=parseInt(r,10),n=s(e,t),n>-1&&(i=b[n],b.splice(n,1),clearTimeout(i[2])),o=v.setTimeout(function(){l||a.run.apply(a,u);var r=s(e,t);r>-1&&b.splice(r,1)},r),l&&-1===n&&a.run.apply(a,u),i=[e,t,o],b.push(i),i},cancelTimers:function(){var e,t;for(e=0,t=f.length;t>e;e++)clearTimeout(f[e][2]);for(f=[],e=0,t=b.length;t>e;e++)clearTimeout(b[e][2]);b=[],c&&(clearTimeout(c),c=null),E=[],l&&(clearTimeout(l),l=null)},hasTimers:function(){return!!E.length||l},cancel:function(e){var t=typeof e;if(e&&"object"===t&&e.queue&&e.method)return e.queue.cancel(e);if("function"!==t)return"[object Array]"===Object.prototype.toString.call(e)?this._cancelItem(u,f,e)||this._cancelItem(s,b,e):void 0;for(var r=0,n=E.length;n>r;r+=2)if(E[r+1]===e)return E.splice(r,2),!0},_cancelItem:function(e,t,r){var n,i;return r.length<3?!1:(i=e(r[0],r[1]),i>-1&&(n=t[i],n[2]===r[2])?(t.splice(i,1),clearTimeout(r[2]),!0):!1)}},n.prototype.schedule=n.prototype.defer,n.prototype.scheduleOnce=n.prototype.deferOnce,n.prototype.later=n.prototype.setTimeout,t.Backburner=n})}(),function(){function e(e){try{return a.run.apply(a,e)}catch(t){Ember.onerror(t)}}function r(){Ember.run.currentRunLoop||Ember.assert("You have turned on testing mode, which disabled the run-loop's autorun. You will need to wrap any code with asynchronous side-effects in an Ember.run",!Ember.testing)}{var n=function(e){Ember.run.currentRunLoop=e},i=function(e,t){Ember.run.currentRunLoop=t},o=t("backburner").Backburner,a=new o(["sync","actions","destroy"],{sync:{before:Ember.beginPropertyChanges,after:Ember.endPropertyChanges},defaultQueue:"actions",onBegin:n,onEnd:i}),s=[].slice;[].concat}Ember.run=function(){return Ember.onerror?e(arguments):a.run.apply(a,arguments)},Ember.run.join=function(){if(!Ember.run.currentRunLoop)return Ember.run.apply(Ember.run,arguments);var e=s.call(arguments);e.unshift("actions"),Ember.run.schedule.apply(Ember.run,e)},Ember.run.bind=function(){var e=s.call(arguments);return function(){return Ember.run.join.apply(Ember.run,e.concat(s.call(arguments)))}},Ember.run.backburner=a;Ember.run;Ember.run.currentRunLoop=null,Ember.run.queues=a.queueNames,Ember.run.begin=function(){a.begin()},Ember.run.end=function(){a.end()},Ember.run.schedule=function(){r(),a.schedule.apply(a,arguments)},Ember.run.hasScheduledTimers=function(){return a.hasTimers()},Ember.run.cancelTimers=function(){a.cancelTimers()},Ember.run.sync=function(){a.currentInstance&&a.currentInstance.queues.sync.flush()},Ember.run.later=function(){return a.later.apply(a,arguments)},Ember.run.once=function(){r();var e=s.call(arguments);return e.unshift("actions"),a.scheduleOnce.apply(a,e)},Ember.run.scheduleOnce=function(){return r(),a.scheduleOnce.apply(a,arguments)},Ember.run.next=function(){var e=s.call(arguments);return e.push(1),a.later.apply(a,e)},Ember.run.cancel=function(e){return a.cancel(e)},Ember.run.debounce=function(){return a.debounce.apply(a,arguments)},Ember.run.throttle=function(){return a.throttle.apply(a,arguments)}}(),function(){function e(e,t){return r(o(t)?Ember.lookup:e,t)}function t(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])}Ember.LOG_BINDINGS=!1||!!Ember.ENV.LOG_BINDINGS;var r=Ember.get,n=(Ember.set,Ember.guidFor),i=/^([A-Z$]|([0-9][A-Z$]))/,o=Ember.isGlobalPath=function(e){return i.test(e)},a=function(e,t){this._direction="fwd",this._from=t,this._to=e,this._directionMap=Ember.Map.create()};a.prototype={copy:function(){var e=new a(this._to,this._from);return this._oneWay&&(e._oneWay=!0),e},from:function(e){return this._from=e,this},to:function(e){return this._to=e,this},oneWay:function(){return this._oneWay=!0,this},toString:function(){var e=this._oneWay?"[oneWay]":"";return"Ember.Binding<"+n(this)+">("+this._from+" -> "+this._to+")"+e},connect:function(t){Ember.assert("Must pass a valid object to Ember.Binding.connect()",!!t);var r=this._from,n=this._to;return Ember.trySet(t,n,e(t,r)),Ember.addObserver(t,r,this,this.fromDidChange),this._oneWay||Ember.addObserver(t,n,this,this.toDidChange),this._readyToSync=!0,this},disconnect:function(e){Ember.assert("Must pass a valid object to Ember.Binding.disconnect()",!!e);var t=!this._oneWay;return Ember.removeObserver(e,this._from,this,this.fromDidChange),t&&Ember.removeObserver(e,this._to,this,this.toDidChange),this._readyToSync=!1,this},fromDidChange:function(e){this._scheduleSync(e,"fwd")},toDidChange:function(e){this._scheduleSync(e,"back")},_scheduleSync:function(e,t){var r=this._directionMap,n=r.get(e);n||(Ember.run.schedule("sync",this,this._sync,e),r.set(e,t)),"back"===n&&"fwd"===t&&r.set(e,"fwd")},_sync:function(t){var n=Ember.LOG_BINDINGS;if(!t.isDestroyed&&this._readyToSync){var i=this._directionMap,o=i.get(t),a=this._from,s=this._to;if(i.remove(t),"fwd"===o){var u=e(t,this._from);n&&Ember.Logger.log(" ",this.toString(),"->",u,t),this._oneWay?Ember.trySet(t,s,u):Ember._suspendObserver(t,s,this,this.toDidChange,function(){Ember.trySet(t,s,u)})}else if("back"===o){var l=r(t,this._to);n&&Ember.Logger.log(" ",this.toString(),"<-",l,t),Ember._suspendObserver(t,a,this,this.fromDidChange,function(){Ember.trySet(Ember.isGlobalPath(a)?Ember.lookup:t,a,l)})}}}},t(a,{from:function(){var e=this,t=new e;return t.from.apply(t,arguments)},to:function(){var e=this,t=new e;return t.to.apply(t,arguments)},oneWay:function(e,t){var r=this,n=new r(null,e);return n.oneWay(t)}}),Ember.Binding=a,Ember.bind=function(e,t,r){return new Ember.Binding(t,r).connect(e)},Ember.oneWay=function(e,t,r){return new Ember.Binding(t,r).oneWay().connect(e)}}(),function(){function e(){var e,t=this.__nextSuper;return t&&(this.__nextSuper=null,e=t.apply(this,arguments),this.__nextSuper=t),e}function t(e){var t=N(e,!0),r=t.mixins;return r?t.hasOwnProperty("mixins")||(r=t.mixins=x(r)):r=t.mixins={},r}function r(e,t){return t&&t.length>0&&(e.mixins=O.call(t,function(e){if(e instanceof w)return e;var t=new w;return t.properties=e,t})),e}function n(e){return"function"==typeof e&&e.isMethod!==!1&&e!==Boolean&&e!==Object&&e!==Number&&e!==Array&&e!==Date&&e!==String}function i(e,t){var r;return t instanceof w?(r=V(t),e[r]?k:(e[r]=t,t.properties)):t}function o(e,t,r,n){var i;return i=r[e]||n[e],t[e]&&(i=i?i.concat(t[e]):t[e]),i}function a(e,t,r,n,i){var o;return void 0===n[t]&&(o=i[t]),o=o||e.descs[t],o&&o instanceof Ember.ComputedProperty?(r=x(r),r.func=Ember.wrap(r.func,o.func),r):r}function s(e,t,r,n,i){var o;return void 0===i[t]&&(o=n[t]),o=o||e[t],"function"!=typeof o?r:Ember.wrap(r,o)}function u(e,t,r,n){var i=n[t]||e[t];return i?"function"==typeof i.concat?i.concat(r):Ember.makeArray(i).concat(r):Ember.makeArray(r)}function l(t,r,i,o){var a=o[r]||t[r];if(!a)return i;var u=Ember.merge({},a),l=!1;for(var c in i)if(i.hasOwnProperty(c)){var h=i[c];n(h)?(l=!0,u[c]=s(t,c,h,a,{})):u[c]=h}return l&&(u._super=e),u}function c(e,t,r,i,o,c,h,m){if(r instanceof Ember.Descriptor){if(r===_&&o[t])return k;r.func&&(r=a(i,t,r,c,o)),o[t]=r,c[t]=void 0}else h&&A.call(h,t)>=0||"concatenatedProperties"===t||"mergedProperties"===t?r=u(e,t,r,c):m&&A.call(m,t)>=0?r=l(e,t,r,c):n(r)&&(r=s(e,t,r,c,o)),o[t]=void 0,c[t]=r}function h(e,t,r,n,a,s){function u(e){delete r[e],delete n[e]}for(var l,m,p,d,f,b,E=0,v=e.length;v>E;E++)if(l=e[E],Ember.assert("Expected hash or Mixin instance, got "+Object.prototype.toString.call(l),"object"==typeof l&&null!==l&&"[object Array]"!==Object.prototype.toString.call(l)),m=i(t,l),m!==k)if(m){b=N(a),a.willMergeMixin&&a.willMergeMixin(m),d=o("concatenatedProperties",m,n,a),f=o("mergedProperties",m,n,a);for(p in m)m.hasOwnProperty(p)&&(s.push(p),c(a,p,m[p],b,r,n,d,f));m.hasOwnProperty("toString")&&(a.toString=m.toString)}else l.mixins&&(h(l.mixins,t,r,n,a,s),l._without&&P.call(l._without,u))}function m(e,t,r,n){if(D.test(t)){var i=n.bindings;i?n.hasOwnProperty("bindings")||(i=n.bindings=x(n.bindings)):i=n.bindings={},i[t]=r}}function p(e,t){var r,n,i,o=t.bindings;if(o){for(r in o)n=o[r],n&&(i=r.slice(0,-7),n instanceof Ember.Binding?(n=n.copy(),n.to(i)):n=new Ember.Binding(i,n),n.connect(e),e[r]=n);t.bindings={}}}function d(e,t){return p(e,t||N(e)),e}function f(e,t,r,n,i){var o,a=t.methodName;return n[a]||i[a]?(o=i[a],t=n[a]):r.descs[a]?(t=r.descs[a],o=void 0):(t=void 0,o=e[a]),{desc:t,value:o}}function b(e,t,r,n,i){var o=r[n];if(o)for(var a=0,s=o.length;s>a;a++)Ember[i](e,o[a],null,t)}function E(e,t,r){var n=e[t];"function"==typeof n&&(b(e,t,n,"__ember_observesBefore__","removeBeforeObserver"),b(e,t,n,"__ember_observes__","removeObserver"),b(e,t,n,"__ember_listens__","removeListener")),"function"==typeof r&&(b(e,t,r,"__ember_observesBefore__","addBeforeObserver"),b(e,t,r,"__ember_observes__","addObserver"),b(e,t,r,"__ember_listens__","addListener"))}function v(r,n,i){var o,a,s,u={},l={},c=N(r),p=[];r._super=e,h(n,t(r),u,l,r,p);for(var b=0,v=p.length;v>b;b++)if(o=p[b],"constructor"!==o&&l.hasOwnProperty(o)&&(s=u[o],a=l[o],s!==_)){for(;s&&s instanceof C;){var g=f(r,s,c,u,l);s=g.desc,a=g.value}(void 0!==s||void 0!==a)&&(E(r,o,a),m(r,o,a,c),S(r,o,s,a,c))}return i||d(r,c),r}function g(e,t,r){var n=V(e);if(r[n])return!1;if(r[n]=!0,e===t)return!0;for(var i=e.mixins,o=i?i.length:0;--o>=0;)if(g(i[o],t,r))return!0;return!1}function y(e,t,r){if(!r[V(t)])if(r[V(t)]=!0,t.properties){var n=t.properties;for(var i in n)n.hasOwnProperty(i)&&(e[i]=!0)}else t.mixins&&P.call(t.mixins,function(t){y(e,t,r)})}var w,_,C,O=Ember.ArrayPolyfills.map,A=Ember.ArrayPolyfills.indexOf,P=Ember.ArrayPolyfills.forEach,T=[].slice,x=Ember.create,S=Ember.defineProperty,V=Ember.guidFor,N=Ember.meta,I=Ember.META_KEY,R=Ember.expandProperties,k={},D=Ember.IS_BINDING=/^.+Binding$/;Ember.mixin=function(e){var t=T.call(arguments,1);return v(e,t,!1),e},Ember.Mixin=function(){return r(this,arguments)},w=Ember.Mixin,w.prototype={properties:null,mixins:null,ownerConstructor:null},w._apply=v,w.applyPartial=function(e){var t=T.call(arguments,1);return v(e,t,!0)},w.finishPartial=d,Ember.anyUnprocessedMixins=!1,w.create=function(){Ember.anyUnprocessedMixins=!0;var e=this;return r(new e,arguments)};var j=w.prototype;j.reopen=function(){var e,t;this.properties?(e=w.create(),e.properties=this.properties,delete this.properties,this.mixins=[e]):this.mixins||(this.mixins=[]);var r,n=arguments.length,i=this.mixins;for(r=0;n>r;r++)e=arguments[r],Ember.assert("Expected hash or Mixin instance, got "+Object.prototype.toString.call(e),"object"==typeof e&&null!==e&&"[object Array]"!==Object.prototype.toString.call(e)),e instanceof w?i.push(e):(t=w.create(),t.properties=e,i.push(t));return this},j.apply=function(e){return v(e,[this],!1)},j.applyPartial=function(e){return v(e,[this],!0)},j.detect=function(e){if(!e)return!1;if(e instanceof w)return g(e,this,{});var t=e[I],r=t&&t.mixins;return r?!!r[V(this)]:!1},j.without=function(){var e=new w(this);return e._without=T.call(arguments),e},j.keys=function(){var e={},t={},r=[];y(e,this,t);for(var n in e)e.hasOwnProperty(n)&&r.push(n);return r},w.mixins=function(e){var t=e[I],r=t&&t.mixins,n=[];if(!r)return n;for(var i in r){var o=r[i];o.properties||n.push(o)}return n},_=new Ember.Descriptor,_.toString=function(){return"(Required Property)"},Ember.required=function(){return _},C=function(e){this.methodName=e},C.prototype=new Ember.Descriptor,Ember.aliasMethod=function(e){return new C(e)},Ember.observer=function(){var e,t=T.call(arguments,-1)[0],r=function(t){e.push(t)},n=T.call(arguments,0,-1);"function"!=typeof t&&(t=arguments[0],n=T.call(arguments,1)),e=[];for(var i=0;ie;e++){var r=arguments[e];Ember.assert("Immediate observers must observe internal properties only, not properties on other objects.","string"!=typeof r||-1===r.indexOf("."))}return Ember.observer.apply(this,arguments)},Ember.beforeObserver=function(){var e,t=T.call(arguments,-1)[0],r=function(t){e.push(t)},n=T.call(arguments,0,-1);"function"!=typeof t&&(t=arguments[0],n=T.call(arguments,1)),e=[];for(var i=0;ir;r++)if(e[r]===t)return r;return-1},r=function(e){var t=e._promiseCallbacks;return t||(t=e._promiseCallbacks={}),t};e["default"]={mixin:function(e){return e.on=this.on,e.off=this.off,e.trigger=this.trigger,e._promiseCallbacks=void 0,e},on:function(e,n){var i,o=r(this);i=o[e],i||(i=o[e]=[]),-1===t(i,n)&&i.push(n)},off:function(e,n){var i,o,a=r(this);return n?(i=a[e],o=t(i,n),void(-1!==o&&i.splice(o,1))):void(a[e]=[])},trigger:function(e,t){var n,i,o=r(this);if(n=o[e])for(var a=0;at;t++)e[t]&&i.push(n[t]);return i})})}var o=e["default"],a=t["default"],s=r.isFunction,u=r.isArray;n["default"]=i}),e("rsvp/hash",["./promise","./utils","exports"],function(e,t,r){"use strict";var n=e["default"],i=t.isNonThenable,o=t.keysOf;r["default"]=function(e){return new n(function(t,r){function a(e){return function(r){c[e]=r,0===--m&&t(c)}}function s(e){m=0,r(e)}var u,l,c={},h=o(e),m=h.length;if(0===m)return void t(c);for(var p=0;ps;s++)l.push(t(n[s]));return i(l,r)})}}),e("rsvp/node",["./promise","exports"],function(e,t){"use strict";function r(e,t){return function(r,n){r?t(r):e(arguments.length>2?i.call(arguments,1):n)}}var n=e["default"],i=Array.prototype.slice;t["default"]=function(e,t){return function(){var o=i.call(arguments),a=this||t;return new n(function(t,i){n.all(o).then(function(n){try{n.push(r(t,i)),e.apply(a,n)}catch(o){i(o)}})})}}}),e("rsvp/promise",["./config","./events","./instrument","./utils","./promise/cast","./promise/all","./promise/race","./promise/resolve","./promise/reject","exports"],function(e,t,r,n,i,o,a,s,u,l){"use strict";function c(){}function h(e,t){if(!A(e))throw new TypeError("You must pass a resolver function as the first argument to the promise constructor");if(!(this instanceof h))throw new TypeError("Failed to construct 'Promise': Please use the 'new' operator, this object constructor cannot be called as a function.");this._id=R++,this._label=t,this._subscribers=[],_.instrument&&C("created",this),c!==e&&m(e,this)}function m(e,t){function r(e){E(t,e)}function n(e){g(t,e)}try{e(r,n)}catch(i){n(i)}}function p(e,t,r,n){var i=e._subscribers,o=i.length;i[o]=t,i[o+j]=r,i[o+M]=n}function d(e,t){var r,n,i=e._subscribers,o=e._detail;_.instrument&&C(t===j?"fulfilled":"rejected",e);for(var a=0;aa;a++){if(n=t[a],o=i(e,n.fullName),void 0===o)throw new Error("Attempting to inject an unknown injection: `"+n.fullName+"`");r[n.property]=o}return r}function u(e,t,r){var n=e._options.get(t);if(n&&void 0!==n[r])return n[r];var i=t.split(":")[0];return n=e._typeOptions.get(i),n?n[r]:void 0}function l(e,t){var r,n=t,i=e.resolve(n),o=e.factoryCache,a=t.split(":")[0];if(void 0!==i){if(o.has(t))return o.get(t);if(!i||"function"!=typeof i.extend||!Ember.MODEL_FACTORY_INJECTIONS&&"model"===a)return i;var s=c(e,t),u=h(e,t);return u._toString=e.makeToString(i,t),r=i.extend(s),r.reopenClass(u),o.set(t,r),r}}function c(e,t){var r=t.split(":"),n=r[0],i=[];return i=i.concat(e.typeInjections.get(n)||[]),i=i.concat(e.injections[t]||[]),i=s(e,i),i._debugContainerKey=t,i.container=e,i}function h(e,t){var r=t.split(":"),n=r[0],i=[];return i=i.concat(e.factoryTypeInjections.get(n)||[]),i=i.concat(e.factoryInjections[t]||[]),i=s(e,i),i._debugContainerKey=t,i}function m(e,t){var r=l(e,t);return u(e,t,"instantiate")===!1?r:r?"function"==typeof r.extend?r.create():r.create(c(e,t)):void 0}function p(e,t){e.cache.eachLocal(function(r,n){u(e,r,"instantiate")!==!1&&t(n)})}function d(e){e.cache.eachLocal(function(t,r){u(e,t,"instantiate")!==!1&&r.destroy()}),e.cache.dict={}}function f(e,t,r,n){var i=e.get(t);i||(i=[],e.set(t,i)),i.push({property:r,fullName:n})}function b(e){if(!g.test(e))throw new TypeError("Invalid Fullname, expected: `type:name` got: "+e)}function E(e,t,r,n){var i=e[t]=e[t]||[];i.push({property:r,fullName:n})}var v=e["default"];r.prototype={parent:null,children:null,resolver:null,registry:null,cache:null,typeInjections:null,injections:null,_options:null,_typeOptions:null,child:function(){var e=new r(this);return this.children.push(e),e},set:function(e,t,r){e[t]=r},register:function(e,t,r){if(b(e),void 0===t)throw new TypeError("Attempting to register an unknown factory: `"+e+"`");var n=this.normalize(e);if(this.cache.has(n))throw new Error("Cannot re-register: `"+e+"`, as it has already been looked up.");this.registry.set(n,t),this._options.set(n,r||{})},unregister:function(e){b(e);var t=this.normalize(e);this.registry.remove(t),this.cache.remove(t),this.factoryCache.remove(t),this.resolveCache.remove(t),this._options.remove(t)},resolve:function(e){b(e);var t=this.normalize(e),r=this.resolveCache.get(t);if(r)return r;var n=this.resolver(t)||this.registry.get(t);return this.resolveCache.set(t,n),n},describe:function(e){return e},normalize:function(e){return e},makeToString:function(e){return e.toString()},lookup:function(e,t){return b(e),i(this,this.normalize(e),t)},lookupFactory:function(e){return b(e),l(this,this.normalize(e))},has:function(e){return b(e),n(this,this.normalize(e))},optionsForType:function(e,t){this.parent&&o("optionsForType"),this._typeOptions.set(e,t)},options:function(e,t){this.optionsForType(e,t)},typeInjection:function(e,t,r){b(r),this.parent&&o("typeInjection"),f(this.typeInjections,e,t,r)},injection:function(e,t,r){this.parent&&o("injection"),b(r);var n=this.normalize(r);if(-1===e.indexOf(":"))return this.typeInjection(e,t,n);b(e);var i=this.normalize(e);E(this.injections,i,t,n)},factoryTypeInjection:function(e,t,r){this.parent&&o("factoryTypeInjection"),f(this.factoryTypeInjections,e,t,this.normalize(r))},factoryInjection:function(e,t,r){this.parent&&o("injection");var n=this.normalize(e),i=this.normalize(r);return b(r),-1===e.indexOf(":")?this.factoryTypeInjection(n,t,i):(b(e),void E(this.factoryInjections,n,t,i))},destroy:function(){for(var e=0,t=this.children.length;t>e;e++)this.children[e].destroy();this.children=[],p(this,function(e){e.destroy()}),this.parent=void 0,this.isDestroyed=!0},reset:function(){for(var e=0,t=this.children.length;t>e;e++)d(this.children[e]);d(this)}};var g=/^[^:]+.+:[^:]+$/;t["default"]=r}),e("container/inheriting_dict",["exports"],function(e){"use strict";function t(e){this.parent=e,this.dict={}}t.prototype={parent:null,dict:null,get:function(e){var t=this.dict;return t.hasOwnProperty(e)?t[e]:this.parent?this.parent.get(e):void 0},set:function(e,t){this.dict[e]=t},remove:function(e){delete this.dict[e]},has:function(e){var t=this.dict;return t.hasOwnProperty(e)?!0:this.parent?this.parent.has(e):!1},eachLocal:function(e,t){var r=this.dict;for(var n in r)r.hasOwnProperty(n)&&e.call(t,n,r[n])}},e["default"]=t}),e("container",["container/container","exports"],function(e,t){"use strict";Ember.MODEL_FACTORY_INJECTIONS=!1||!!Ember.ENV.MODEL_FACTORY_INJECTIONS;var r=e["default"];t["default"]=r})}(),function(){function e(r,n,i,o){var a,s,u;if("object"!=typeof r||null===r)return r;if(n&&(s=t(i,r))>=0)return o[s];if(Ember.assert("Cannot clone an Ember.Object that does not implement Ember.Copyable",!(r instanceof Ember.Object)||Ember.Copyable&&Ember.Copyable.detect(r)),"array"===Ember.typeOf(r)){if(a=r.slice(),n)for(s=a.length;--s>=0;)a[s]=e(a[s],n,i,o)}else if(Ember.Copyable&&Ember.Copyable.detect(r))a=r.copy(n,i,o);else if(r instanceof Date)a=new Date(r.getTime());else{a={};for(u in r)r.hasOwnProperty(u)&&"__"!==u.substring(0,2)&&(a[u]=n?e(r[u],n,i,o):r[u])}return n&&(i.push(r),o.push(a)),a}var t=Ember.EnumerableUtils.indexOf;if(Ember.compare=function i(e,t){if(e===t)return 0;var r=Ember.typeOf(e),n=Ember.typeOf(t),o=Ember.Comparable;if(o){if("instance"===r&&o.detect(e.constructor))return e.constructor.compare(e,t);if("instance"===n&&o.detect(t.constructor))return 1-t.constructor.compare(t,e)}var a=Ember.ORDER_DEFINITION_MAPPING;if(!a){var s=Ember.ORDER_DEFINITION;a=Ember.ORDER_DEFINITION_MAPPING={};var u,l;for(u=0,l=s.length;l>u;++u)a[s[u]]=u;delete Ember.ORDER_DEFINITION}var c=a[r],h=a[n];if(h>c)return-1;if(c>h)return 1;switch(r){case"boolean":case"number":return t>e?-1:e>t?1:0;case"string":var m=e.localeCompare(t);return 0>m?-1:m>0?1:0;case"array":for(var p=e.length,d=t.length,f=Math.min(p,d),b=0,E=0;0===b&&f>E;)b=i(e[E],t[E]),E++;return 0!==b?b:d>p?-1:p>d?1:0;case"instance":return Ember.Comparable&&Ember.Comparable.detect(e)?e.compare(e,t):0;case"date":var v=e.getTime(),g=t.getTime();return g>v?-1:v>g?1:0;default:return 0}},Ember.copy=function(t,r){return"object"!=typeof t||null===t?t:Ember.Copyable&&Ember.Copyable.detect(t)?t.copy(r):e(t,r,r?[]:null,r?[]:null)},Ember.isEqual=function(e,t){return e&&"function"==typeof e.isEqual?e.isEqual(t):e===t},Ember.ORDER_DEFINITION=Ember.ENV.ORDER_DEFINITION||["undefined","null","boolean","number","string","array","object","instance","function","class","date"],Ember.keys=Object.keys,!Ember.keys||Ember.create.isSimulated){var r=["constructor","hasOwnProperty","isPrototypeOf","propertyIsEnumerable","valueOf","toLocaleString","toString"],n=function(e,r,n){"__"!==n.substring(0,2)&&"_super"!==n&&(t(r,n)>=0||e.hasOwnProperty(n)&&r.push(n))};Ember.keys=function(e){var t,i=[];for(t in e)n(e,i,t);for(var o=0,a=r.length;a>o;o++)t=r[o],n(e,i,t);return i}}}(),function(){var e=/[ _]/g,t={},r=/([a-z\d])([A-Z])/g,n=/(\-|_|\.|\s)+(.)?/g,i=/([a-z\d])([A-Z]+)/g,o=/\-|\s+/g;Ember.STRINGS={},Ember.String={fmt:function(e,t){var r=0;return e.replace(/%@([0-9]+)?/g,function(e,n){return n=n?parseInt(n,10)-1:r++,e=t[n],null===e?"(null)":void 0===e?"":Ember.inspect(e)})},loc:function(e,t){return e=Ember.STRINGS[e]||e,Ember.String.fmt(e,t)},w:function(e){return e.split(/\s+/)},decamelize:function(e){return e.replace(r,"$1_$2").toLowerCase()},dasherize:function(r){var n,i=t,o=i.hasOwnProperty(r);return o?i[r]:(n=Ember.String.decamelize(r).replace(e,"-"),i[r]=n,n)},camelize:function(e){return e.replace(n,function(e,t,r){return r?r.toUpperCase():""}).replace(/^([A-Z])/,function(e){return e.toLowerCase()})},classify:function(e){for(var t=e.split("."),r=[],n=0,i=t.length;i>n;n++){var o=Ember.String.camelize(t[n]);r.push(o.charAt(0).toUpperCase()+o.substr(1))}return r.join(".")},underscore:function(e){return e.replace(i,"$1_$2").replace(o,"_").toLowerCase()},capitalize:function(e){return e.charAt(0).toUpperCase()+e.substr(1)}}}(),function(){var e=Ember.String.fmt,t=Ember.String.w,r=Ember.String.loc,n=Ember.String.camelize,i=Ember.String.decamelize,o=Ember.String.dasherize,a=Ember.String.underscore,s=Ember.String.capitalize,u=Ember.String.classify;(Ember.EXTEND_PROTOTYPES===!0||Ember.EXTEND_PROTOTYPES.String)&&(String.prototype.fmt=function(){return e(this,arguments)},String.prototype.w=function(){return t(this)},String.prototype.loc=function(){return r(this,arguments)},String.prototype.camelize=function(){return n(this)},String.prototype.decamelize=function(){return i(this)},String.prototype.dasherize=function(){return o(this)},String.prototype.underscore=function(){return a(this)},String.prototype.classify=function(){return u(this)},String.prototype.capitalize=function(){return s(this)})}(),function(){var e=Ember.get,t=Ember.set,r=Array.prototype.slice,n=Ember.getProperties;Ember.Observable=Ember.Mixin.create({get:function(t){return e(this,t)},getProperties:function(){return n.apply(null,[this].concat(r.call(arguments)))},set:function(e,r){return t(this,e,r),this},setProperties:function(e){return Ember.setProperties(this,e)},beginPropertyChanges:function(){return Ember.beginPropertyChanges(),this},endPropertyChanges:function(){return Ember.endPropertyChanges(),this},propertyWillChange:function(e){return Ember.propertyWillChange(this,e),this},propertyDidChange:function(e){return Ember.propertyDidChange(this,e),this},notifyPropertyChange:function(e){return this.propertyWillChange(e),this.propertyDidChange(e),this},addBeforeObserver:function(e,t,r){Ember.addBeforeObserver(this,e,t,r)},addObserver:function(e,t,r){Ember.addObserver(this,e,t,r)},removeObserver:function(e,t,r){Ember.removeObserver(this,e,t,r)},hasObserverFor:function(e){return Ember.hasListeners(this,e+":change")},getWithDefault:function(e,t){return Ember.getWithDefault(this,e,t)},incrementProperty:function(r,n){return Ember.isNone(n)&&(n=1),Ember.assert("Must pass a numeric value to incrementProperty",!isNaN(parseFloat(n))&&isFinite(n)),t(this,r,(e(this,r)||0)+n),e(this,r)},decrementProperty:function(r,n){return Ember.isNone(n)&&(n=1),Ember.assert("Must pass a numeric value to decrementProperty",!isNaN(parseFloat(n))&&isFinite(n)),t(this,r,(e(this,r)||0)-n),e(this,r)},toggleProperty:function(r){return t(this,r,!e(this,r)),e(this,r)},cacheFor:function(e){return Ember.cacheFor(this,e)},observersForKey:function(e){return Ember.observersFor(this,e)}})}(),function(){function e(){var e,t,o=!1,a=function(){o||a.proto(),n(this,i,w),n(this,"__nextSuper",y);var u=s(this),l=u.proto;if(u.proto=this,e){var m=e;e=null,this.reopen.apply(this,m)}if(t){var p=t;t=null;for(var d=this.concatenatedProperties,f=0,E=p.length;E>f;f++){var _=p[f];if(Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.",!(_ instanceof Ember.Mixin)),"object"!=typeof _&&void 0!==_)throw new Ember.Error("Ember.Object.create only accepts objects.");if(_)for(var C=Ember.keys(_),O=0,A=C.length;A>O;O++){var P=C[O];if(_.hasOwnProperty(P)){var T=_[P],x=Ember.IS_BINDING;if(x.test(P)){var S=u.bindings;S?u.hasOwnProperty("bindings")||(S=u.bindings=r(u.bindings)):S=u.bindings={},S[P]=T}var V=u.descs[P];if(Ember.assert("Ember.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().",!(T instanceof Ember.ComputedProperty)),Ember.assert("Ember.Object.create no longer supports defining methods that call _super.",!("function"==typeof T&&-1!==T.toString().indexOf("._super"))),Ember.assert("`actions` must be provided at extend time, not at create time, when Ember.ActionHandler is used (i.e. views, controllers & routes).",!("actions"===P&&Ember.ActionHandler.detect(this))),d&&g(d,P)>=0){var N=this[P];T=N?"function"==typeof N.concat?N.concat(T):Ember.makeArray(N).concat(T):Ember.makeArray(T)}V?V.set(this,P,T):"function"!=typeof this.setUnknownProperty||P in this?v?Ember.defineProperty(this,P,null,T):this[P]=T:this.setUnknownProperty(P,T)}}}}b(this,u),this.init.apply(this,arguments),u.proto=l,c(this),h(this,"init")};return a.toString=d.prototype.toString,a.willReopen=function(){o&&(a.PrototypeMixin=d.create(a.PrototypeMixin)),o=!1},a._initMixins=function(t){e=t},a._initProperties=function(e){t=e},a.proto=function(){var e=a.superclass;return e&&e.proto(),o||(o=!0,a.PrototypeMixin.applyPartial(a.prototype),l(a.prototype)),this.prototype},a}function t(e){return function(){return e}}var r=(Ember.set,Ember.get,Ember.create),n=Ember.platform.defineProperty,i=Ember.GUID_KEY,o=Ember.guidFor,a=Ember.generateGuid,s=Ember.meta,u=Ember.META_KEY,l=Ember.rewatch,c=Ember.finishChains,h=Ember.sendEvent,m=Ember.destroy,p=Ember.run.schedule,d=Ember.Mixin,f=d._apply,b=d.finishPartial,E=d.prototype.reopen,v=Ember.ENV.MANDATORY_SETTER,g=Ember.EnumerableUtils.indexOf,y={configurable:!0,writable:!0,enumerable:!1,value:void 0},w={configurable:!0,writable:!0,enumerable:!1,value:null},_=e();_.toString=function(){return"Ember.CoreObject"},_.PrototypeMixin=d.create({reopen:function(){return f(this,arguments,!0),this},init:function(){},concatenatedProperties:null,isDestroyed:!1,isDestroying:!1,destroy:function(){return this.isDestroying?void 0:(this.isDestroying=!0,p("actions",this,this.willDestroy),p("destroy",this,this._scheduledDestroy),this)},willDestroy:Ember.K,_scheduledDestroy:function(){this.isDestroyed||(m(this),this.isDestroyed=!0)},bind:function(e,t){return t instanceof Ember.Binding||(t=Ember.Binding.from(t)),t.to(e).connect(this),t},toString:function(){var e="function"==typeof this.toStringExtension,r=e?":"+this.toStringExtension():"",n="<"+this.constructor.toString()+":"+o(this)+r+">";return this.toString=t(n),n}}),_.PrototypeMixin.ownerConstructor=_,Ember.config.overridePrototypeMixin&&Ember.config.overridePrototypeMixin(_.PrototypeMixin),_.__super__=null;var C=d.create({ClassMixin:Ember.required(),PrototypeMixin:Ember.required(),isClass:!0,isMethod:!1,extend:function(){var t,n=e();return n.ClassMixin=d.create(this.ClassMixin),n.PrototypeMixin=d.create(this.PrototypeMixin),n.ClassMixin.ownerConstructor=n,n.PrototypeMixin.ownerConstructor=n,E.apply(n.PrototypeMixin,arguments),n.superclass=this,n.__super__=this.prototype,t=n.prototype=r(this.prototype),t.constructor=n,a(t),s(t).proto=t,n.ClassMixin.apply(n),n},createWithMixins:function(){var e=this;return arguments.length>0&&this._initMixins(arguments),new e},create:function(){var e=this;return arguments.length>0&&this._initProperties(arguments),new e},reopen:function(){return this.willReopen(),E.apply(this.PrototypeMixin,arguments),this},reopenClass:function(){return E.apply(this.ClassMixin,arguments),f(this,arguments,!1),this},detect:function(e){if("function"!=typeof e)return!1;for(;e;){if(e===this)return!0;e=e.superclass}return!1},detectInstance:function(e){return e instanceof this},metaForProperty:function(e){var t=this.proto()[u],r=t&&t.descs[e];return Ember.assert("metaForProperty() could not find a computed property with key '"+e+"'.",!!r&&r instanceof Ember.ComputedProperty),r._meta||{}},eachComputedProperty:function(e,t){var r,n=this.proto(),i=s(n).descs,o={};for(var a in i)r=i[a],r instanceof Ember.ComputedProperty&&e.call(t||this,a,r._meta||o)}});C.ownerConstructor=_,Ember.config.overrideClassMixin&&Ember.config.overrideClassMixin(C),_.ClassMixin=C,C.apply(_),Ember.CoreObject=_}(),function(){Ember.Object=Ember.CoreObject.extend(Ember.Observable),Ember.Object.toString=function(){return"Ember.Object"}}(),function(){function e(t,r,i){var a=t.length;l[t.join(".")]=r;for(var s in r)if(c.call(r,s)){var u=r[s];if(t[a]=s,u&&u.toString===n)u.toString=o(t.join(".")),u[m]=t.join(".");else if(u&&u.isNamespace){if(i[h(u)])continue;i[h(u)]=!0,e(t,u,i)}}t.length=a}function t(){var e,t,r=Ember.Namespace,n=Ember.lookup;if(!r.PROCESSED)for(var i in n)if("parent"!==i&&"top"!==i&&"frameElement"!==i&&"webkitStorageInfo"!==i&&!("globalStorage"===i&&n.StorageList&&n.globalStorage instanceof n.StorageList||n.hasOwnProperty&&!n.hasOwnProperty(i))){try{e=Ember.lookup[i],t=e&&e.isNamespace}catch(o){continue}t&&(Ember.deprecate("Namespaces should not begin with lowercase.",/^[A-Z]/.test(i)),e[m]=i)}}function r(e){var t=e.superclass;return t?t[m]?t[m]:r(t):void 0}function n(){Ember.BOOTED||this[m]||i();var e;if(this[m])e=this[m];else if(this._toString)e=this._toString;else{var t=r(this);e=t?"(subclass of "+t+")":"(unknown mixin)",this.toString=o(e)}return e}function i(){var r=!u.PROCESSED,n=Ember.anyUnprocessedMixins;if(r&&(t(),u.PROCESSED=!0),r||n){for(var i,o=u.NAMESPACES,a=0,s=o.length;s>a;a++)i=o[a],e([i.toString()],i,{});Ember.anyUnprocessedMixins=!1}}function o(e){return function(){return e}}var a=Ember.get,s=Ember.ArrayPolyfills.indexOf,u=Ember.Namespace=Ember.Object.extend({isNamespace:!0,init:function(){Ember.Namespace.NAMESPACES.push(this),Ember.Namespace.PROCESSED=!1},toString:function(){var e=a(this,"name");return e?e:(t(),this[Ember.GUID_KEY+"_name"])},nameClasses:function(){e([this.toString()],this,{})},destroy:function(){var e=Ember.Namespace.NAMESPACES;Ember.lookup[this.toString()]=void 0,delete Ember.Namespace.NAMESPACES_BY_ID[this.toString()],e.splice(s.call(e,this),1),this._super()}});u.reopenClass({NAMESPACES:[Ember],NAMESPACES_BY_ID:{},PROCESSED:!1,processAll:i,byName:function(e){return Ember.BOOTED||i(),l[e]}});var l=u.NAMESPACES_BY_ID,c={}.hasOwnProperty,h=Ember.guidFor,m=Ember.NAME_KEY=Ember.GUID_KEY+"_name";Ember.Mixin.prototype.toString=n}(),function(){function e(e,t){var r=t.slice(8);r in this||l(this,r)}function t(e,t){var r=t.slice(8);r in this||c(this,r)}var r=Ember.get,n=Ember.set,i=Ember.String.fmt,o=Ember.addBeforeObserver,a=Ember.addObserver,s=Ember.removeBeforeObserver,u=Ember.removeObserver,l=Ember.propertyWillChange,c=Ember.propertyDidChange,h=Ember.meta,m=Ember.defineProperty;Ember.ObjectProxy=Ember.Object.extend({content:null,_contentDidChange:Ember.observer("content",function(){Ember.assert("Can't set ObjectProxy's content to itself",this.get("content")!==this)}),isTruthy:Ember.computed.bool("content"),_debugContainerKey:null,willWatchProperty:function(r){var n="content."+r;o(this,n,null,e),a(this,n,null,t)},didUnwatchProperty:function(r){var n="content."+r;s(this,n,null,e),u(this,n,null,t)},unknownProperty:function(e){var t=r(this,"content");return t?r(t,e):void 0},setUnknownProperty:function(e,t){var o=h(this);if(o.proto===this)return m(this,e,null,t),t;var a=r(this,"content");return Ember.assert(i("Cannot delegate set('%@', %@) to the 'content' property of object proxy %@: its 'content' is undefined.",[e,t,this]),a),n(a,e,t)}})}(),function(){function e(){return 0===s.length?{}:s.pop()}function t(e){return s.push(e),null}function r(e,t){function r(r){var o=n(r,e);return i?t===o:!!o}var i=2===arguments.length;return r}var n=Ember.get,i=Ember.set,o=Array.prototype.slice,a=Ember.EnumerableUtils.indexOf,s=[];Ember.Enumerable=Ember.Mixin.create({nextObject:Ember.required(Function),firstObject:Ember.computed(function(){if(0===n(this,"length"))return void 0;var r,i=e();return r=this.nextObject(0,null,i),t(i),r}).property("[]"),lastObject:Ember.computed(function(){var r=n(this,"length");if(0===r)return void 0;var i,o=e(),a=0,s=null;do s=i,i=this.nextObject(a++,s,o);while(void 0!==i);return t(o),s}).property("[]"),contains:function(e){return void 0!==this.find(function(t){return t===e})},forEach:function(r,i){if("function"!=typeof r)throw new TypeError;var o=n(this,"length"),a=null,s=e();void 0===i&&(i=null);for(var u=0;o>u;u++){var l=this.nextObject(u,a,s);r.call(i,l,u,this),a=l}return a=null,s=t(s),this},getEach:function(e){return this.mapBy(e)},setEach:function(e,t){return this.forEach(function(r){i(r,e,t)})},map:function(e,t){var r=Ember.A();return this.forEach(function(n,i,o){r[i]=e.call(t,n,i,o)}),r},mapBy:function(e){return this.map(function(t){return n(t,e)})},mapProperty:Ember.aliasMethod("mapBy"),filter:function(e,t){var r=Ember.A();return this.forEach(function(n,i,o){e.call(t,n,i,o)&&r.push(n)}),r},reject:function(e,t){return this.filter(function(){return!e.apply(t,arguments)})},filterBy:function(){return this.filter(r.apply(this,arguments))},filterProperty:Ember.aliasMethod("filterBy"),rejectBy:function(e,t){var r=function(r){return n(r,e)===t},i=function(t){return!!n(t,e)},o=2===arguments.length?r:i;return this.reject(o)},rejectProperty:Ember.aliasMethod("rejectBy"),find:function(r,i){var o=n(this,"length");void 0===i&&(i=null);for(var a,s,u=null,l=!1,c=e(),h=0;o>h&&!l;h++)a=this.nextObject(h,u,c),(l=r.call(i,a,h,this))&&(s=a),u=a;return a=u=null,c=t(c),s},findBy:function(){return this.find(r.apply(this,arguments))},findProperty:Ember.aliasMethod("findBy"),every:function(e,t){return!this.find(function(r,n,i){return!e.call(t,r,n,i)})},everyBy:Ember.aliasMethod("isEvery"),everyProperty:Ember.aliasMethod("isEvery"),isEvery:function(){return this.every(r.apply(this,arguments))},any:function(r,i){var o,a,s=n(this,"length"),u=e(),l=!1,c=null;for(void 0===i&&(i=null),a=0;s>a&&!l;a++)o=this.nextObject(a,c,u),l=r.call(i,o,a,this),c=o;return o=c=null,u=t(u),l},some:Ember.aliasMethod("any"),isAny:function(){return this.any(r.apply(this,arguments))},anyBy:Ember.aliasMethod("isAny"),someProperty:Ember.aliasMethod("isAny"),reduce:function(e,t,r){if("function"!=typeof e)throw new TypeError;var n=t;return this.forEach(function(t,i){n=e(n,t,i,this,r)},this),n},invoke:function(e){var t,r=Ember.A();return arguments.length>1&&(t=o.call(arguments,1)),this.forEach(function(n,i){var o=n&&n[e];"function"==typeof o&&(r[i]=t?o.apply(n,t):n[e]())},this),r},toArray:function(){var e=Ember.A();return this.forEach(function(t,r){e[r]=t}),e},compact:function(){return this.filter(function(e){return null!=e})},without:function(e){if(!this.contains(e))return this;var t=Ember.A();return this.forEach(function(r){r!==e&&(t[t.length]=r)}),t},uniq:function(){var e=Ember.A();return this.forEach(function(t){a(e,t)<0&&e.push(t)}),e},"[]":Ember.computed(function(){return this}),addEnumerableObserver:function(e,t){var r=t&&t.willChange||"enumerableWillChange",i=t&&t.didChange||"enumerableDidChange",o=n(this,"hasEnumerableObservers");return o||Ember.propertyWillChange(this,"hasEnumerableObservers"),Ember.addListener(this,"@enumerable:before",e,r),Ember.addListener(this,"@enumerable:change",e,i),o||Ember.propertyDidChange(this,"hasEnumerableObservers"),this},removeEnumerableObserver:function(e,t){var r=t&&t.willChange||"enumerableWillChange",i=t&&t.didChange||"enumerableDidChange",o=n(this,"hasEnumerableObservers");return o&&Ember.propertyWillChange(this,"hasEnumerableObservers"),Ember.removeListener(this,"@enumerable:before",e,r),Ember.removeListener(this,"@enumerable:change",e,i),o&&Ember.propertyDidChange(this,"hasEnumerableObservers"),this},hasEnumerableObservers:Ember.computed(function(){return Ember.hasListeners(this,"@enumerable:change")||Ember.hasListeners(this,"@enumerable:before")}),enumerableContentWillChange:function(e,t){var r,i,o;return r="number"==typeof e?e:e?n(e,"length"):e=-1,i="number"==typeof t?t:t?n(t,"length"):t=-1,o=0>i||0>r||i-r!==0,-1===e&&(e=null),-1===t&&(t=null),Ember.propertyWillChange(this,"[]"),o&&Ember.propertyWillChange(this,"length"),Ember.sendEvent(this,"@enumerable:before",[this,e,t]),this},enumerableContentDidChange:function(e,t){var r,i,o;return r="number"==typeof e?e:e?n(e,"length"):e=-1,i="number"==typeof t?t:t?n(t,"length"):t=-1,o=0>i||0>r||i-r!==0,-1===e&&(e=null),-1===t&&(t=null),Ember.sendEvent(this,"@enumerable:change",[this,e,t]),o&&Ember.propertyDidChange(this,"length"),Ember.propertyDidChange(this,"[]"),this},sortBy:function(){var e=arguments;return this.toArray().sort(function(t,r){for(var i=0;it||t>=e(this,"length")?void 0:e(this,t)},objectsAt:function(e){var t=this;return r(e,function(e){return t.objectAt(e)})},nextObject:function(e){return this.objectAt(e)},"[]":Ember.computed(function(t,r){return void 0!==r&&this.replace(0,e(this,"length"),r),this}),firstObject:Ember.computed(function(){return this.objectAt(0)}),lastObject:Ember.computed(function(){return this.objectAt(e(this,"length")-1)}),contains:function(e){return this.indexOf(e)>=0},slice:function(r,n){var i=Ember.A(),o=e(this,"length");for(t(r)&&(r=0),(t(n)||n>o)&&(n=o),0>r&&(r=o+r),0>n&&(n=o+n);n>r;)i[i.length]=this.objectAt(r++);return i},indexOf:function(t,r){var n,i=e(this,"length");for(void 0===r&&(r=0),0>r&&(r+=i),n=r;i>n;n++)if(this.objectAt(n)===t)return n;return-1},lastIndexOf:function(t,r){var n,i=e(this,"length");for((void 0===r||r>=i)&&(r=i-1),0>r&&(r+=i),n=r;n>=0;n--)if(this.objectAt(n)===t)return n;return-1},addArrayObserver:function(t,r){var n=r&&r.willChange||"arrayWillChange",i=r&&r.didChange||"arrayDidChange",o=e(this,"hasArrayObservers");return o||Ember.propertyWillChange(this,"hasArrayObservers"),Ember.addListener(this,"@array:before",t,n),Ember.addListener(this,"@array:change",t,i),o||Ember.propertyDidChange(this,"hasArrayObservers"),this},removeArrayObserver:function(t,r){var n=r&&r.willChange||"arrayWillChange",i=r&&r.didChange||"arrayDidChange",o=e(this,"hasArrayObservers");return o&&Ember.propertyWillChange(this,"hasArrayObservers"),Ember.removeListener(this,"@array:before",t,n),Ember.removeListener(this,"@array:change",t,i),o&&Ember.propertyDidChange(this,"hasArrayObservers"),this},hasArrayObservers:Ember.computed(function(){return Ember.hasListeners(this,"@array:change")||Ember.hasListeners(this,"@array:before")}),arrayContentWillChange:function(t,r,n){void 0===t?(t=0,r=n=-1):(void 0===r&&(r=-1),void 0===n&&(n=-1)),Ember.isWatching(this,"@each")&&e(this,"@each"),Ember.sendEvent(this,"@array:before",[this,t,r,n]);var i,o;if(t>=0&&r>=0&&e(this,"hasEnumerableObservers")){i=[],o=t+r;for(var a=t;o>a;a++)i.push(this.objectAt(a))}else i=r;return this.enumerableContentWillChange(i,n),this},arrayContentDidChange:function(t,r,i){void 0===t?(t=0,r=i=-1):(void 0===r&&(r=-1),void 0===i&&(i=-1));var o,a;if(t>=0&&i>=0&&e(this,"hasEnumerableObservers")){o=[],a=t+i;for(var s=t;a>s;s++)o.push(this.objectAt(s))}else o=i;this.enumerableContentDidChange(r,o),Ember.sendEvent(this,"@array:change",[this,t,r,i]);var u=e(this,"length"),l=n(this,"firstObject"),c=n(this,"lastObject");return this.objectAt(0)!==l&&(Ember.propertyWillChange(this,"firstObject"),Ember.propertyDidChange(this,"firstObject")),this.objectAt(u-1)!==c&&(Ember.propertyWillChange(this,"lastObject"),Ember.propertyDidChange(this,"lastObject")),this},"@each":Ember.computed(function(){return this.__each||(this.__each=new Ember.EachProxy(this)),this.__each})})}(),function(){function e(e,t){return"@this"===t?e:m(e,t)}function t(e,t,r){this.callbacks=e,this.cp=t,this.instanceMeta=r,this.dependentKeysByGuid={},this.trackedArraysByGuid={},this.suspended=!1,this.changedItems={}}function r(e,t,r){Ember.assert("Internal error: trackedArray is null or undefined",r),this.dependentArray=e,this.index=t,this.item=e.objectAt(t),this.trackedArray=r,this.beforeObserver=null,this.observer=null,this.destroyed=!1}function n(e,t,r){return 0>e?Math.max(0,t+e):t>e?e:Math.min(t-r,e)}function i(e,t,r){return Math.min(r,t-e)}function o(e,t,r,n,i,o){var a={arrayChanged:e,index:r,item:t,propertyName:n,property:i};return o&&(a.previousValues=o),a}function a(e,t,r,n,i){O(e,function(a,s){i.setValue(t.addedItem.call(this,i.getValue(),a,o(e,a,s,n,r),i.sugarMeta))},this)}function s(e,t){{var r;e._callbacks()}e._hasInstanceMeta(this,t)?(r=e._instanceMeta(this,t),r.setValue(e.resetValue(r.getValue()))):r=e._instanceMeta(this,t),e.options.initialize&&e.options.initialize.call(this,r.getValue(),{property:e,propertyName:t},r.sugarMeta)}function u(t,r){if(T.test(r))return!1;var n=e(t,r);return Ember.Array.detect(n)}function l(e,t,r){this.context=e,this.propertyName=t,this.cache=d(e).cache,this.dependentArrays={},this.sugarMeta={},this.initialValue=r}function c(t){var r=this;this.options=t,this._dependentKeys=null,this._itemPropertyKeys={},this._previousItemPropertyKeys={},this.readOnly(),this.cacheable(),this.recomputeOnce=function(e){Ember.run.once(this,n,e)};var n=function(t){var n=(r._dependentKeys,r._instanceMeta(this,t)),i=r._callbacks();s.call(this,r,t),n.dependentArraysObserver.suspendArrayObservers(function(){O(r._dependentKeys,function(t){if(Ember.assert("dependent array "+t+" must be an `Ember.Array`. If you are not extending arrays, you will need to wrap native arrays with `Ember.A`",!(Ember.isArray(e(this,t))&&!Ember.Array.detect(e(this,t)))),u(this,t)){var i=e(this,t),o=n.dependentArrays[t];i===o?r._previousItemPropertyKeys[t]&&(delete r._previousItemPropertyKeys[t],n.dependentArraysObserver.setupPropertyObservers(t,r._itemPropertyKeys[t])):(n.dependentArrays[t]=i,o&&n.dependentArraysObserver.teardownObservers(o,t),i&&n.dependentArraysObserver.setupObservers(i,t))}},this)},this),O(r._dependentKeys,function(o){if(u(this,o)){var s=e(this,o);s&&a.call(this,s,i,r,t,n)}},this)};this.func=function(e){return Ember.assert("Computed reduce values require at least one dependent key",r._dependentKeys),n.call(this,e),r._instanceMeta(this,e).getValue()}}function h(e){return e}var m=Ember.get,p=(Ember.set,Ember.guidFor),d=Ember.meta,f=Ember.propertyWillChange,b=Ember.propertyDidChange,E=Ember.addBeforeObserver,v=Ember.removeBeforeObserver,g=Ember.addObserver,y=Ember.removeObserver,w=Ember.ComputedProperty,_=[].slice,C=Ember.create,O=Ember.EnumerableUtils.forEach,A=(Ember.cacheFor.set,Ember.cacheFor.get,Ember.cacheFor.remove,/^(.*)\.@each\.(.*)/),P=/(.*\.@each){2,}/,T=/\.\[\]$/,x=Ember.expandProperties;t.prototype={setValue:function(e){this.instanceMeta.setValue(e,!0)},getValue:function(){return this.instanceMeta.getValue()},setupObservers:function(e,t){this.dependentKeysByGuid[p(e)]=t,e.addArrayObserver(this,{willChange:"dependentArrayWillChange",didChange:"dependentArrayDidChange"}),this.cp._itemPropertyKeys[t]&&this.setupPropertyObservers(t,this.cp._itemPropertyKeys[t])},teardownObservers:function(e,t){var r=this.cp._itemPropertyKeys[t]||[];delete this.dependentKeysByGuid[p(e)],this.teardownPropertyObservers(t,r),e.removeArrayObserver(this,{willChange:"dependentArrayWillChange",didChange:"dependentArrayDidChange"})},suspendArrayObservers:function(e,t){var r=this.suspended;this.suspended=!0,e.call(t),this.suspended=r},setupPropertyObservers:function(t,r){var n=e(this.instanceMeta.context,t),i=e(n,"length"),o=new Array(i);this.resetTransformations(t,o),O(n,function(e,i){var a=this.createPropertyObserverContext(n,i,this.trackedArraysByGuid[t]);o[i]=a,O(r,function(t){E(e,t,this,a.beforeObserver),g(e,t,this,a.observer)},this)},this)},teardownPropertyObservers:function(e,t){var r,n,i,o=this,a=this.trackedArraysByGuid[e];a&&a.apply(function(e,a,s){s!==Ember.TrackedArray.DELETE&&O(e,function(e){e.destroyed=!0,r=e.beforeObserver,n=e.observer,i=e.item,O(t,function(e){v(i,e,o,r),y(i,e,o,n)})})})},createPropertyObserverContext:function(e,t,n){var i=new r(e,t,n);return this.createPropertyObserver(i),i},createPropertyObserver:function(e){var t=this;e.beforeObserver=function(r,n){return t.itemPropertyWillChange(r,n,e.dependentArray,e)},e.observer=function(r,n){return t.itemPropertyDidChange(r,n,e.dependentArray,e)}},resetTransformations:function(e,t){this.trackedArraysByGuid[e]=new Ember.TrackedArray(t)},trackAdd:function(e,t,r){var n=this.trackedArraysByGuid[e];n&&n.addItems(t,r)},trackRemove:function(e,t,r){var n=this.trackedArraysByGuid[e];return n?n.removeItems(t,r):[]},updateIndexes:function(t,r){var n=e(r,"length");t.apply(function(e,t,r){r!==Ember.TrackedArray.DELETE&&(r!==Ember.TrackedArray.RETAIN||e.length!==n||0!==t)&&O(e,function(e,r){e.index=r+t})})},dependentArrayWillChange:function(t,r,a){function s(e){m[h].destroyed=!0,v(l,e,this,m[h].beforeObserver),y(l,e,this,m[h].observer)}if(!this.suspended){var u,l,c,h,m,d=this.callbacks.removedItem,f=p(t),b=this.dependentKeysByGuid[f],E=this.cp._itemPropertyKeys[b]||[],g=e(t,"length"),w=n(r,g,0),_=i(w,g,a);for(m=this.trackRemove(b,w,_),h=_-1;h>=0&&(c=w+h,!(c>=g));--h)l=t.objectAt(c),O(E,s,this),u=o(t,l,c,this.instanceMeta.propertyName,this.cp),this.setValue(d.call(this.instanceMeta.context,this.getValue(),l,u,this.instanceMeta.sugarMeta)) +}},dependentArrayDidChange:function(t,r,i,a){if(!this.suspended){var s,u,l=this.callbacks.addedItem,c=p(t),h=this.dependentKeysByGuid[c],m=new Array(a),d=this.cp._itemPropertyKeys[h],f=e(t,"length"),b=n(r,f,a);O(t.slice(b,b+a),function(e,r){d&&(u=m[r]=this.createPropertyObserverContext(t,b+r,this.trackedArraysByGuid[h]),O(d,function(t){E(e,t,this,u.beforeObserver),g(e,t,this,u.observer)},this)),s=o(t,e,b+r,this.instanceMeta.propertyName,this.cp),this.setValue(l.call(this.instanceMeta.context,this.getValue(),e,s,this.instanceMeta.sugarMeta))},this),this.trackAdd(h,b,m)}},itemPropertyWillChange:function(t,r,n,i){var o=p(t);this.changedItems[o]||(this.changedItems[o]={array:n,observerContext:i,obj:t,previousValues:{}}),this.changedItems[o].previousValues[r]=e(t,r)},itemPropertyDidChange:function(){this.flushChanges()},flushChanges:function(){var e,t,r,n=this.changedItems;for(e in n)t=n[e],t.observerContext.destroyed||(this.updateIndexes(t.observerContext.trackedArray,t.observerContext.dependentArray),r=o(t.array,t.obj,t.observerContext.index,this.instanceMeta.propertyName,this.cp,t.previousValues),this.setValue(this.callbacks.removedItem.call(this.instanceMeta.context,this.getValue(),t.obj,r,this.instanceMeta.sugarMeta)),this.setValue(this.callbacks.addedItem.call(this.instanceMeta.context,this.getValue(),t.obj,r,this.instanceMeta.sugarMeta)));this.changedItems={}}},l.prototype={getValue:function(){return this.propertyName in this.cache?this.cache[this.propertyName]:this.initialValue},setValue:function(e,t){e!==this.cache[this.propertyName]&&(t&&f(this.context,this.propertyName),void 0===e?delete this.cache[this.propertyName]:this.cache[this.propertyName]=e,t&&b(this.context,this.propertyName))}},Ember.ReduceComputedProperty=c,c.prototype=C(w.prototype),c.prototype._callbacks=function(){if(!this.callbacks){var e=this.options;this.callbacks={removedItem:e.removedItem||h,addedItem:e.addedItem||h}}return this.callbacks},c.prototype._hasInstanceMeta=function(e,t){return!!d(e).cacheMeta[t]},c.prototype._instanceMeta=function(e,r){var n=d(e).cacheMeta,i=n[r];return i||(i=n[r]=new l(e,r,this.initialValue()),i.dependentArraysObserver=new t(this._callbacks(),this,i,e,r,i.sugarMeta)),i},c.prototype.initialValue=function(){return"function"==typeof this.options.initialValue?this.options.initialValue():this.options.initialValue},c.prototype.resetValue=function(){return this.initialValue()},c.prototype.itemPropertyKey=function(e,t){this._itemPropertyKeys[e]=this._itemPropertyKeys[e]||[],this._itemPropertyKeys[e].push(t)},c.prototype.clearItemPropertyKeys=function(e){this._itemPropertyKeys[e]&&(this._previousItemPropertyKeys[e]=this._itemPropertyKeys[e],this._itemPropertyKeys[e]=[])},c.prototype.property=function(){var e,t,r=this,n=_.call(arguments),i=new Ember.Set;return O(n,function(n){if(P.test(n))throw new Ember.Error("Nested @each properties not supported: "+n);if(e=A.exec(n)){t=e[1];var o=e[2],a=function(e){r.itemPropertyKey(t,e)};x(o,a),i.add(t)}else i.add(n)}),w.prototype.property.apply(this,i.toArray())},Ember.reduceComputed=function(e){var t;if(arguments.length>1&&(t=_.call(arguments,0,-1),e=_.call(arguments,-1)[0]),"object"!=typeof e)throw new Ember.Error("Reduce Computed Property declared without an options hash");if(!("initialValue"in e))throw new Ember.Error("Reduce Computed Property declared without an initial value");var r=new c(e);return t&&r.property.apply(r,t),r}}(),function(){function e(){var e=this;return t.apply(this,arguments),this.func=function(t){return function(r){return e._hasInstanceMeta(this,r)||i(e._dependentKeys,function(t){Ember.addObserver(this,t,function(){e.recomputeOnce.call(this,r)})},this),t.apply(this,arguments)}}(this.func),this}var t=Ember.ReduceComputedProperty,r=[].slice,n=Ember.create,i=Ember.EnumerableUtils.forEach;Ember.ArrayComputedProperty=e,e.prototype=n(t.prototype),e.prototype.initialValue=function(){return Ember.A()},e.prototype.resetValue=function(e){return e.clear(),e},e.prototype.didChange=function(){},Ember.arrayComputed=function(t){var n;if(arguments.length>1&&(n=r.call(arguments,0,-1),t=r.call(arguments,-1)[0]),"object"!=typeof t)throw new Ember.Error("Array Computed Property declared without an options hash");var i=new e(t);return n&&i.property.apply(i,n),i}}(),function(){function e(e,i,o,a){function s(e){return n(t.detectInstance(e)?r(e,"content"):e)}var u,l,c,h,m;return arguments.length<4&&(a=r(e,"length")),arguments.length<3&&(o=0),o===a?o:(u=o+Math.floor((a-o)/2),l=e.objectAt(u),h=s(l),m=s(i),h===m?u:(c=this.order(l,i),0===c&&(c=m>h?-1:1),0>c?this.binarySearch(e,i,u+1,a):c>0?this.binarySearch(e,i,o,u):u))}var t,r=Ember.get,n=(Ember.set,Ember.guidFor),i=Ember.merge,o=[].slice,a=Ember.EnumerableUtils.forEach,s=Ember.EnumerableUtils.map;Ember.computed.sum=function(e){return Ember.reduceComputed(e,{initialValue:0,addedItem:function(e,t){return e+t},removedItem:function(e,t){return e-t}})},Ember.computed.max=function(e){return Ember.reduceComputed(e,{initialValue:-1/0,addedItem:function(e,t){return Math.max(e,t)},removedItem:function(e,t){return e>t?e:void 0}})},Ember.computed.min=function(e){return Ember.reduceComputed(e,{initialValue:1/0,addedItem:function(e,t){return Math.min(e,t)},removedItem:function(e,t){return t>e?e:void 0}})},Ember.computed.map=function(e,t){var r={addedItem:function(e,r,n){var i=t.call(this,r);return e.insertAt(n.index,i),e},removedItem:function(e,t,r){return e.removeAt(r.index,1),e}};return Ember.arrayComputed(e,r)},Ember.computed.mapBy=function(e,t){var n=function(e){return r(e,t)};return Ember.computed.map(e+".@each."+t,n)},Ember.computed.mapProperty=Ember.computed.mapBy,Ember.computed.filter=function(e,t){var r={initialize:function(e,t,r){r.filteredArrayIndexes=new Ember.SubArray},addedItem:function(e,r,n,i){var o=!!t.call(this,r),a=i.filteredArrayIndexes.addItem(n.index,o);return o&&e.insertAt(a,r),e},removedItem:function(e,t,r,n){var i=n.filteredArrayIndexes.removeItem(r.index);return i>-1&&e.removeAt(i),e}};return Ember.arrayComputed(e,r)},Ember.computed.filterBy=function(e,t,n){var i;return i=2===arguments.length?function(e){return r(e,t)}:function(e){return r(e,t)===n},Ember.computed.filter(e+".@each."+t,i)},Ember.computed.filterProperty=Ember.computed.filterBy,Ember.computed.uniq=function(){var e=o.call(arguments);return e.push({initialize:function(e,t,r){r.itemCounts={}},addedItem:function(e,t,r,i){var o=n(t);return i.itemCounts[o]?++i.itemCounts[o]:i.itemCounts[o]=1,e.addObject(t),e},removedItem:function(e,t,r,i){var o=n(t),a=i.itemCounts;return 0===--a[o]&&e.removeObject(t),e}}),Ember.arrayComputed.apply(null,e)},Ember.computed.union=Ember.computed.uniq,Ember.computed.intersect=function(){var e=function(e){return s(e.property._dependentKeys,function(e){return n(e)})},t=o.call(arguments);return t.push({initialize:function(e,t,r){r.itemCounts={}},addedItem:function(t,r,i,o){var a=n(r),s=(e(i),n(i.arrayChanged)),u=i.property._dependentKeys.length,l=o.itemCounts;return l[a]||(l[a]={}),void 0===l[a][s]&&(l[a][s]=0),1===++l[a][s]&&u===Ember.keys(l[a]).length&&t.addObject(r),t},removedItem:function(t,r,i,o){var a,s=n(r),u=(e(i),n(i.arrayChanged)),l=(i.property._dependentKeys.length,o.itemCounts);return void 0===l[s][u]&&(l[s][u]=0),0===--l[s][u]&&(delete l[s][u],a=Ember.keys(l[s]).length,0===a&&delete l[s],t.removeObject(r)),t}}),Ember.arrayComputed.apply(null,t)},Ember.computed.setDiff=function(e,t){if(2!==arguments.length)throw new Ember.Error("setDiff requires exactly two dependent arrays.");return Ember.arrayComputed(e,t,{addedItem:function(n,i,o){var a=r(this,e),s=r(this,t);return o.arrayChanged===a?s.contains(i)||n.addObject(i):n.removeObject(i),n},removedItem:function(n,i,o){var a=r(this,e),s=r(this,t);return o.arrayChanged===s?a.contains(i)&&n.addObject(i):n.removeObject(i),n}})},t=Ember.ObjectProxy.extend(),Ember.computed.sort=function(n,o){Ember.assert("Ember.computed.sort requires two arguments: an array key to sort and either a sort properties key or sort function",2===arguments.length);var s,u;return"function"==typeof o?s=function(t,r,n){n.order=o,n.binarySearch=e}:(u=o,s=function(i,o,s){function l(){var e,t,i,l=r(this,u),h=s.sortProperties=[],m=s.sortPropertyAscending={};Ember.assert("Cannot sort: '"+u+"' is not an array.",Ember.isArray(l)),o.property.clearItemPropertyKeys(n),a(l,function(r){-1!==(t=r.indexOf(":"))?(e=r.substring(0,t),i="desc"!==r.substring(t+1).toLowerCase()):(e=r,i=!0),h.push(e),m[e]=i,o.property.itemPropertyKey(n,e)}),l.addObserver("@each",this,c)}function c(){Ember.run.once(this,h,o.propertyName)}function h(e){l.call(this),o.property.recomputeOnce.call(this,e)}Ember.addObserver(this,u,c),l.call(this),s.order=function(e,n){for(var i,o,a,s=n instanceof t,u=0;ue;e++){var r=arguments[e];Ember.assert("Immediate observers must observe internal properties only, not properties on other objects.",-1===r.indexOf("."))}return this.observes.apply(this,arguments)},Function.prototype.observesBefore=function(){for(var e=function(e){r.push(e)},r=[],n=0;nr(this,"length"))throw new Ember.Error(e);return this.replace(t,0,[n]),this},removeAt:function(n,i){if("number"==typeof n){if(0>n||n>=r(this,"length"))throw new Ember.Error(e);void 0===i&&(i=1),this.replace(n,i,t)}return this},pushObject:function(e){return this.insertAt(r(this,"length"),e),e},pushObjects:function(e){if(!Ember.Enumerable.detect(e)&&!Ember.isArray(e))throw new TypeError("Must pass Ember.Enumerable to Ember.MutableArray#pushObjects");return this.replace(r(this,"length"),0,e),this},popObject:function(){var e=r(this,"length");if(0===e)return null;var t=this.objectAt(e-1);return this.removeAt(e-1,1),t},shiftObject:function(){if(0===r(this,"length"))return null;var e=this.objectAt(0);return this.removeAt(0),e},unshiftObject:function(e){return this.insertAt(0,e),e},unshiftObjects:function(e){return this.replace(0,0,e),this},reverseObjects:function(){var e=r(this,"length");if(0===e)return this;var t=this.toArray().reverse();return this.replace(0,e,t),this},setObjects:function(e){if(0===e.length)return this.clear();var t=r(this,"length");return this.replace(0,t,e),this},removeObject:function(e){for(var t=r(this,"length")||0;--t>=0;){var n=this.objectAt(t);n===e&&this.removeAt(t)}return this},addObject:function(e){return this.contains(e)||this.pushObject(e),this}})}(),function(){{var e=Ember.get;Ember.set}Ember.TargetActionSupport=Ember.Mixin.create({target:null,action:null,actionContext:null,targetObject:Ember.computed(function(){var t=e(this,"target");if("string"===Ember.typeOf(t)){var r=e(this,t);return void 0===r&&(r=e(Ember.lookup,t)),r}return t}).property("target"),actionContextObject:Ember.computed(function(){var t=e(this,"actionContext");if("string"===Ember.typeOf(t)){var r=e(this,t);return void 0===r&&(r=e(Ember.lookup,t)),r}return t}).property("actionContext"),triggerAction:function(t){function r(e,t){var r=[];return t&&r.push(t),r.concat(e)}t=t||{};var n=t.action||e(this,"action"),i=t.target||e(this,"targetObject"),o=t.actionContext;if("undefined"==typeof o&&(o=e(this,"actionContextObject")||this),i&&n){var a;return i.send?a=i.send.apply(i,r(o,n)):(Ember.assert("The action '"+n+"' did not exist on "+i,"function"==typeof i[n]),a=i[n].apply(i,r(o))),a!==!1&&(a=!0),a}return!1}})}(),function(){Ember.Evented=Ember.Mixin.create({on:function(e,t,r){return Ember.addListener(this,e,t,r),this},one:function(e,t,r){return r||(r=t,t=null),Ember.addListener(this,e,t,r,!0),this},trigger:function(e){var t,r,n=[];for(t=1,r=arguments.length;r>t;t++)n.push(arguments[t]);Ember.sendEvent(this,e,n)},off:function(e,t,r){return Ember.removeListener(this,e,t,r),this},has:function(e){return Ember.hasListeners(this,e)}})}(),function(){var e=t("rsvp");if(Ember.FEATURES["ember-runtime-test-friendly-promises"]){var r=function(){Ember.Test&&Ember.Test.adapter&&Ember.Test.adapter.asyncStart()},n=function(){Ember.Test&&Ember.Test.adapter&&Ember.Test.adapter.asyncEnd()};e.configure("async",function(e,t){var i=!Ember.run.currentRunLoop;Ember.testing&&i&&r(),Ember.run.backburner.schedule("actions",function(){Ember.testing&&i&&n(),e(t)})})}else e.configure("async",function(e,t){Ember.run.backburner.schedule("actions",function(){e(t)})});e.Promise.prototype.fail=function(e,t){return Ember.deprecate("RSVP.Promise.fail has been renamed as RSVP.Promise.catch"),this["catch"](e,t)};var i=Ember.get;Ember.DeferredMixin=Ember.Mixin.create({then:function(e,t,r){function n(t){return e(t===a?s:t)}var o,a,s;return s=this,o=i(this,"_deferred"),a=o.promise,a.then(e&&n,t,r)},resolve:function(e){var t,r;t=i(this,"_deferred"),r=t.promise,t.resolve(e===this?r:e)},reject:function(e){i(this,"_deferred").reject(e)},_deferred:Ember.computed(function(){return e.defer("Ember: DeferredMixin - "+this)})})}(),function(){var e=Ember.get,t=Ember.typeOf;Ember.ActionHandler=Ember.Mixin.create({mergedProperties:["_actions"],willMergeMixin:function(e){var r;e._actions||(Ember.assert("'actions' should not be a function","function"!=typeof e.actions),"object"===t(e.actions)?r="actions":"object"===t(e.events)&&(Ember.deprecate("Action handlers contained in an `events` object are deprecated in favor of putting them in an `actions` object",!1),r="events"),r&&(e._actions=Ember.merge(e._actions||{},e[r])),delete e[r])},send:function(t){var r,n=[].slice.call(arguments,1);if(this._actions&&this._actions[t]){if(this._actions[t].apply(this,n)!==!0)return}else if(!Ember.FEATURES.isEnabled("ember-routing-drop-deprecated-action-style")&&this.deprecatedSend&&this.deprecatedSendHandles&&this.deprecatedSendHandles(t)&&(Ember.warn("The current default is deprecated but will prefer to handle actions directly on the controller instead of a similarly named action in the actions hash. To turn off this deprecated feature set: Ember.FEATURES['ember-routing-drop-deprecated-action-style'] = true"),this.deprecatedSend.apply(this,[].slice.call(arguments))!==!0))return;(r=e(this,"target"))&&(Ember.assert("The `target` for "+this+" ("+r+") does not have a `send` method","function"==typeof r.send),r.send.apply(r,arguments))}})}(),function(){function e(e,t){return r(e,"isFulfilled",!1),r(e,"isRejected",!1),t.then(function(t){return r(e,"isFulfilled",!0),r(e,"content",t),t},function(t){throw r(e,"isRejected",!0),r(e,"reason",t),t},"Ember: PromiseProxy")}function t(e){return function(){var t=n(this,"promise");return t[e].apply(t,arguments)}}var r=Ember.set,n=Ember.get,i=Ember.computed.not,o=Ember.computed.or;Ember.PromiseProxyMixin=Ember.Mixin.create({reason:null,isPending:i("isSettled").readOnly(),isSettled:o("isRejected","isFulfilled").readOnly(),isRejected:!1,isFulfilled:!1,promise:Ember.computed(function(t,r){if(2===arguments.length)return e(this,r);throw new Ember.Error("PromiseProxy's promise must be set")}),then:t("then"),"catch":t("catch"),"finally":t("finally")})}(),function(){function e(e,t,r){this.type=e,this.count=t,this.items=r}function t(e,t,r,n){this.operation=e,this.index=t,this.split=r,this.rangeStart=n}var r=Ember.get,n=Ember.EnumerableUtils.forEach,i="r",o="i",a="d";Ember.TrackedArray=function(t){arguments.length<1&&(t=[]);var n=r(t,"length");this._operations=n?[new e(i,n,t)]:[]},Ember.TrackedArray.RETAIN=i,Ember.TrackedArray.INSERT=o,Ember.TrackedArray.DELETE=a,Ember.TrackedArray.prototype={addItems:function(t,n){var i=r(n,"length");if(!(1>i)){var a,s,u=this._findArrayOperation(t),l=u.operation,c=u.index,h=u.rangeStart;s=new e(o,i,n),l?u.split?(this._split(c,t-h,s),a=c+1):(this._operations.splice(c,0,s),a=c):(this._operations.push(s),a=c),this._composeInsert(a)}},removeItems:function(t,r){if(!(1>r)){var n,i,o=this._findArrayOperation(t),s=(o.operation,o.index),u=o.rangeStart;return n=new e(a,r),o.split?(this._split(s,t-u,n),i=s+1):(this._operations.splice(s,0,n),i=s),this._composeDelete(i)}},apply:function(t){var r=[],o=0;n(this._operations,function(e){t(e.items,o,e.type),e.type!==a&&(o+=e.count,r=r.concat(e.items))}),this._operations=[new e(i,r.length,r)]},_findArrayOperation:function(e){var r,n,i,o,s,u=!1;for(r=o=0,n=this._operations.length;n>r;++r)if(i=this._operations[r],i.type!==a){if(s=o+i.count-1,e===o)break;if(e>o&&s>=e){u=!0;break}o=s+1}return new t(i,r,u,o)},_split:function(t,r,n){var i=this._operations[t],o=i.items.slice(r),a=new e(i.type,o.length,o);i.count=r,i.items=i.items.slice(0,r),this._operations.splice(t+1,0,n,a)},_composeInsert:function(e){var t=this._operations[e],r=this._operations[e-1],n=this._operations[e+1],i=r&&r.type,a=n&&n.type;i===o?(r.count+=t.count,r.items=r.items.concat(t.items),a===o?(r.count+=n.count,r.items=r.items.concat(n.items),this._operations.splice(e,2)):this._operations.splice(e,1)):a===o&&(t.count+=n.count,t.items=t.items.concat(n.items),this._operations.splice(e+1,1))},_composeDelete:function(e){var t,r,n,i=this._operations[e],s=i.count,u=this._operations[e-1],l=u&&u.type,c=!1,h=[];l===a&&(i=u,e-=1);for(var m=e+1;s>0;++m)t=this._operations[m],r=t.type,n=t.count,r!==a?(n>s?(h=h.concat(t.items.splice(0,s)),t.count-=s,m-=1,n=s,s=0):(n===s&&(c=!0),h=h.concat(t.items),s-=n),r===o&&(i.count-=n)):i.count+=n;return i.count>0?this._operations.splice(e+1,m-1-e):this._operations.splice(e,c?2:1),h},toString:function(){var e="";return n(this._operations,function(t){e+=" "+t.type+":"+t.count}),e.substring(1)}}}(),function(){function e(e,t){this.type=e,this.count=t}var t=(Ember.get,Ember.EnumerableUtils.forEach),r="r",n="f";Ember.SubArray=function(t){arguments.length<1&&(t=0),this._operations=t>0?[new e(r,t)]:[]},Ember.SubArray.prototype={addItem:function(t,i){var o=-1,a=i?r:n,s=this;return this._findOperation(t,function(n,u,l,c,h){var m,p;a===n.type?++n.count:t===l?s._operations.splice(u,0,new e(a,1)):(m=new e(a,1),p=new e(n.type,c-t+1),n.count=t-l,s._operations.splice(u+1,0,m,p)),i&&(o=n.type===r?h+(t-l):h),s._composeAt(u)},function(t){s._operations.push(new e(a,1)),i&&(o=t),s._composeAt(s._operations.length-1)}),o},removeItem:function(e){var t=-1,n=this;return this._findOperation(e,function(i,o,a,s,u){i.type===r&&(t=u+(e-a)),i.count>1?--i.count:(n._operations.splice(o,1),n._composeAt(o))},function(){throw new Ember.Error("Can't remove an item that has never been added.")}),t},_findOperation:function(e,t,n){var i,o,a,s,u,l=0;for(i=s=0,o=this._operations.length;o>i;s=u+1,++i){if(a=this._operations[i],u=s+a.count-1,e>=s&&u>=e)return void t(a,i,s,u,l);a.type===r&&(l+=a.count)}n(l)},_composeAt:function(e){var t,r=this._operations[e];r&&(e>0&&(t=this._operations[e-1],t.type===r.type&&(r.count+=t.count,this._operations.splice(e-1,1),--e)),er(this,"content.length"))throw new Ember.Error(e);return this._replace(t,0,[n]),this},insertAt:function(e,t){if(r(this,"arrangedContent")===r(this,"content"))return this._insertAt(e,t);throw new Ember.Error("Using insertAt on an arranged ArrayProxy is not allowed.")},removeAt:function(n,i){if("number"==typeof n){var o,a=r(this,"content"),s=r(this,"arrangedContent"),u=[];if(0>n||n>=r(this,"length"))throw new Ember.Error(e);for(void 0===i&&(i=1),o=n;n+i>o;o++)u.push(a.indexOf(s.objectAt(o)));for(u.sort(function(e,t){return t-e}),Ember.beginPropertyChanges(),o=0;o=i;){var u=e.objectAt(o);u&&(Ember.assert("When using @each to observe the array "+e+", the array must return an object","instance"===Ember.typeOf(u)||"object"===Ember.typeOf(u)),Ember.addBeforeObserver(u,t,r,"contentKeyWillChange"),Ember.addObserver(u,t,r,"contentKeyDidChange"),a=n(u),s[a]||(s[a]=[]),s[a].push(o))}}function t(e,t,r,i,a){var s=r._objects;s||(s=r._objects={});for(var u,l;--a>=i;){var c=e.objectAt(a);c&&(Ember.removeBeforeObserver(c,t,r,"contentKeyWillChange"),Ember.removeObserver(c,t,r,"contentKeyDidChange"),l=n(c),u=s[l],u[o.call(u,a)]=null)}}var r=(Ember.set,Ember.get),n=Ember.guidFor,i=Ember.EnumerableUtils.forEach,o=Ember.ArrayPolyfills.indexOf,a=Ember.Object.extend(Ember.Array,{init:function(e,t,r){this._super(),this._keyName=t,this._owner=r,this._content=e},objectAt:function(e){var t=this._content.objectAt(e);return t&&r(t,this._keyName)},length:Ember.computed(function(){var e=this._content;return e?r(e,"length"):0})}),s=/^.+:(before|change)$/;Ember.EachProxy=Ember.Object.extend({init:function(e){this._super(),this._content=e,e.addArrayObserver(this),i(Ember.watchedEvents(this),function(e){this.didAddListener(e)},this)},unknownProperty:function(e){var t;return t=new a(this._content,e,this),Ember.defineProperty(this,e,null,t),this.beginObservingContentKey(e),t},arrayWillChange:function(e,r,n){var i,o,a=this._keys;o=n>0?r+n:-1,Ember.beginPropertyChanges(this);for(i in a)a.hasOwnProperty(i)&&(o>0&&t(e,i,this,r,o),Ember.propertyWillChange(this,i));Ember.propertyWillChange(this._content,"@each"),Ember.endPropertyChanges(this)},arrayDidChange:function(t,r,n,i){var o,a=this._keys;o=i>0?r+i:-1,Ember.changeProperties(function(){for(var n in a)a.hasOwnProperty(n)&&(o>0&&e(t,n,this,r,o),Ember.propertyDidChange(this,n));Ember.propertyDidChange(this._content,"@each")},this)},didAddListener:function(e){s.test(e)&&this.beginObservingContentKey(e.slice(0,-7))},didRemoveListener:function(e){s.test(e)&&this.stopObservingContentKey(e.slice(0,-7))},beginObservingContentKey:function(t){var n=this._keys;if(n||(n=this._keys={}),n[t])n[t]++;else{n[t]=1;var i=this._content,o=r(i,"length");e(i,t,this,0,o)}},stopObservingContentKey:function(e){var n=this._keys;if(n&&n[e]>0&&--n[e]<=0){var i=this._content,o=r(i,"length");t(i,e,this,0,o)}},contentKeyWillChange:function(e,t){Ember.propertyWillChange(this,t)},contentKeyDidChange:function(e,t){Ember.propertyDidChange(this,t)}})}(),function(){var e=Ember.get,t=(Ember.set,Ember.EnumerableUtils._replace),r=Ember.Mixin.create(Ember.MutableArray,Ember.Observable,Ember.Copyable,{get:function(e){return"length"===e?this.length:"number"==typeof e?this[e]:this._super(e)},objectAt:function(e){return this[e]},replace:function(r,n,i){if(this.isFrozen)throw Ember.FROZEN_ERROR;var o=i?e(i,"length"):0;return this.arrayContentWillChange(r,n,o),0===o?this.splice(r,n):t(this,r,n,i),this.arrayContentDidChange(r,n,o),this},unknownProperty:function(e,t){var r;return void 0!==t&&void 0===r&&(r=this[e]=t),r},indexOf:function(e,t){var r,n=this.length;for(t=void 0===t?0:0>t?Math.ceil(t):Math.floor(t),0>t&&(t+=n),r=t;n>r;r++)if(this[r]===e)return r;return-1},lastIndexOf:function(e,t){var r,n=this.length;for(t=void 0===t?n-1:0>t?Math.ceil(t):Math.floor(t),0>t&&(t+=n),r=t;r>=0;r--)if(this[r]===e)return r;return-1},copy:function(e){return e?this.map(function(e){return Ember.copy(e,!0)}):this.slice()}}),n=["length"];Ember.EnumerableUtils.forEach(r.keys(),function(e){Array.prototype[e]&&n.push(e)}),n.length>0&&(r=r.without.apply(r,n)),Ember.NativeArray=r,Ember.A=function(e){return void 0===e&&(e=[]),Ember.Array.detect(e)?e:Ember.NativeArray.apply(e)},Ember.NativeArray.activate=function(){r.apply(Array.prototype),Ember.A=function(e){return e||[]}},(Ember.EXTEND_PROTOTYPES===!0||Ember.EXTEND_PROTOTYPES.Array)&&Ember.NativeArray.activate()}(),function(){var e=Ember.get,t=Ember.set,r=Ember.guidFor,n=Ember.isNone,i=Ember.String.fmt;Ember.Set=Ember.CoreObject.extend(Ember.MutableEnumerable,Ember.Copyable,Ember.Freezable,{length:0,clear:function(){if(this.isFrozen)throw new Ember.Error(Ember.FROZEN_ERROR);var n=e(this,"length");if(0===n)return this;var i;this.enumerableContentWillChange(n,0),Ember.propertyWillChange(this,"firstObject"),Ember.propertyWillChange(this,"lastObject");for(var o=0;n>o;o++)i=r(this[o]),delete this[i],delete this[o];return t(this,"length",0),Ember.propertyDidChange(this,"firstObject"),Ember.propertyDidChange(this,"lastObject"),this.enumerableContentDidChange(n,0),this},isEqual:function(t){if(!Ember.Enumerable.detect(t))return!1;var r=e(this,"length");if(e(t,"length")!==r)return!1;for(;--r>=0;)if(!t.contains(this[r]))return!1;return!0},add:Ember.aliasMethod("addObject"),remove:Ember.aliasMethod("removeObject"),pop:function(){if(e(this,"isFrozen"))throw new Ember.Error(Ember.FROZEN_ERROR);var t=this.length>0?this[this.length-1]:null;return this.remove(t),t},push:Ember.aliasMethod("addObject"),shift:Ember.aliasMethod("pop"),unshift:Ember.aliasMethod("push"),addEach:Ember.aliasMethod("addObjects"),removeEach:Ember.aliasMethod("removeObjects"),init:function(e){this._super(),e&&this.addObjects(e)},nextObject:function(e){return this[e]},firstObject:Ember.computed(function(){return this.length>0?this[0]:void 0}),lastObject:Ember.computed(function(){return this.length>0?this[this.length-1]:void 0}),addObject:function(i){if(e(this,"isFrozen"))throw new Ember.Error(Ember.FROZEN_ERROR);if(n(i))return this;var o,a=r(i),s=this[a],u=e(this,"length");return s>=0&&u>s&&this[s]===i?this:(o=[i],this.enumerableContentWillChange(null,o),Ember.propertyWillChange(this,"lastObject"),u=e(this,"length"),this[a]=u,this[u]=i,t(this,"length",u+1),Ember.propertyDidChange(this,"lastObject"),this.enumerableContentDidChange(null,o),this)},removeObject:function(i){if(e(this,"isFrozen"))throw new Ember.Error(Ember.FROZEN_ERROR);if(n(i))return this;var o,a,s=r(i),u=this[s],l=e(this,"length"),c=0===u,h=u===l-1;return u>=0&&l>u&&this[u]===i&&(a=[i],this.enumerableContentWillChange(a,null),c&&Ember.propertyWillChange(this,"firstObject"),h&&Ember.propertyWillChange(this,"lastObject"),l-1>u&&(o=this[l-1],this[u]=o,this[r(o)]=u),delete this[s],delete this[l-1],t(this,"length",l-1),c&&Ember.propertyDidChange(this,"firstObject"),h&&Ember.propertyDidChange(this,"lastObject"),this.enumerableContentDidChange(a,null)),this},contains:function(e){return this[r(e)]>=0},copy:function(){var n=this.constructor,i=new n,o=e(this,"length");for(t(i,"length",o);--o>=0;)i[o]=this[o],i[r(this[o])]=o;return i},toString:function(){var e,t=this.length,r=[];for(e=0;t>e;e++)r[e]=this[e];return i("Ember.Set<%@>",[r.join(",")])}})}(),function(){var e=Ember.DeferredMixin,t=(Ember.get,Ember.Object.extend(e)); +t.reopenClass({promise:function(e,r){var n=t.create();return e.call(r,n),n}}),Ember.Deferred=t}(),function(){var e=Ember.ArrayPolyfills.forEach,t=Ember.ENV.EMBER_LOAD_HOOKS||{},r={};Ember.onLoad=function(e,n){var i;t[e]=t[e]||Ember.A(),t[e].pushObject(n),(i=r[e])&&n(i)},Ember.runLoadHooks=function(n,i){if(r[n]=i,"object"==typeof window&&"function"==typeof window.dispatchEvent&&"function"==typeof CustomEvent){var o=new CustomEvent(n,{detail:i,name:n});window.dispatchEvent(o)}t[n]&&e.call(t[n],function(e){e(i)})}}(),function(){Ember.get;Ember.ControllerMixin=Ember.Mixin.create(Ember.ActionHandler,{isController:!0,target:null,container:null,parentController:null,store:null,model:Ember.computed.alias("content"),deprecatedSendHandles:function(e){return!!this[e]},deprecatedSend:function(e){var t=[].slice.call(arguments,1);Ember.assert(""+this+" has the action "+e+" but it is not a function","function"==typeof this[e]),Ember.deprecate("Action handlers implemented directly on controllers are deprecated in favor of action handlers on an `actions` object ( action: `"+e+"` on "+this+")",!1),this[e].apply(this,t)}}),Ember.Controller=Ember.Object.extend(Ember.ControllerMixin)}(),function(){var e=Ember.get,t=(Ember.set,Ember.EnumerableUtils.forEach);Ember.SortableMixin=Ember.Mixin.create(Ember.MutableEnumerable,{sortProperties:null,sortAscending:!0,sortFunction:Ember.compare,orderBy:function(r,n){var i=0,o=e(this,"sortProperties"),a=e(this,"sortAscending"),s=e(this,"sortFunction");return Ember.assert("you need to define `sortProperties`",!!o),t(o,function(t){0===i&&(i=s(e(r,t),e(n,t)),0===i||a||(i=-1*i))}),i},destroy:function(){var r=e(this,"content"),n=e(this,"sortProperties");return r&&n&&t(r,function(e){t(n,function(t){Ember.removeObserver(e,t,this,"contentItemSortPropertyDidChange")},this)},this),this._super()},isSorted:Ember.computed.bool("sortProperties"),arrangedContent:Ember.computed("content","sortProperties.@each",function(){var r=e(this,"content"),n=e(this,"isSorted"),i=e(this,"sortProperties"),o=this;return r&&n?(r=r.slice(),r.sort(function(e,t){return o.orderBy(e,t)}),t(r,function(e){t(i,function(t){Ember.addObserver(e,t,this,"contentItemSortPropertyDidChange")},this)},this),Ember.A(r)):r}),_contentWillChange:Ember.beforeObserver("content",function(){var r=e(this,"content"),n=e(this,"sortProperties");r&&n&&t(r,function(e){t(n,function(t){Ember.removeObserver(e,t,this,"contentItemSortPropertyDidChange")},this)},this),this._super()}),sortAscendingWillChange:Ember.beforeObserver("sortAscending",function(){this._lastSortAscending=e(this,"sortAscending")}),sortAscendingDidChange:Ember.observer("sortAscending",function(){if(e(this,"sortAscending")!==this._lastSortAscending){var t=e(this,"arrangedContent");t.reverseObjects()}}),contentArrayWillChange:function(r,n,i,o){var a=e(this,"isSorted");if(a){var s=e(this,"arrangedContent"),u=r.slice(n,n+i),l=e(this,"sortProperties");t(u,function(e){s.removeObject(e),t(l,function(t){Ember.removeObserver(e,t,this,"contentItemSortPropertyDidChange")},this)},this)}return this._super(r,n,i,o)},contentArrayDidChange:function(r,n,i,o){var a=e(this,"isSorted"),s=e(this,"sortProperties");if(a){var u=r.slice(n,n+o);t(u,function(e){this.insertItemSorted(e),t(s,function(t){Ember.addObserver(e,t,this,"contentItemSortPropertyDidChange")},this)},this)}return this._super(r,n,i,o)},insertItemSorted:function(t){var r=e(this,"arrangedContent"),n=e(r,"length"),i=this._binarySearch(t,0,n);r.insertAt(i,t)},contentItemSortPropertyDidChange:function(t){var r=e(this,"arrangedContent"),n=r.indexOf(t),i=r.objectAt(n-1),o=r.objectAt(n+1),a=i&&this.orderBy(t,i),s=o&&this.orderBy(t,o);(0>a||s>0)&&(r.removeObject(t),this.insertItemSorted(t))},_binarySearch:function(t,r,n){var i,o,a,s;return r===n?r:(s=e(this,"arrangedContent"),i=r+Math.floor((n-r)/2),o=s.objectAt(i),a=this.orderBy(o,t),0>a?this._binarySearch(t,i+1,n):a>0?this._binarySearch(t,r,i):i)}})}(),function(){var e=Ember.get,t=(Ember.set,Ember.EnumerableUtils.forEach),r=Ember.EnumerableUtils.replace;Ember.ArrayController=Ember.ArrayProxy.extend(Ember.ControllerMixin,Ember.SortableMixin,{itemController:null,lookupItemController:function(){return e(this,"itemController")},objectAtContent:function(t){var r=e(this,"length"),n=e(this,"arrangedContent"),i=n&&n.objectAt(t);if(t>=0&&r>t){var o=this.lookupItemController(i);if(o)return this.controllerAt(t,i,o)}return i},arrangedContentDidChange:function(){this._super(),this._resetSubControllers()},arrayContentDidChange:function(n,i,o){var a=e(this,"_subControllers"),s=a.slice(n,n+i);t(s,function(e){e&&e.destroy()}),r(a,n,i,new Array(o)),this._super(n,i,o)},init:function(){this._super(),this.set("_subControllers",Ember.A())},content:Ember.computed(function(){return Ember.A()}),_isVirtual:!1,controllerAt:function(t,r,n){var i,o=e(this,"container"),a=e(this,"_subControllers"),s=a[t];if(s)return s;if(i="controller:"+n,!o.has(i))throw new Ember.Error('Could not resolve itemController: "'+n+'"');var u;return this._isVirtual&&(u=e(this,"parentController")),u=u||this,s=o.lookupFactory(i).create({target:this,parentController:u,content:r}),a[t]=s,s},_subControllers:null,_resetSubControllers:function(){var r=e(this,"_subControllers");r&&t(r,function(e){e&&e.destroy()}),this.set("_subControllers",Ember.A())}})}(),function(){Ember.ObjectController=Ember.ObjectProxy.extend(Ember.ControllerMixin)}(),function(){var e=Ember.imports&&Ember.imports.jQuery||this&&this.jQuery;e||"function"!=typeof r||(e=r("jquery")),Ember.assert("Ember Views require jQuery between 1.7 and 2.1",e&&(e().jquery.match(/^((1\.(7|8|9|10|11))|(2\.(0|1)))(\.\d+)?(pre|rc\d?)?/)||Ember.ENV.FORCE_JQUERY)),Ember.$=e}(),function(){if(Ember.$){var e=Ember.String.w("dragstart drag dragenter dragleave dragover drop dragend");Ember.EnumerableUtils.forEach(e,function(e){Ember.$.event.fixHooks[e]={props:["dataTransfer"]}})}}(),function(){function e(e){var t=e.shiftKey||e.metaKey||e.altKey||e.ctrlKey,r=e.which>1;return!t&&!r}var t="undefined"!=typeof document&&function(){var e=document.createElement("div");return e.innerHTML="
    ",e.firstChild.innerHTML="",""===e.firstChild.innerHTML}(),r="undefined"!=typeof document&&function(){var e=document.createElement("div");return e.innerHTML="Test: Value","Test:"===e.childNodes[0].nodeValue&&" Value"===e.childNodes[2].nodeValue}(),n=function(e,t){if(e.getAttribute("id")===t)return e;var r,i,o,a=e.childNodes.length;for(r=0;a>r;r++)if(i=e.childNodes[r],o=1===i.nodeType&&n(i,t))return o},i=function(e,i){t&&(i="­"+i);var o=[];if(r&&(i=i.replace(/(\s+)(",""===e.firstChild.innerHTML}(),o=document&&function(){var e=document.createElement("div");return e.innerHTML="Test: Value","Test:"===e.childNodes[0].nodeValue&&" Value"===e.childNodes[2].nodeValue}(),a=function(r){var n;n=this instanceof a?this:new e,n.innerHTML=r;var i="metamorph-"+t++;return n.start=i+"-start",n.end=i+"-end",n};e.prototype=a.prototype;var s,u,l,c,h,m,p,d,f;if(c=function(){return this.startTag()+this.innerHTML+this.endTag()},d=function(){return""},f=function(){return""},n)s=function(e,t){var r=document.createRange(),n=document.getElementById(e.start),i=document.getElementById(e.end);return t?(r.setStartBefore(n),r.setEndAfter(i)):(r.setStartAfter(n),r.setEndBefore(i)),r},u=function(e,t){var r=s(this,t);r.deleteContents();var n=r.createContextualFragment(e);r.insertNode(n)},l=function(){var e=s(this,!0);e.deleteContents()},h=function(e){var t=document.createRange();t.setStart(e),t.collapse(!1);var r=t.createContextualFragment(this.outerHTML());e.appendChild(r)},m=function(e){var t=document.createRange(),r=document.getElementById(this.end);t.setStartAfter(r),t.setEndAfter(r);var n=t.createContextualFragment(e);t.insertNode(n)},p=function(e){var t=document.createRange(),r=document.getElementById(this.start);t.setStartAfter(r),t.setEndAfter(r);var n=t.createContextualFragment(e);t.insertNode(n)};else{var b={select:[1,""],fieldset:[1,"
    ","
    "],table:[1,"","
    "],tbody:[2,"","
    "],tr:[3,"","
    "],colgroup:[2,"","
    "],map:[1,"",""],_default:[0,"",""]},E=function(e,t){if(e.getAttribute("id")===t)return e;var r,n,i,o=e.childNodes.length;for(r=0;o>r;r++)if(n=e.childNodes[r],i=1===n.nodeType&&E(n,t))return i},v=function(e,t){var r=[];if(o&&(t=t.replace(/(\s+)(