dist/ember.js in ember-source-1.3.0.beta.4 vs dist/ember.js in ember-source-1.3.0

- old
+ new

@@ -1,13 +1,13 @@ /*! * @overview Ember - JavaScript Application Framework - * @copyright Copyright 2011-2013 Tilde Inc. and contributors + * @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.4.0-beta.1+canary.dc0dc0ce + * @version 1.3.0 */ (function() { /*global __fail__*/ @@ -29,11 +29,24 @@ if ('undefined' !== typeof window) { window.Em = window.Ember = Em = Ember; } } -Ember.ENV = 'undefined' === typeof ENV ? {} : ENV; +// 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 } @@ -55,10 +68,15 @@ @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) { + Ember.Logger.assert(test, desc); + } + + if (Ember.testing && !test) { + // when testing, ensure test failures when assertions fail throw new Ember.Error("Assertion Failed: " + desc); } }; @@ -178,16 +196,16 @@ })(); /*! * @overview Ember - JavaScript Application Framework - * @copyright Copyright 2011-2013 Tilde Inc. and contributors + * @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.4.0-beta.1+canary.dc0dc0ce + * @version 1.3.0 */ (function() { var define, requireModule, require, requirejs; @@ -242,11 +260,11 @@ return parentBase.join("/"); } }; })(); (function() { -/*globals Em:true ENV */ +/*globals Em:true ENV EmberENV MetamorphENV:true */ /** @module ember @submodule ember-metal */ @@ -266,11 +284,11 @@ The core Runtime framework is based on the jQuery API with a number of performance optimizations. @class Ember @static - @version 1.4.0-beta.1+canary.dc0dc0ce + @version 1.3.0 */ if ('undefined' === typeof Ember) { // Create core object. Make it act like an instance of Ember.Namespace so that // objects assigned to it are given a sane string representation. @@ -293,38 +311,51 @@ /** @property VERSION @type String - @default '1.4.0-beta.1+canary.dc0dc0ce' - @final + @default '1.3.0' + @static */ -Ember.VERSION = '1.4.0-beta.1+canary.dc0dc0ce'; +Ember.VERSION = '1.3.0'; /** - Standard environmental variables. You can define these in a global `ENV` - variable before loading Ember to control various configuration - settings. + Standard environmental variables. You can define these in a global `EmberENV` + variable before loading Ember to control various configuration settings. + For backwards compatibility with earlier versions of Ember the global `ENV` + variable will be used if `EmberENV` is not defined. + @property ENV @type Hash */ -if ('undefined' === typeof ENV) { - exports.ENV = {}; +// This needs to be kept in sync with the logic in +// `packages/ember-debug/lib/main.js`. +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 = {}; } +Ember.config = Ember.config || {}; + // We disable the RANGE API by default for performance reasons -if ('undefined' === typeof ENV.DISABLE_RANGE_API) { - ENV.DISABLE_RANGE_API = true; +if ('undefined' === typeof Ember.ENV.DISABLE_RANGE_API) { + Ember.ENV.DISABLE_RANGE_API = true; } +if ("undefined" === typeof MetamorphENV) { + exports.MetamorphENV = {}; +} -Ember.ENV = Ember.ENV || ENV; +MetamorphENV.DISABLE_RANGE_API = Ember.ENV.DISABLE_RANGE_API; -Ember.config = Ember.config || {}; - /** Hash of enabled Canary features. Add to before creating your application. You can also define `ENV.FEATURES` if you need to enable features flagged at runtime. @@ -845,14 +876,13 @@ @param {Exception} error the error object */ Ember.onerror = null; /** - @private - Wrap code block in a try/catch if `Ember.onerror` is set. + @private @method handleErrors @for Ember @param {Function} func @param [context] */ @@ -878,14 +908,12 @@ /** @module ember-metal */ /** - @private - Prefix used for guids through out Ember. - + @private */ Ember.GUID_PREFIX = 'ember'; var o_defineProperty = Ember.platform.defineProperty, @@ -897,19 +925,18 @@ stringCache = {}; var MANDATORY_SETTER = Ember.ENV.MANDATORY_SETTER; /** - @private - 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 */ @@ -921,16 +948,15 @@ enumerable: false, value: null }; /** - @private - 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. @@ -949,19 +975,18 @@ } return ret; }; /** - @private - 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. */ @@ -1022,31 +1047,33 @@ @final @type String */ Ember.META_KEY = META_KEY; -// Placeholder for non-writable metas. -var EMPTY_META = { - descs: {}, - watching: {} -}; - -if (MANDATORY_SETTER) { EMPTY_META.values = {}; } - -Ember.EMPTY_META = EMPTY_META; - -if (Object.freeze) Object.freeze(EMPTY_META); - var isDefinePropertySimulated = Ember.platform.defineProperty.isSimulated; function Meta(obj) { this.descs = {}; this.watching = {}; this.cache = {}; this.source = obj; } +Meta.prototype = { + descs: null, + deps: null, + watching: null, + listeners: null, + cache: null, + source: null, + mixins: null, + bindings: null, + chains: null, + chainWatchers: null, + values: 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. @@ -1055,10 +1082,17 @@ // 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 @@ -1174,16 +1208,15 @@ return value; }; /** - @private - Wraps the passed function so that `this._super` will point to the superFunc when the function is invoked. This is the primitive we use to implement calls to super. + @private @method wrap @for Ember @param {Function} func The function to call @param {Function} superFunc The super function. @return {Function} wrapped function. @@ -1489,10 +1522,12 @@ | 'boolean' | Boolean primitive or Boolean object. | | 'null' | Null value | | 'undefined' | Undefined value | | 'function' | A function | | 'array' | An instance of Array | + | 'regexp' | An instance of RegExp | + | 'date' | An instance of Date | | 'class' | An Ember class (created using Ember.Object.extend()) | | 'instance' | An Ember object instance | | 'error' | An instance of the Error object | | 'object' | A JavaScript object not inheriting from Ember.Object | @@ -1508,10 +1543,12 @@ Ember.typeOf(new Number(101)); // 'number' Ember.typeOf(true); // 'boolean' Ember.typeOf(new Boolean(true)); // 'boolean' Ember.typeOf(Ember.makeArray); // 'function' Ember.typeOf([1,2,90]); // 'array' + Ember.typeOf(/abc/); // 'regexp' + Ember.typeOf(new Date()); // 'date' Ember.typeOf(Ember.Object.extend()); // 'class' Ember.typeOf(Ember.Object.create()); // 'instance' Ember.typeOf(new Error('teamocil')); // 'error' // "normal" JavaScript object @@ -1531,51 +1568,16 @@ if (ret === 'function') { if (Ember.Object && Ember.Object.detect(item)) ret = 'class'; } else if (ret === 'object') { if (item instanceof Error) ret = 'error'; else if (Ember.Object && item instanceof Ember.Object) ret = 'instance'; - else ret = 'object'; + else if (item instanceof Date) ret = 'date'; } return ret; }; -/** - Convenience method to inspect an object. This method will attempt to - convert the object into a useful string description. - - It is a pretty simple implementation. If you want something more robust, - use something like JSDump: https://github.com/NV/jsDump - - @method inspect - @for Ember - @param {Object} obj The object you want to inspect. - @return {String} A description of the object -*/ -Ember.inspect = function(obj) { - var type = Ember.typeOf(obj); - if (type === 'array') { - return '[' + obj + ']'; - } - if (type !== 'object') { - return obj + ''; - } - - var v, ret = []; - for(var key in obj) { - if (obj.hasOwnProperty(key)) { - v = obj[key]; - if (v === 'toString') { continue; } // ignore useless items - if (Ember.typeOf(v) === 'function') { v = "function() { ... }"; } - ret.push(key + ": " + v); - } - } - return "{" + ret.join(", ") + "}"; -}; - - - })(); (function() { @@ -1955,17 +1957,16 @@ Ember.config.overrideAccessors(); get = Ember.get; } /** - @private - 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. @@ -1983,11 +1984,11 @@ 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'); + if (!path || path.length===0) throw new Ember.Error('Invalid Path'); return [ target, path ]; }; var getPath = Ember._getPath = function(root, path) { @@ -2210,19 +2211,18 @@ } } } /** - @private - 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. + @private @method suspendListener @for Ember @param obj @param {String} eventName @param {Object|Function} targetOrMethod A target object or a function @@ -2247,15 +2247,13 @@ return Ember.tryFinally(tryable, finalizer); } /** - @private - Suspends multiple listeners during a callback. - + @private @method suspendListeners @for Ember @param obj @param {Array} eventName Array of event names @param {Object|Function} targetOrMethod A target object or a function @@ -2295,14 +2293,13 @@ return Ember.tryFinally(tryable, finalizer); } /** - @private - Return a list of currently watched events + @private @method watchedEvents @for Ember @param obj */ function watchedEvents(obj) { @@ -2815,11 +2812,11 @@ } // 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)) { + if ((currentValue === undefined && !(keyName in obj)) || !obj.propertyIsEnumerable(keyName)) { Ember.defineProperty(obj, keyName, null, value); // setup mandatory setter } else { meta.values[keyName] = value; } } else { @@ -3454,12 +3451,10 @@ return meta && meta.values[name]; }; }; /** - @private - 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 @@ -3489,10 +3484,11 @@ 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 @@ -3665,11 +3661,11 @@ if (MANDATORY_SETTER && keyName in obj) { m.values[keyName] = obj[keyName]; o_defineProperty(obj, keyName, { configurable: true, - enumerable: true, + enumerable: obj.propertyIsEnumerable(keyName), set: Ember.MANDATORY_SETTER_FUNCTION, get: Ember.DEFAULT_GETTER_FUNCTION(keyName) }); } } else { @@ -3689,15 +3685,23 @@ } if (MANDATORY_SETTER && keyName in obj) { o_defineProperty(obj, keyName, { configurable: true, - enumerable: true, - writable: true, - value: m.values[keyName] + enumerable: obj.propertyIsEnumerable(keyName), + set: function(val) { + // redefine to set as enumerable + o_defineProperty(obj, keyName, { + configurable: true, + writable: true, + enumerable: true, + value: val + }); + delete m.values[keyName]; + }, + get: Ember.DEFAULT_GETTER_FUNCTION(keyName) }); - delete m.values[keyName]; } } else if (watching[keyName] > 1) { watching[keyName]--; } }; @@ -4036,57 +4040,11 @@ })(); (function() { -if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - /** - @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() { @@ -4156,18 +4114,17 @@ function isKeyName(path) { return path==='*' || !IS_PATH.test(path); } /** - @private - 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 */ @@ -4199,16 +4156,15 @@ unwatchPath(obj, _keyPath); } }; /** - @private - 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) { @@ -4287,13 +4243,10 @@ o_create = Ember.create, META_KEY = Ember.META_KEY, watch = Ember.watch, unwatch = Ember.unwatch; -if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - var expandProperties = Ember.expandProperties; -} // .......................................................... // DEPENDENT KEYS // @@ -4554,22 +4507,13 @@ @chainable */ ComputedPropertyPrototype.property = function() { var args; - if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - var addArg = function (property) { - args.push(property); - }; - - args = []; - for (var i = 0, l = arguments.length; i < l; i++) { - expandProperties(arguments[i], addArg); - } - } else { + args = a_slice.call(arguments); - } + this._dependentKeys = args; return this; }; @@ -4694,11 +4638,11 @@ hadCachedValue = false, cache = meta.cache, funcArgLength, cachedValue, ret; if (this._readOnly) { - throw new Ember.Error('Cannot Set: ' + keyName + ' on: ' + Ember.inspect(obj)); + throw new Ember.Error('Cannot Set: ' + keyName + ' on: ' + obj.toString() ); } this._suspended = obj; try { @@ -4880,15 +4824,19 @@ /** 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') + 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 @@ -5264,20 +5212,20 @@ Example ```javascript var Hamster = Ember.Object.extend({ - clothes: Ember.computed.map('hat', 'shirt') + 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.map + @method computed.collect @for Ember @param {String} dependentKey* @return {Ember.ComputedProperty} computed property which maps values of all passed properties in to an array. */ @@ -5371,55 +5319,11 @@ return Ember.computed(dependentKey, function() { return get(this, dependentKey); }); }; -if (Ember.FEATURES.isEnabled('computed-read-only')) { -/** - 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:ember27288>' );` - - 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 @@ -5564,11 +5468,11 @@ })(); (function() { -define("backburner/queue", +define("backburner/queue", ["exports"], function(__exports__) { "use strict"; function Queue(daq, name, options) { this.daq = daq; @@ -5671,14 +5575,15 @@ } } } }; + __exports__.Queue = Queue; }); -define("backburner/deferred_action_queues", +define("backburner/deferred_action_queues", ["backburner/queue","exports"], function(__dependency1__, __exports__) { "use strict"; var Queue = __dependency1__.Queue; @@ -5773,14 +5678,15 @@ } return -1; } + __exports__.DeferredActionQueues = DeferredActionQueues; }); -define("backburner", +define("backburner", ["backburner/deferred_action_queues","exports"], function(__dependency1__, __exports__) { "use strict"; var DeferredActionQueues = __dependency1__.DeferredActionQueues; @@ -5885,11 +5791,11 @@ if (typeof method === 'string') { method = target[method]; } - var stack = this.DEBUG ? new Error().stack : undefined, + 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); }, @@ -5901,11 +5807,11 @@ if (typeof method === 'string') { method = target[method]; } - var stack = this.DEBUG ? new Error().stack : undefined, + 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); }, @@ -6201,13 +6107,13 @@ } return index; } + __exports__.Backburner = Backburner; }); - })(); (function() { @@ -6285,20 +6191,18 @@ 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 @@ -6312,11 +6216,11 @@ @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, + @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) { if (!Ember.run.currentRunLoop) { return Ember.run.apply(Ember.run, arguments); @@ -6401,11 +6305,12 @@ 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: + // 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 @@ -6493,11 +6398,11 @@ @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`. + @return {Object} timer */ Ember.run.once = function(target, method) { checkAutoRun(); var args = slice.call(arguments); args.unshift('actions'); @@ -6544,11 +6449,11 @@ @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`. + @return {Object} timer */ Ember.run.scheduleOnce = function(queue, target, method) { checkAutoRun(); return backburner.scheduleOnce.apply(backburner, arguments); }; @@ -6558,11 +6463,12 @@ 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 + // 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 @@ -6606,22 +6512,21 @@ @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`. + @return {Object} timer */ 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()`. + `Ember.run.once()`, or `Ember.run.next()`. ```javascript var runNext = Ember.run.next(myContext, function() { // will not be executed }); @@ -6634,33 +6539,15 @@ 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); - 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 + @return {void} */ Ember.run.cancel = function(timer) { return backburner.cancel(timer); }; @@ -6688,47 +6575,19 @@ // 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 msut 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. - @return {Array} Timer information for use in cancelling, see `Ember.run.cancel`. + @return {void} */ Ember.run.debounce = function() { return backburner.debounce.apply(backburner, arguments); }; @@ -6761,11 +6620,11 @@ @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`. + @return {void} */ Ember.run.throttle = function() { return backburner.throttle.apply(backburner, arguments); }; @@ -7271,13 +7130,10 @@ a_slice = [].slice, o_create = Ember.create, defineProperty = Ember.defineProperty, guidFor = Ember.guidFor; -if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - var expandProperties = Ember.expandProperties; -} function mixinsMeta(obj) { var m = Ember.meta(obj, true), ret = m.mixins; if (!ret) { ret = m.mixins = {}; @@ -7454,11 +7310,12 @@ delete values[keyName]; } for(var i=0, l=mixins.length; i<l; i++) { mixin = mixins[i]; - Ember.assert('Expected hash or Mixin instance, got ' + Object.prototype.toString.call(mixin), typeof mixin === 'object' && mixin !== null && Object.prototype.toString.call(mixin) !== '[object Array]'); + Ember.assert('Expected hash or Mixin instance, got ' + Object.prototype.toString.call(mixin), + typeof mixin === 'object' && mixin !== null && Object.prototype.toString.call(mixin) !== '[object Array]'); props = mixinProperties(m, mixin); if (props === CONTINUE) { continue; } if (props) { @@ -7727,11 +7584,12 @@ var len = arguments.length, mixins = this.mixins, idx; for(idx=0; idx < len; idx++) { mixin = arguments[idx]; - Ember.assert('Expected hash or Mixin instance, got ' + Object.prototype.toString.call(mixin), typeof mixin === 'object' && mixin !== null && Object.prototype.toString.call(mixin) !== '[object Array]'); + Ember.assert('Expected hash or Mixin instance, got ' + Object.prototype.toString.call(mixin), + typeof mixin === 'object' && mixin !== null && Object.prototype.toString.call(mixin) !== '[object Array]'); if (mixin instanceof Mixin) { mixins.push(mixin); } else { tmp = Mixin.create(); @@ -7848,10 +7706,39 @@ this.methodName = methodName; }; Alias.prototype = new Ember.Descriptor(); /** + Makes a property or method available via an additional name. + + ```javascript + App.PaintSample = Ember.Object.extend({ + color: 'red', + colour: Ember.alias('color'), + name: function() { + return "Zed"; + }, + moniker: Ember.alias("name") + }); + + var paintSample = App.PaintSample.create() + paintSample.get('colour'); // 'red' + paintSample.moniker(); // 'Zed' + ``` + + @method alias + @for Ember + @param {String} methodName name of the method or property to alias + @return {Ember.Descriptor} + @deprecated Use `Ember.aliasMethod` or `Ember.computed.alias` instead +*/ +Ember.alias = function(methodName) { + Ember.deprecate("Ember.alias is deprecated. Please use Ember.aliasMethod or Ember.computed.alias instead."); + return new Alias(methodName); +}; + +/** Makes a method available via an additional name. ```javascript App.Person = Ember.Object.extend({ name: function() { @@ -7901,36 +7788,20 @@ */ Ember.observer = function() { var func = a_slice.call(arguments, -1)[0]; var paths; - if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - 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); - } - } else { + 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); } - } + if (typeof func !== "function") { throw new Ember.Error("Ember.observer called without a function"); } @@ -8015,37 +7886,20 @@ */ Ember.beforeObserver = function() { var func = a_slice.call(arguments, -1)[0]; var paths; - if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - 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); - } - } else { + 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); } - } + if (typeof func !== "function") { throw new Ember.Error("Ember.beforeObserver called without a function"); } @@ -8120,181 +7974,149 @@ /** @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"; - /* global toString */ - - var Promise = __dependency1__.Promise; + var Promise = __dependency1__["default"]; var isArray = __dependency2__.isArray; - var isFunction = __dependency2__.isFunction; + 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 - fulfilled, or rejected if any of them become rejected. The return promise - is fulfilled with an array that gives all the values in the order they were - passed in the `promises` array argument. + settled. The return promise is fulfilled with an array of the states of + the promises passed into the `promises` array argument. - Example: + 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 - var promise1 = RSVP.resolve(1); - var promise2 = RSVP.resolve(2); - var promise3 = RSVP.resolve(3); - var promises = [ promise1, promise2, promise3 ]; - - RSVP.all(promises).then(function(array){ - // The array here would be [ 1, 2, 3 ]; - }); + { state: 'fulfilled', value: value } + or + { state: 'rejected', reason: reason } ``` - 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 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.all(promises).then(function(array){ - // Code here never runs because there are rejected promises! + 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) { - // error.message === "2" + // Not run. (This block would only be called if allSettled had failed, + // for instance if passed an incorrect argument type.) }); ``` - @method all + @method allSettled @for RSVP @param {Array} promises - @param {String} label - @return {Promise} promise that is fulfilled when all `promises` have been - fulfilled, or rejected if any of them become rejected. + @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 */ - function all(promises, label) { - if (!isArray(promises)) { - throw new TypeError('You must pass an array to all.'); - } + __exports__["default"] = function allSettled(entries, label) { return new Promise(function(resolve, reject) { - var results = [], remaining = promises.length, - promise; + if (!isArray(entries)) { + throw new TypeError('You must pass an array to allSettled.'); + } + var remaining = entries.length; + var entry; + if (remaining === 0) { resolve([]); + return; } - function resolver(index) { + var results = new Array(remaining); + + function fulfilledResolver(index) { return function(value) { - resolveAll(index, 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 i = 0; i < promises.length; i++) { - promise = promises[i]; + for (var index = 0; index < entries.length; index++) { + entry = entries[index]; - if (promise && isFunction(promise.then)) { - promise.then(resolver(i), reject, "RSVP: RSVP#all"); + if (isNonThenable(entry)) { + resolveAll(index, fulfilled(entry)); } else { - resolveAll(i, promise); + Promise.cast(entry).then(fulfilledResolver(index), rejectedResolver(index)); } } }, label); - } + }; - __exports__.all = all; - }); - -define("rsvp/cast", - ["exports"], - function(__exports__) { - "use strict"; - /** - `RSVP.Promise.cast` returns the same promise if that promise shares a constructor - with the promise being casted. - - Example: - - ```javascript - var promise = RSVP.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. - - 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.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 - @for RSVP - @param {Object} object to be casted - @return {Promise} promise that is fulfilled when all properties of `promises` - have been fulfilled, or rejected if any of them become rejected. - */ - - - function cast(object) { - /*jshint validthis:true */ - if (object && typeof object === 'object' && object.constructor === this) { - return object; - } - - var Promise = this; - - return new Promise(function(resolve) { - resolve(object); - }); + function fulfilled(value) { + return { state: 'fulfilled', value: value }; } - __exports__.cast = cast; + function rejected(reason) { + return { state: 'rejected', reason: reason }; + } }); define("rsvp/config", ["./events","exports"], function(__dependency1__, __exports__) { "use strict"; - var EventTarget = __dependency1__.EventTarget; + var EventTarget = __dependency1__["default"]; var config = { instrument: false }; @@ -8321,14 +8143,14 @@ }); define("rsvp/defer", ["./promise","exports"], function(__dependency1__, __exports__) { "use strict"; - var Promise = __dependency1__.Promise; + var Promise = __dependency1__["default"]; /** - `RSVP.defer` returns an object similar to jQuery's `$.Deferred` objects. + `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: @@ -8350,31 +8172,25 @@ }); ``` @method defer @for RSVP - @param {String} - + @param {String} label optional string for labeling the promise. + Useful for tooling. @return {Object} */ - function defer(label) { - var deferred = { - // pre-allocate shape - resolve: undefined, - reject: undefined, - promise: undefined - }; + __exports__["default"] = function defer(label) { + var deferred = { }; deferred.promise = new Promise(function(resolve, reject) { deferred.resolve = resolve; deferred.reject = reject; }, label); return deferred; - } - - __exports__.defer = defer; + }; }); define("rsvp/events", ["exports"], function(__exports__) { "use strict"; @@ -8395,17 +8211,15 @@ return callbacks; }; /** - //@module RSVP - //@class EventTarget + @class RSVP.EventTarget */ - var EventTarget = { + __exports__["default"] = { /** - @private `RSVP.EventTarget.mixin` extends an object with EventTarget methods. For Example: ```javascript var object = {}; @@ -8440,22 +8254,21 @@ tom.trigger("poke"); ``` @method mixin @param {Object} object object to extend with EventTarget methods + @private */ mixin: function(object) { object.on = this.on; object.off = this.off; object.trigger = this.trigger; object._promiseCallbacks = undefined; return object; }, /** - @private - Registers a callback to be executed when `eventName` is triggered ```javascript object.on('event', function(eventInfo){ // handle the event @@ -8465,10 +8278,11 @@ ``` @method on @param {String} eventName name of the event to listen for @param {Function} callback function to be called when the event is triggered. + @private */ on: function(eventName, callback) { var allCallbacks = callbacksFor(this), callbacks; callbacks = allCallbacks[eventName]; @@ -8481,12 +8295,10 @@ callbacks.push(callback); } }, /** - @private - You can use `off` to stop firing a particular callback for an event: ```javascript function doStuff() { // do stuff! } object.on('stuff', doStuff); @@ -8518,10 +8330,12 @@ @param {String} eventName event to stop listening to @param {Function} callback optional argument. If given, only the function given will be removed from the event's callback queue. If no `callback` argument is given, all callbacks will be removed from the event's callback queue. + @private + */ off: function(eventName, callback) { var allCallbacks = callbacksFor(this), callbacks, index; if (!callback) { @@ -8535,12 +8349,10 @@ if (index !== -1) { callbacks.splice(index, 1); } }, /** - @private - Use `trigger` to fire custom events. For example: ```javascript object.on('foo', function(){ console.log('foo event happened!'); @@ -8563,10 +8375,11 @@ @method trigger @param {String} eventName name of the event to be triggered @param {Any} options optional value to be passed to any event handlers for the given `eventName` + @private */ trigger: function(eventName, options) { var allCallbacks = callbacksFor(this), callbacks, callbackTuple, callback, binding; @@ -8578,30 +8391,135 @@ callback(options); } } } }; + }); +define("rsvp/filter", + ["./all","./map","./utils","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { + "use strict"; + var all = __dependency1__["default"]; + var map = __dependency2__["default"]; + var isFunction = __dependency3__.isFunction; + var isArray = __dependency3__.isArray; - __exports__.EventTarget = EventTarget; + /** + `RSVP.filter` is similar to JavaScript's native `filter` method, except that it + waits for all promises to become fulfilled before running the `filterFn` on + each item in given to `promises`. `RSVP.filter` returns a promise that will + become fulfilled with the result of running `filterFn` 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 filterFn = function(item){ + return item > 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__.Promise; - var isFunction = __dependency2__.isFunction; + var Promise = __dependency1__["default"]; + var isNonThenable = __dependency2__.isNonThenable; + var keysOf = __dependency2__.keysOf; - var keysOf = Object.keys || function(object) { - var result = []; - - for (var prop in object) { - result.push(prop); - } - - return result; - }; - /** `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 @@ -8630,13 +8548,14 @@ // } }); ```` If any of the `promises` given to `RSVP.hash` are rejected, the first promise - that is rejected will be given as as the first argument, or as the reason to - the rejection handler. For example: + 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")), @@ -8679,90 +8598,198 @@ ``` @method hash @for RSVP @param {Object} promises - @param {String} label - optional string that describes the promise. + @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 */ - function hash(object, label) { - var results = {}, - keys = keysOf(object), - remaining = keys.length; - + __exports__["default"] = function hash(object, label) { return new Promise(function(resolve, reject){ - var promise, prop; + var results = {}; + var keys = keysOf(object); + var remaining = keys.length; + var entry, property; if (remaining === 0) { - resolve({}); + resolve(results); return; } - var resolver = function(prop) { + function fulfilledTo(property) { return function(value) { - resolveAll(prop, value); + results[property] = value; + if (--remaining === 0) { + resolve(results); + } }; - }; + } - var resolveAll = function(prop, value) { - results[prop] = 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]; - for (var i = 0, l = keys.length; i < l; i ++) { - prop = keys[i]; - promise = object[prop]; - - if (promise && isFunction(promise.then)) { - promise.then(resolver(prop), reject, "RSVP: RSVP#hash"); + if (isNonThenable(entry)) { + results[property] = entry; + if (--remaining === 0) { + resolve(results); + } } else { - resolveAll(prop, promise); + Promise.cast(entry).then(fulfilledTo(property), onRejection); } } }); - } - - __exports__.hash = hash; + }; }); define("rsvp/instrument", ["./config","./utils","exports"], function(__dependency1__, __dependency2__, __exports__) { "use strict"; var config = __dependency1__.config; var now = __dependency2__.now; - function instrument(eventName, promise, child) { + __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() + 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; - __exports__.instrument = instrument; + /** + `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","./all","exports"], - function(__dependency1__, __dependency2__, __exports__) { + ["./promise","exports"], + function(__dependency1__, __exports__) { "use strict"; - var Promise = __dependency1__.Promise; - var all = __dependency2__.all; + var Promise = __dependency1__["default"]; var slice = Array.prototype.slice; function makeNodeCallbackFor(resolve, reject) { return function (error, value) { @@ -8847,46 +8874,159 @@ 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 */ - function denodeify(nodeFunc, binding) { + __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) { - all(nodeArgs).then(function(nodeArgs) { + Promise.all(nodeArgs).then(function(nodeArgs) { try { nodeArgs.push(makeNodeCallbackFor(resolve, reject)); nodeFunc.apply(thisArg, nodeArgs); } catch(e) { reject(e); } }); }); }; - } - - __exports__.denodeify = denodeify; + }; }); define("rsvp/promise", - ["./config","./events","./cast","./instrument","./utils","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __exports__) { + ["./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__.EventTarget; - var cast = __dependency3__.cast; - var instrument = __dependency4__.instrument; - var objectOrFunction = __dependency5__.objectOrFunction; - var isFunction = __dependency5__.isFunction; - var now = __dependency5__.now; + 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'); } @@ -8900,11 +9040,13 @@ if (config.instrument) { instrument('created', this); } - invokeResolver(resolver, this); + if (noop !== resolver) { + invokeResolver(resolver, this); + } } function invokeResolver(resolver, promise) { function resolvePromise(value) { resolve(promise, value); @@ -8919,40 +9061,16 @@ } catch(e) { rejectPromise(e); } } - function invokeCallback(settled, promise, callback, detail) { - var hasCallback = isFunction(callback), - value, error, succeeded, failed; + Promise.cast = cast; + Promise.all = all; + Promise.race = race; + Promise.resolve = Resolve; + Promise.reject = Reject; - 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); - } - } - var PENDING = void 0; var SEALED = 0; var FULFILLED = 1; var REJECTED = 2; @@ -8995,15 +9113,209 @@ _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(function() {}, label); + 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); @@ -9017,14 +9329,82 @@ } 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(){ @@ -9036,12 +9416,40 @@ }); }, label); } }; - Promise.cast = cast; + 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 { @@ -9065,11 +9473,11 @@ }, function(val) { if (resolved) { return true; } resolved = true; reject(promise, val); - }, 'Locked onto ' + (promise._label || ' unknown promise')); + }, 'derived from: ' + (promise._label || ' unknown promise')); return true; } } } catch (error) { @@ -9114,25 +9522,209 @@ 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; - __exports__.Promise = Promise; + /** + `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 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/race", - ["./promise","./utils","exports"], - function(__dependency1__, __dependency2__, __exports__) { +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 Promise = __dependency1__.Promise; - var isArray = __dependency2__.isArray; + var isArray = __dependency1__.isArray; + var isFunction = __dependency1__.isFunction; + var isNonThenable = __dependency1__.isNonThenable; /** - `RSVP.race` allows you to watch a series of promises and act as soon as the - first promise given to the `promises` argument fulfills or rejects. + `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){ @@ -9145,21 +9737,21 @@ setTimeout(function(){ resolve("promise 2"); }, 100); }); - RSVP.race([promise1, promise2]).then(function(result){ + RSVP.Promise.race([promise1, promise2]).then(function(result){ // result === "promise 2" because it was resolved before promise1 // was resolved. }); ``` - `RSVP.race` is deterministic in that only the state of the first completed - promise matters. For example, even if other promises given to the `promises` - array argument are resolved, but the first completed promise has become - rejected before the other promises became fulfilled, the returned promise - will become rejected: + `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"); @@ -9170,58 +9762,66 @@ setTimeout(function(){ reject(new Error("promise 2")); }, 100); }); - RSVP.race([promise1, promise2]).then(function(result){ - // Code here never runs because there are rejected promises! + 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 - @for RSVP @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 that becomes fulfilled with the value the first - completed promises is resolved with if the first completed promise was - fulfilled, or rejected with the reason that the first completed promise - was rejected with. + @return {Promise} a promise which settles in the same way as the first passed + promise to settle. + @static */ - function race(promises, label) { - if (!isArray(promises)) { - throw new TypeError('You must pass an array to race.'); - } - return new Promise(function(resolve, reject) { - var results = [], promise; + __exports__["default"] = function race(entries, label) { + /*jshint validthis:true */ + var Constructor = this, entry; - for (var i = 0; i < promises.length; i++) { - promise = promises[i]; + return new Constructor(function(resolve, reject) { + if (!isArray(entries)) { + throw new TypeError('You must pass an array to race.'); + } - if (promise && typeof promise.then === 'function') { - promise.then(resolve, reject, "RSVP: RSVP#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 { - resolve(promise); + Constructor.cast(entry).then(onFulfillment, onRejection); } } }, label); - } - - __exports__.race = race; + }; }); -define("rsvp/reject", - ["./promise","exports"], - function(__dependency1__, __exports__) { +define("rsvp/promise/reject", + ["exports"], + function(__exports__) { "use strict"; - var Promise = __dependency1__.Promise; - /** - `RSVP.reject` returns a promise that will become rejected with the passed - `reason`. `RSVP.reject` is essentially shorthand for the following: + `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')); }); @@ -9234,44 +9834,42 @@ ``` Instead of writing the above, your code now simply becomes the following: ```javascript - var promise = RSVP.reject(new Error('WHOOPS')); + 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 - @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 that will become rejected with the given - `reason`. + @return {Promise} a promise rejected with the given `reason`. + @static */ - function reject(reason, label) { - return new Promise(function (resolve, reject) { + __exports__["default"] = function reject(reason, label) { + /*jshint validthis:true */ + var Constructor = this; + + return new Constructor(function (resolve, reject) { reject(reason); }, label); - } - - __exports__.reject = reject; + }; }); -define("rsvp/resolve", - ["./promise","exports"], - function(__dependency1__, __exports__) { +define("rsvp/promise/resolve", + ["exports"], + function(__exports__) { "use strict"; - var Promise = __dependency1__.Promise; - /** - `RSVP.resolve` returns a promise that will become fulfilled with the passed - `value`. `RSVP.resolve` is essentially shorthand for the following: + `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); }); @@ -9282,128 +9880,200 @@ ``` Instead of writing the above, your code now simply becomes the following: ```javascript - var promise = RSVP.resolve(1); + var promise = RSVP.Promise.resolve(1); promise.then(function(value){ // value === 1 }); ``` @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 */ - function resolve(value, label) { - return new Promise(function(resolve, reject) { + __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"]; - __exports__.resolve = resolve; + /** + 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"; - var local = (typeof global === "undefined") ? this : global; - /** `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 throw the error - again so the error can be handled by the promise. + 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.fail(RSVP.rethrow).then(function(){ + 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 `.fail` on the returned promise. + 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 */ - function rethrow(reason) { - local.setTimeout(function() { + __exports__["default"] = function rethrow(reason) { + setTimeout(function() { throw reason; }); throw reason; - } - - __exports__.rethrow = rethrow; + }; }); define("rsvp/utils", ["exports"], function(__exports__) { "use strict"; function objectOrFunction(x) { - return isFunction(x) || (typeof x === "object" && x !== null); + return typeof x === "function" || (typeof x === "object" && x !== null); } - function isFunction(x) { + __exports__.objectOrFunction = objectOrFunction;function isFunction(x) { return typeof x === "function"; } - function isArray(x) { + __exports__.isFunction = isFunction;function isNonThenable(x) { + return !objectOrFunction(x); + } + + __exports__.isNonThenable = isNonThenable;function isArray(x) { return Object.prototype.toString.call(x) === "[object Array]"; } - // Date.now is not available in browsers < IE9 + __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); + } - __exports__.objectOrFunction = objectOrFunction; - __exports__.isFunction = isFunction; - __exports__.isArray = isArray; - __exports__.now = now; + return result; + }; + __exports__.keysOf = keysOf; }); define("rsvp", - ["./rsvp/events","./rsvp/promise","./rsvp/node","./rsvp/all","./rsvp/race","./rsvp/hash","./rsvp/rethrow","./rsvp/defer","./rsvp/config","./rsvp/resolve","./rsvp/reject", "exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { + ["./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 EventTarget = __dependency1__.EventTarget; - var Promise = __dependency2__.Promise; - var denodeify = __dependency3__.denodeify; - var all = __dependency4__.all; - var race = __dependency5__.race; - var hash = __dependency6__.hash; - var rethrow = __dependency7__.rethrow; - var defer = __dependency8__.defer; - var config = __dependency9__.config; - var configure = __dependency9__.configure; - var resolve = __dependency10__.resolve; - var reject = __dependency11__.reject; + 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); } @@ -9413,13 +10083,25 @@ 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; @@ -9427,20 +10109,22 @@ __exports__.on = on; __exports__.off = off; __exports__.resolve = resolve; __exports__.reject = reject; __exports__.async = async; + __exports__.map = map; + __exports__.filter = filter; }); })(); (function() { /** -@private Public api for the container is still in flux. The public api, specified on the application namespace should be considered the stable api. // @module container + @private */ /* Flag to enable/disable model factory injections (disabled by default) If model factory injections are enabled, models should not be @@ -9449,11 +10133,10 @@ Ember.MODEL_FACTORY_INJECTIONS = false || !!Ember.ENV.MODEL_FACTORY_INJECTIONS; define("container", [], function() { - "use strict"; // A safe and simple inheriting object. function InheritingDict(parent) { this.parent = parent; this.dict = {}; @@ -9571,12 +10254,11 @@ 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.factoryCache = new InheritingDict(parent && parent.cache); this.typeInjections = new InheritingDict(parent && parent.typeInjections); this.injections = {}; this.factoryTypeInjections = new InheritingDict(parent && parent.factoryTypeInjections); this.factoryInjections = {}; @@ -9693,11 +10375,13 @@ @param {String} fullName @param {Function} factory @param {Object} options */ register: function(fullName, factory, options) { - validateFullName(fullName); + if (fullName.indexOf(':') === -1) { + throw new TypeError("malformed fullName, expected: `type:name` got: " + fullName + ""); + } if (factory === undefined) { throw new TypeError('Attempting to register an unknown factory: `' + fullName + '`'); } @@ -9726,18 +10410,15 @@ @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. @@ -9770,22 +10451,11 @@ @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; + return this.resolver(fullName) || this.registry.get(fullName); }, /** A hook that can be used to describe how the resolver will attempt to find the factory. @@ -9862,24 +10532,38 @@ @param {String} fullName @param {Object} options @return {any} */ lookup: function(fullName, options) { - validateFullName(fullName); - return lookup(this, this.normalize(fullName), options); + fullName = this.normalize(fullName); + + options = options || {}; + + if (this.cache.has(fullName) && options.singleton !== false) { + return this.cache.get(fullName); + } + + var value = instantiate(this, fullName); + + if (value === undefined) { return; } + + if (isSingleton(this, fullName) && options.singleton !== false) { + this.cache.set(fullName, value); + } + + return value; }, /** 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)); + return factoryFor(this, fullName); }, /** Given a fullName check if the container is aware of its factory or singleton instance. @@ -9887,12 +10571,15 @@ @method has @param {String} fullName @return {Boolean} */ has: function(fullName) { - validateFullName(fullName); - return has(this, this.normalize(fullName)); + if (this.cache.has(fullName)) { + return true; + } + + return !!this.resolve(fullName); }, /** Allow registering options for all factories of a type. @@ -9934,12 +10621,10 @@ options: function(type, options) { this.optionsForType(type, options); }, /** - @private - 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. @@ -9964,17 +10649,17 @@ // 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); }, @@ -10020,30 +10705,22 @@ @method injection @param {String} factoryName @param {String} property @param {String} injectionName */ - injection: function(fullName, property, injectionName) { + injection: function(factoryName, property, injectionName) { if (this.parent) { illegalChildOperation('injection'); } - validateFullName(injectionName); - var normalizedInjectionName = this.normalize(injectionName); - - if (fullName.indexOf(':') === -1) { - return this.typeInjection(fullName, property, normalizedInjectionName); + if (factoryName.indexOf(':') === -1) { + return this.typeInjection(factoryName, property, injectionName); } - validateFullName(fullName); - var normalizedName = this.normalize(fullName); - - addInjection(this.injections, normalizedName, property, normalizedInjectionName); + addInjection(this.injections, factoryName, property, injectionName); }, /** - @private - 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. @@ -10062,19 +10739,20 @@ 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)); + addTypeInjection(this.factoryTypeInjections, type, property, fullName); }, /** Defines factory injection rules. @@ -10122,34 +10800,28 @@ @method factoryInjection @param {String} factoryName @param {String} property @param {String} injectionName */ - factoryInjection: function(fullName, property, injectionName) { + factoryInjection: function(factoryName, 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); + if (factoryName.indexOf(':') === -1) { + return this.factoryTypeInjection(factoryName, property, injectionName); } - validateFullName(fullName); - - addInjection(this.factoryInjections, normalizedName, property, normalizedInjectionName); + addInjection(this.factoryInjections, factoryName, property, injectionName); }, /** 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<l; i++) { this.children[i].destroy(); } this.children = []; @@ -10171,36 +10843,10 @@ } resetCache(this); } }; - function has(container, fullName){ - if (container.cache.has(fullName)) { - return true; - } - - return !!container.resolve(fullName); - } - - function lookup(container, fullName, options) { - options = options || {}; - - if (container.cache.has(fullName) && options.singleton !== false) { - return container.cache.get(fullName); - } - - var value = instantiate(container, fullName); - - if (value === undefined) { return; } - - if (isSingleton(container, fullName) && options.singleton !== false) { - container.cache.set(fullName, value); - } - - return value; - } - function illegalChildOperation(operation) { throw new Error(operation + " is not currently supported on child containers"); } function isSingleton(container, fullName) { @@ -10212,18 +10858,18 @@ function buildInjections(container, injections) { var hash = {}; if (!injections) { return hash; } - var injection, injectable; + var injection, lookup; for (var i=0, l=injections.length; i<l; i++) { injection = injections[i]; - injectable = lookup(container, injection.fullName); + lookup = container.lookup(injection.fullName); - if (injectable !== undefined) { - hash[injection.property] = injectable; + if (lookup !== undefined) { + hash[injection.property] = lookup; } else { throw new Error('Attempting to inject an unknown injection: `' + injection.fullName + '`'); } } @@ -10244,11 +10890,11 @@ return options[optionName]; } } function factoryFor(container, fullName) { - var name = fullName; + var name = container.normalize(fullName); var factory = container.resolve(name); var injectedFactory; var cache = container.factoryCache; var type = fullName.split(":")[0]; @@ -10354,17 +11000,10 @@ property: property, fullName: fullName }); } - var VALID_FULL_NAME_REGEXP = /^[^:]+.+:[^:]+$/; - function validateFullName(fullName) { - if (!VALID_FULL_NAME_REGEXP.test(fullName)) { - throw new TypeError('Invalid Fullname, expected: `type:name` got: ' + fullName); - } - } - function addInjection(rules, factoryName, property, injectionName) { var injections = rules[factoryName] = rules[factoryName] || []; injections.push({ property: property, fullName: injectionName }); } @@ -10556,10 +11195,43 @@ if (Ember.Copyable && Ember.Copyable.detect(obj)) return obj.copy(deep); return _copy(obj, deep, deep ? [] : null, deep ? [] : null); }; /** + Convenience method to inspect an object. This method will attempt to + convert the object into a useful string description. + + It is a pretty simple implementation. If you want something more robust, + use something like JSDump: https://github.com/NV/jsDump + + @method inspect + @for Ember + @param {Object} obj The object you want to inspect. + @return {String} A description of the object +*/ +Ember.inspect = function(obj) { + var type = Ember.typeOf(obj); + if (type === 'array') { + return '[' + obj + ']'; + } + if (type !== 'object') { + return obj + ''; + } + + var v, ret = []; + for(var key in obj) { + if (obj.hasOwnProperty(key)) { + v = obj[key]; + if (v === 'toString') { continue; } // ignore useless items + if (Ember.typeOf(v) === 'function') { v = "function() { ... }"; } + ret.push(key + ": " + v); + } + } + return "{" + ret.join(", ") + "}"; +}; + +/** 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. @@ -10659,14 +11331,10 @@ 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. @@ -10901,60 +11569,12 @@ capitalize: function(str) { return str.charAt(0).toUpperCase() + str.substr(1); } }; -if (Ember.FEATURES.isEnabled("string-humanize")) { - /** - Returns the Humanized form of a string - Replaces underscores with spaces, and capitializes first character - of string. Also strips "_id" suffixes. - ```javascript - 'first_name'.humanize() // 'First name' - 'user_id'.humanize() // 'User' - ``` - - @method humanize - @param {String} str The string to humanize. - @return {String} The humanized string. - */ - - Ember.String.humanize = function(str) { - return str.replace(/_id$/, ''). - replace(/_/g, ' '). - replace(/^\w/g, function(s){ - return s.toUpperCase(); - }); - }; -} - -if (Ember.FEATURES.isEnabled("string-parameterize")) { - /** - Transforms a string so that it may be used as part of a 'pretty' / SEO friendly URL. - - ```javascript - 'My favorite items.'.parameterize(); // 'my-favorite-items' - 'action_name'.parameterize(); // 'action-name' - '100 ways Ember.js is better than Angular.'.parameterize(); // '100-ways-emberjs-is-better-than-angular' - ``` - - @method parameterize - @param {String} str The string to parameterize. - @return {String} the parameterized string. - */ - Ember.String.parameterize = function(str) { - return str.replace(STRING_PARAMETERIZE_REGEXP_1, '-') // replace underscores, slashes and spaces with separator - .replace(STRING_PARAMETERIZE_REGEXP_2, '') // remove non-alphanumeric characters except the separator - .replace(STRING_PARAMETERIZE_REGEXP_3, '-') // replace multiple occurring separators - .replace(STRING_PARAMETERIZE_REGEXP_4, '') // trim leading and trailing separators - .toLowerCase(); - }; -} - - })(); (function() { @@ -10973,18 +11593,11 @@ dasherize = Ember.String.dasherize, underscore = Ember.String.underscore, capitalize = Ember.String.capitalize, classify = Ember.String.classify; -if (Ember.FEATURES.isEnabled("string-humanize")) { - var humanize = Ember.String.humanize; -} -if (Ember.FEATURES.isEnabled("string-parameterize")) { - var parameterize = Ember.String.parameterize; -} - if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.String) { /** See [Ember.String.fmt](/api/classes/Ember.String.html#method_fmt). @@ -11073,34 +11686,11 @@ */ String.prototype.capitalize = function() { return capitalize(this); }; - if (Ember.FEATURES.isEnabled("string-humanize")) { - /** - See [Ember.String.humanize](/api/classes/Ember.String.html#method_humanize). - - @method humanize - @for String - */ - String.prototype.humanize = function() { - return humanize(this); - }; - } - - if (Ember.FEATURES.isEnabled("string-parameterize")) { - /** - See [Ember.String.parameterize](/api/classes/Ember.String.html#method_parameterize). - - @method parameterize - @for String - */ - String.prototype.parameterize = function() { - return parameterize(this); - }; - } - + } })(); @@ -11697,11 +12287,13 @@ var desc = m.descs[keyName]; Ember.assert("Ember.Object.create no longer supports defining computed properties.", !(value instanceof Ember.ComputedProperty)); Ember.assert("Ember.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1)); - Ember.assert("`actions` must be provided at extend time, not at create time, when Ember.ActionHandler is used (i.e. views, controllers & routes).", !((keyName === 'actions') && Ember.ActionHandler.detect(this))); + Ember.assert("`actions` must be provided at extend time, not at create " + + "time, when Ember.ActionHandler is used (i.e. views, " + + "controllers & routes).", !((keyName === 'actions') && Ember.ActionHandler.detect(this))); if (concatenatedProperties && indexOf(concatenatedProperties, keyName) >= 0) { var baseValue = this[keyName]; if (baseValue) { @@ -11925,15 +12517,14 @@ @method willDestroy */ willDestroy: Ember.K, /** - @private - 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); @@ -12680,11 +13271,11 @@ @class ObjectProxy @namespace Ember @extends Ember.Object */ -Ember.ObjectProxy = Ember.Object.extend(/** @scope Ember.ObjectProxy.prototype */ { +Ember.ObjectProxy = Ember.Object.extend({ /** The object whose properties will be forwarded. @property content @type Ember.Object @@ -13469,11 +14060,11 @@ if (typeof callback !== "function") { throw new TypeError(); } var ret = initialValue; this.forEach(function(item, i) { - ret = callback(ret, item, i, this, reducerProperty); + ret = callback.call(null, ret, item, i, this, reducerProperty); }, this); return ret; }, @@ -13492,11 +14083,11 @@ if (arguments.length>1) 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](); + ret[idx] = args ? method.apply(x, args) : method.call(x); } }, this); return ret; }, @@ -13687,10 +14278,12 @@ 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 {Number} [start] optional start offset for the content change. + For unordered enumerables, you should always pass -1. @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 @@ -13793,11 +14386,11 @@ @class Array @namespace Ember @uses Ember.Enumerable @since Ember 0.9.0 */ -Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.prototype */ { +Ember.Array = Ember.Mixin.create(Ember.Enumerable, { /** Your array must support the `length` property. Your replace methods should set this property whenever it changes. @@ -13896,11 +14489,11 @@ 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). + @param {Integer} endIndex (Optional) index to end the slice at. @return {Array} New array with specified slice */ slice: function(beginIndex, endIndex) { var ret = Ember.A(); var length = get(this, 'length') ; @@ -14047,11 +14640,11 @@ /** Becomes true whenever the array currently has observers watching changes on the array. - @property {Boolean} hasArrayObservers + @property Boolean */ hasArrayObservers: Ember.computed(function() { return Ember.hasListeners(this, '@array:change') || Ember.hasListeners(this, '@array:before'); }), @@ -14196,13 +14789,10 @@ // testing, but there's no particular reason why it should be disallowed. eachPropertyPattern = /^(.*)\.@each\.(.*)/, doubleEachPropertyPattern = /(.*\.@each){2,}/, arrayBracketPattern = /\.\[\]$/; -if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - var expandProperties = Ember.expandProperties; -} function get(obj, key) { if (key === '@this') { return obj; } @@ -14656,13 +15246,11 @@ reset.call(this, cp, propertyName); meta.dependentArraysObserver.suspendArrayObservers(function () { forEach(cp._dependentKeys, function (dependentKey) { - - if (!partiallyRecomputeFor(this, dependentKey)) { return; } - + if (!partiallyRecomputeFor(this, dependentKey)) { return; } var dependentArray = get(this, dependentKey), previousDependentArray = meta.dependentArrays[dependentKey]; if (dependentArray === previousDependentArray) { @@ -14687,13 +15275,11 @@ } }, this); }, this); forEach(cp._dependentKeys, function(dependentKey) { - - if (!partiallyRecomputeFor(this, dependentKey)) { return; } - + if (!partiallyRecomputeFor(this, dependentKey)) { return; } var dependentArray = get(this, dependentKey); if (dependentArray) { addItems.call(this, dependentArray, callbacks, cp, propertyName, meta); } @@ -14784,21 +15370,14 @@ if (doubleEachPropertyPattern.test(dependentKey)) { throw new Ember.Error("Nested @each properties not supported: " + dependentKey); } else if (match = eachPropertyPattern.exec(dependentKey)) { dependentArrayKey = match[1]; - if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - var itemPropertyKeyPattern = match[2], - addItemPropertyKey = function (itemPropertyKey) { - cp.itemPropertyKey(dependentArrayKey, itemPropertyKey); - }; - - expandProperties(itemPropertyKeyPattern, addItemPropertyKey); - } else { + itemPropertyKey = match[2]; cp.itemPropertyKey(dependentArrayKey, itemPropertyKey); - } + propertyArgs.add(dependentArrayKey); } else { propertyArgs.add(dependentKey); } }); @@ -14905,11 +15484,11 @@ Example ```javascript Ember.computed.max = function (dependentKey) { - return Ember.reduceComputed(dependentKey, { + return Ember.reduceComputed.call(null, dependentKey, { initialValue: -Infinity, addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { return Math.max(accumulatedValue, item); }, @@ -15055,10 +15634,15 @@ 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 @@ -15242,11 +15826,11 @@ @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, { + return Ember.reduceComputed.call(null, dependentKey, { initialValue: -Infinity, addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { return Math.max(accumulatedValue, item); }, @@ -15290,11 +15874,11 @@ @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, { + return Ember.reduceComputed.call(null, dependentKey, { initialValue: Infinity, addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) { return Math.min(accumulatedValue, item); }, @@ -15681,11 +16265,11 @@ */ 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, { + return Ember.arrayComputed.call(null, setAProperty, setBProperty, { addedItem: function (array, item, changeMeta, instanceMeta) { var setA = get(this, setAProperty), setB = get(this, setBProperty); if (changeMeta.arrayChanged === setA) { @@ -15883,11 +16467,11 @@ instanceMeta.binarySearch = binarySearch; }; } - return Ember.arrayComputed(itemsKey, { + return Ember.arrayComputed.call(null, itemsKey, { initialize: initFn, addedItem: function (array, item, changeMeta, instanceMeta) { var index = instanceMeta.binarySearch(array, item); array.insertAt(index, item); @@ -15946,13 +16530,10 @@ @submodule ember-runtime */ var a_slice = Array.prototype.slice; -if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - var expandProperties = Ember.expandProperties; -} if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { /** The `property` extension of Javascript's Function prototype is available @@ -16045,22 +16626,13 @@ @method observes @for Function */ Function.prototype.observes = function() { - if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - var addWatchedProperty = function (obs) { watched.push(obs); }; - var watched = []; - - for (var i=0; i<arguments.length; ++i) { - expandProperties(arguments[i], addWatchedProperty); - } - - this.__ember_observes__ = watched; - } else { + this.__ember_observes__ = a_slice.call(arguments); - } + return this; }; /** @@ -16119,22 +16691,13 @@ @method observesBefore @for Function */ Function.prototype.observesBefore = function() { - if (Ember.FEATURES.isEnabled('propertyBraceExpansion')) { - var addWatchedProperty = function (obs) { watched.push(obs); }; - var watched = []; - - for (var i=0; i<arguments.length; ++i) { - expandProperties(arguments[i], addWatchedProperty); - } - - this.__ember_observesBefore__ = watched; - } else { + this.__ember_observesBefore__ = a_slice.call(arguments); - } + return this; }; /** @@ -16191,11 +16754,11 @@ @class Comparable @namespace Ember @since Ember 0.9 */ -Ember.Comparable = Ember.Mixin.create( /** @scope Ember.Comparable.prototype */{ +Ember.Comparable = Ember.Mixin.create({ /** Override to return the result of the comparison of the two parameters. The compare method should return: @@ -16242,11 +16805,11 @@ @class Copyable @namespace Ember @since Ember 0.9 */ -Ember.Copyable = Ember.Mixin.create(/** @scope Ember.Copyable.prototype */ { +Ember.Copyable = Ember.Mixin.create({ /** Override to return a copy of the receiver. Default implementation raises an exception. @@ -16346,11 +16909,11 @@ @class Freezable @namespace Ember @since Ember 0.9 */ -Ember.Freezable = Ember.Mixin.create(/** @scope Ember.Freezable.prototype */ { +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. @@ -16525,11 +17088,11 @@ @class MutableArray @namespace Ember @uses Ember.Array @uses Ember.MutableEnumerable */ -Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,/** @scope Ember.MutableArray.prototype */ { +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`. @@ -17125,11 +17688,11 @@ @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) { + then: function(resolve, reject, label) { var deferred, promise, entity; entity = this; deferred = get(this, '_deferred'); promise = deferred.promise; @@ -17140,11 +17703,11 @@ } else { return resolve(fulfillment); } } - return promise.then(resolve && fulfillmentHandler, reject); + return promise.then(resolve && fulfillmentHandler, reject, label); }, /** Resolve a Deferred object and call any `doneCallbacks` with the given args. @@ -17171,11 +17734,11 @@ reject: function(value) { get(this, '_deferred').reject(value); }, _deferred: Ember.computed(function() { - return RSVP.defer(); + return RSVP.defer('Ember: DeferredMixin - ' + this); }) }); })(); @@ -17203,24 +17766,141 @@ */ Ember.ActionHandler = Ember.Mixin.create({ mergedProperties: ['_actions'], /** - @private + 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(this + " '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'; @@ -17263,28 +17943,29 @@ (function() { var set = Ember.set, get = Ember.get, + resolve = Ember.RSVP.resolve, + rethrow = Ember.RSVP.rethrow, not = Ember.computed.not, or = Ember.computed.or; /** @module ember @submodule ember-runtime */ -function tap(proxy, promise) { - return promise.then(function(value) { +function observePromise(proxy, promise) { + 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; - }); + // don't re-throw, as we are merely observing + }, "Ember: PromiseProxy"); } /** A low level mixin making ObjectProxy, ObjectController or ArrayController's promise aware. @@ -17335,11 +18016,11 @@ // 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 + If the controller is backing a template, the attributes are bindable from within that template ```handlebars {{#if isPending}} loading... @@ -17349,31 +18030,119 @@ {{/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: <thenable> + }); + ``` + + @property promise + */ promise: Ember.computed(function(key, promise) { if (arguments.length === 2) { - return tap(this, promise); + promise = resolve(promise); + observePromise(this, promise); + return promise.then(); // fork the promise. } else { throw new Ember.Error("PromiseProxy's promise must be set"); } }), - then: function(fulfill, reject) { - return get(this, 'promise').then(fulfill, reject); - } -}); + /** + 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() { @@ -17491,16 +18260,17 @@ /** 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}` + * {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 = [], @@ -17517,11 +18287,11 @@ this._operations = [new ArrayOperation(RETAIN, items.length, items)]; }, /** - Return an ArrayOperationMatch for the operation that contains the item at `index`. + 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. @@ -17679,14 +18449,14 @@ /** Internal data structure to represent an array operation. @method ArrayOperation @private - @property {string} type The type of the operation. One of + @param {string} type The type of the operation. One of `Ember.TrackedArray.{RETAIN, INSERT, DELETE}` - @property {number} count The number of items in this operation. - @property {array} items The items of the operation, if included. RETAIN and + @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; @@ -17697,15 +18467,15 @@ Internal data structure used to include information when looking up operations by item index. @method ArrayOperationMatch @private - @property {ArrayOperation} operation - @property {number} index The index of `operation` in the array of operations. - @property {boolean} split Whether or not the item index searched for would + @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. - @property {number} rangeStart The index of the first item in the operation, + @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; @@ -17962,11 +18732,11 @@ @class ArrayProxy @namespace Ember @extends Ember.Object @uses Ember.MutableArray */ -Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,/** @scope Ember.ArrayProxy.prototype */ { +Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray, { /** The content array. Must be an object that implements `Ember.Array` and/or `Ember.MutableArray.` @@ -18016,15 +18786,14 @@ replaceContent: function(idx, amt, objects) { get(this, 'content').replace(idx, amt, objects); }, /** - @private - 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(); }), @@ -18042,15 +18811,14 @@ contentArrayWillChange: Ember.K, contentArrayDidChange: Ember.K, /** - @private - 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'); @@ -18745,11 +19513,11 @@ @uses Ember.Copyable @uses Ember.Freezable @since Ember 0.9 */ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Ember.Freezable, - /** @scope Ember.Set.prototype */ { + { // .......................................................... // IMPLEMENT ENUMERABLE APIS // @@ -19134,11 +19902,10 @@ 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(...); }); ``` @@ -19201,10 +19968,11 @@ 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, @@ -19815,11 +20583,11 @@ /** @module ember @submodule ember-views */ -var jQuery = (this && this.jQuery) || (Ember.imports && Ember.imports.jQuery); +var jQuery = this.jQuery || (Ember.imports && Ember.imports.jQuery); if (!jQuery && typeof require === 'function') { jQuery = require('jquery'); } Ember.assert("Ember Views require jQuery 1.7, 1.8, 1.9, 1.10, or 2.0", jQuery && (jQuery().jquery.match(/^((1\.(7|8|9|10))|2.0)(\.\d+)?(pre|rc\d?)?/) || Ember.ENV.FORCE_JQUERY)); @@ -19866,21 +20634,21 @@ // 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 &shy; -var needsShy = typeof document !== 'undefined' && (function() { +var needsShy = this.document && (function() { var testEl = document.createElement('div'); testEl.innerHTML = "<div></div>"; testEl.firstChild.innerHTML = "<script></script>"; 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 movesWhitespace = this.document && (function() { var testEl = document.createElement('div'); testEl.innerHTML = "Test: <script type='text/x-placeholder'></script>Value"; return testEl.childNodes[0].nodeValue === 'Test:' && testEl.childNodes[2].nodeValue === ' Value'; })(); @@ -20097,25 +20865,23 @@ Ember._RenderBuffer = function(tagName) { this.tagNames = [tagName || null]; this.buffer = ""; }; -Ember._RenderBuffer.prototype = -/** @scope Ember.RenderBuffer.prototype */ { +Ember._RenderBuffer.prototype = { // The root view's element _element: null, _hasElement: true, /** - @private - 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, @@ -20556,11 +21322,11 @@ @class EventDispatcher @namespace Ember @private @extends Ember.Object */ -Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.prototype */{ +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 @@ -20600,36 +21366,34 @@ drop : 'drop', dragend : 'dragEnd' }, /** - @private - 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', /** - @private - 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'); @@ -20657,12 +21421,10 @@ } } }, /** - @private - 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 @@ -20673,10 +21435,11 @@ ```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 */ @@ -20896,10 +21659,11 @@ @class CoreView @namespace Ember @extends Ember.Object @uses Ember.Evented + @uses Ember.ActionHandler */ Ember.CoreView = Ember.Object.extend(Ember.Evented, Ember.ActionHandler, { isView: true, @@ -20943,12 +21707,10 @@ instrumentDetails: function(hash) { hash.object = this.toString(); }, /** - @private - 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. @@ -20959,18 +21721,19 @@ @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() { + return Ember.instrument(name, details, function instrumentRenderToBuffer() { return this._renderToBuffer(parentBuffer, bufferOperation); }, this); }, _renderToBuffer: function(parentBuffer, bufferOperation) { @@ -20993,17 +21756,16 @@ return buffer; }, /** - @private - 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) { @@ -21685,20 +22447,19 @@ @class View @namespace Ember @extends Ember.CoreView */ -Ember.View = Ember.CoreView.extend( -/** @scope Ember.View.prototype */ { +Ember.View = Ember.CoreView.extend({ concatenatedProperties: ['classNames', 'classNameBindings', 'attributeBindings'], /** @property isView @type Boolean @default true - @final + @static */ isView: true, // .......................................................... // TEMPLATE SUPPORT @@ -21819,12 +22580,10 @@ return get(this, '_context'); } }).volatile(), /** - @private - 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: @@ -21836,10 +22595,11 @@ 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')) { @@ -21853,16 +22613,15 @@ return null; }), /** - @private - 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(); }), @@ -21874,18 +22633,17 @@ @default null */ isVisible: true, /** - @private - 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, @@ -21948,11 +22706,11 @@ }, /** Return the nearest ancestor that has a given property. - @property nearestWithProperty + @function nearestWithProperty @param {String} property A property name @return Ember.View */ nearestWithProperty: function(property) { var view = get(this, 'parentView'); @@ -21965,11 +22723,11 @@ /** Return the nearest ancestor whose parent is an instance of `klass`. - @property nearestChildOf + @method nearestChildOf @param {Class} klass Subclass of Ember.View (or Ember.View itself) @return Ember.View */ nearestChildOf: function(klass) { var view = get(this, 'parentView'); @@ -21979,15 +22737,14 @@ view = get(view, 'parentView'); } }, /** - @private - When the parent view changes, recursively invalidate `controller` @method _parentViewDidChange + @private */ _parentViewDidChange: Ember.observer('_parentView', function() { if (this.isDestroying) { return; } this.trigger('parentViewDidChange'); @@ -22098,18 +22855,17 @@ if (childViews[i]) { childViews[i].destroy(); } } }, /** - @private - 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; @@ -22176,17 +22932,16 @@ }, this); }, /** - @private - 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, elem; a_forEach(attributeBindings, function(binding) { @@ -22212,20 +22967,19 @@ Ember.View.applyAttributeBindings(buffer, attributeName, attributeValue); }, this); }, /** - @private - 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; @@ -22340,11 +23094,11 @@ 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} A selector, element, HTML string, or jQuery object + @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')); @@ -22356,12 +23110,10 @@ return this; }, /** - @private - 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. @@ -22377,10 +23129,11 @@ }); ``` @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); }, @@ -22488,17 +23241,16 @@ @event willClearRender */ willClearRender: Ember.K, /** - @private - Run this callback on the current view (unless includeSelf is false) and recursively on child views. @method invokeRecursively @param fn {Function} - @param includeSelf (optional, default true) + @param includeSelf {Boolean} Includes itself if true. + @private */ invokeRecursively: function(fn, includeSelf) { var childViews = (includeSelf === false) ? this._childViews : [this]; var currentViews, view, currentChildViews; @@ -22579,36 +23331,34 @@ @event willDestroyElement */ willDestroyElement: Ember.K, /** - @private - Triggers the `willDestroyElement` event (which invokes the `willDestroyElement()` method if it exists) on this view and all child views. Before triggering `willDestroyElement`, it first triggers the `willClearRender` event recursively. @method _notifyWillDestroyElement + @private */ _notifyWillDestroyElement: function() { var viewCollection = this.viewHierarchyCollection(); viewCollection.trigger('willClearRender'); viewCollection.trigger('willDestroyElement'); return viewCollection; }, /** - @private - If this view's element changes, we need to invalidate the caches of our child views so that we do not retain references to DOM elements that are no longer needed. @method _elementDidChange + @private */ _elementDidChange: Ember.observer('element', function() { this.forEachChildView(function(view) { delete meta(view).cache.element; }); @@ -22798,18 +23548,18 @@ // ....................................................... // CORE DISPLAY METHODS // /** - @private - 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 + + * 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(); @@ -22984,16 +23734,15 @@ becameVisible: Ember.K, becameHidden: Ember.K, /** - @private - When the view's `isVisible` property changes, toggle the visibility element of the actual DOM element. @method _isVisibleDidChange + @private */ _isVisibleDidChange: Ember.observer('isVisible', function() { var $el = this.$(); if (!$el) { return; } @@ -23070,17 +23819,16 @@ // ....................................................... // EVENT HANDLING // /** - @private - 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); }, @@ -23117,12 +23865,10 @@ * 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 @@ -23185,12 +23931,10 @@ }); Ember.View.reopenClass({ /** - @private - 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: @@ -23203,10 +23947,11 @@ } ``` @method _parsePropertyPath @static + @private */ _parsePropertyPath: function(path) { var split = path.split(':'), propertyPath = split[0], classNames = "", @@ -23229,12 +23974,10 @@ falsyClassName: falsyClassName }; }, /** - @private - 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, @@ -23252,10 +23995,11 @@ @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) { @@ -23331,10 +24075,14 @@ } } else if (name === 'value' || type === 'boolean') { // We can't set properties to undefined or null if (Ember.isNone(value)) { value = ''; } + if (!value) { + elem.removeAttr(name); + } + if (value !== elem.prop(name)) { // value and booleans should always be properties elem.prop(name, value); } } else if (!value) { @@ -23498,11 +24246,14 @@ 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."); + 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; }, @@ -23617,21 +24368,11 @@ 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 @@ -23949,14 +24690,13 @@ length: Ember.computed(function () { return this._childViews.length; }).volatile(), /** - @private - 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) { @@ -23965,18 +24705,17 @@ }, instrumentName: 'container', /** - @private - 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 **/ @@ -23995,20 +24734,19 @@ this.removeObject(child); return this; }, /** - @private - 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 @@ -24194,19 +24932,19 @@ in the item views receiving an appropriately matched `tagName` property. Given an empty `<body>` and the following code: ```javascript - anUnorderedListView = Ember.CollectionView.create({ + anUndorderedListView = Ember.CollectionView.create({ tagName: 'ul', content: ['A','B','C'], itemViewClass: Ember.View.extend({ template: Ember.Handlebars.compile("the letter: {{view.content}}") }) }); - anUnorderedListView.appendTo('body'); + anUndorderedListView.appendTo('body'); ``` Will result in the following HTML structure ```html @@ -24282,11 +25020,11 @@ @class CollectionView @namespace Ember @extends Ember.ContainerView @since Ember 0.9 */ -Ember.CollectionView = Ember.ContainerView.extend(/** @scope Ember.CollectionView.prototype */ { +Ember.CollectionView = Ember.ContainerView.extend({ /** A list of items to be displayed by the `Ember.CollectionView`. @property content @@ -24294,16 +25032,15 @@ @default null */ content: null, /** - @private - 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, /** @@ -24332,15 +25069,14 @@ this._contentDidChange(); return ret; }, /** - @private - 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'); @@ -24348,17 +25084,16 @@ var len = content ? get(content, 'length') : 0; this.arrayWillChange(content, 0, len); }), /** - @private - 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'); @@ -24370,14 +25105,13 @@ var len = content ? get(content, 'length') : 0; this.arrayDidChange(content, 0, null, len); }), /** - @private - 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)); }, @@ -24790,14 +25524,19 @@ 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'); + 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'); + 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; } @@ -24898,24 +25637,32 @@ [], function() { "use strict"; // ========================================================================== // Project: metamorph - // Copyright: ©2011 My Company Inc. All rights reserved. + // Copyright: ©2014 Tilde, Inc. All rights reserved. // ========================================================================== var K = function() {}, guid = 0, - disableRange = ('undefined' === typeof ENV ? {} : ENV).DISABLE_RANGE_API, + 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, + supportsRange = (!disableRange) && document && ('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 &shy; - needsShy = typeof document !== 'undefined' && (function() { + needsShy = document && (function() { var testEl = document.createElement('div'); testEl.innerHTML = "<div></div>"; testEl.firstChild.innerHTML = "<script></script>"; return testEl.firstChild.innerHTML === ''; })(), @@ -25015,11 +25762,11 @@ range.insertNode(fragment); }; /** * @public - * + * * Remove this object (including starting and ending * placeholders). * * @method remove */ @@ -25251,10 +25998,14 @@ // 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); @@ -25380,13 +26131,19 @@ 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); +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 @@ -25463,17 +26220,16 @@ Ember.Handlebars.registerBoundHelper.apply(null, arguments); } }; /** - @private - Returns a helper function that renders the provided ViewClass. Used internally by Ember.Handlebars.helper and other methods involving helper/component registration. + @private @method helper @for Ember.Handlebars @param {Function} ViewClass view class constructor */ Ember.Handlebars.makeViewHelper = function(ViewClass) { @@ -25526,16 +26282,15 @@ Ember.Handlebars.JavaScriptCompiler.prototype.initializeBuffer = function() { return "''"; }; /** - @private - 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+");"; @@ -25579,16 +26334,15 @@ }; var prefix = "ember" + (+new Date()), incr = 1; /** - @private - 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) { @@ -25674,15 +26428,14 @@ (function() { var slice = Array.prototype.slice, originalTemplate = Ember.Handlebars.template; /** - @private - 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} @@ -25732,34 +26485,20 @@ var handlebarsGet = Ember.Handlebars.get = function(root, path, options) { var data = options && options.data, normalizedPath = normalizePath(root, path, data), value; - if (Ember.FEATURES.isEnabled("ember-handlebars-caps-lookup")) { - - // If the path starts with a capital letter, look it up on Ember.lookup, - // which defaults to the `window` object in browsers. - if (Ember.isGlobalPath(path)) { - value = Ember.get(Ember.lookup, path); - } else { - - // In cases where the path begins with a keyword, change the - // root to the value represented by that keyword, and ensure - // the path is relative to it. - value = Ember.get(normalizedPath.root, normalizedPath.path); - } - - } else { + 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; }; Ember.Handlebars.resolveParams = function(context, params, options) { @@ -25796,19 +26535,18 @@ return resolvedHash; }; /** - @private - Registers a helper in Handlebars that will be called if no property with the given name can be found on the current context object, and no helper with that name is registered. This throws an exception with a more helpful error message so the user can track down where the problem is happening. + @private @method helperMissing @for Ember.Handlebars.helpers @param {String} path @param {Hash} options */ @@ -25829,29 +26567,32 @@ } throw new Ember.Error(Ember.String.fmt(error, [view, path, this])); }); /** - @private - Registers a helper in Handlebars that will be called if no property with the given name can be found on the current context object, and no helper with that name is registered. This throws an exception with a more helpful error message so the user can track down where the problem is happening. + @private @method helperMissing @for Ember.Handlebars.helpers @param {String} path @param {Hash} options */ Ember.Handlebars.registerHelper('blockHelperMissing', function(path) { var options = arguments[arguments.length - 1]; - Ember.assert("`blockHelperMissing` was invoked without a helper name, which is most likely due to a mismatch between the version of Ember.js you're running now and the one used to precompile your templates. Please make sure the version of `ember-handlebars-compiler` you're using is up to date.", path); + Ember.assert("`blockHelperMissing` was invoked without a helper name, which " + + "is most likely due to a mismatch between the version of " + + "Ember.js you're running now and the one used to precompile your " + + "templates. Please make sure the version of " + + "`ember-handlebars-compiler` you're using is up to date.", path); var helper = Ember.Handlebars.resolveHelper(options.data.view.container, path); if (helper) { return helper.apply(this, slice.call(arguments, 1)); @@ -25976,12 +26717,10 @@ boundFn = Ember.Handlebars.makeBoundHelper.apply(this, boundHelperArgs); Ember.Handlebars.registerHelper(name, boundFn); }; /** - @private - A (mostly) private helper function to `registerBoundHelper`. Takes the provided Handlebars helper function fn and returns it in wrapped bound helper form. The main use case for using this outside of `registerBoundHelper` @@ -25996,10 +26735,11 @@ ``` In the above example, if the helper function hadn't been wrapped in `makeBoundHelper`, the registered helper would be unbound. + @private @method makeBoundHelper @for Ember.Handlebars @param {Function} function @param {String} dependentKeys* */ @@ -26009,12 +26749,12 @@ function helper() { var properties = slice.call(arguments, 0, -1), numProperties = properties.length, options = arguments[arguments.length - 1], normalizedProperties = [], - types = options.types, data = options.data, + types = data.isUnbound ? slice.call(options.types, 1) : options.types, hash = options.hash, view = data.view, contexts = options.contexts, currentContext = (contexts && contexts.length) ? contexts[0] : this, prefixPathForDependentKeys = '', @@ -26041,11 +26781,15 @@ if (types[loc] === 'ID') { var normalizedProp = normalizePath(currentContext, properties[loc], data); normalizedProperties.push(normalizedProp); watchedProperties.push(normalizedProp); } else { - normalizedProperties.push(null); + if(data.isUnbound) { + normalizedProperties.push({path: properties[loc]}); + }else { + normalizedProperties.push(null); + } } } // Handle case when helper invocation is preceded by `unbound`, e.g. // {{unbound myHelper foo}} @@ -26119,42 +26863,53 @@ helper._rawFunction = fn; return helper; }; /** - @private - Renders the unbound form of an otherwise bound helper function. + @private @method evaluateUnboundHelper @param {Function} fn @param {Object} context @param {Array} normalizedProperties @param {String} options */ function evaluateUnboundHelper(context, fn, normalizedProperties, options) { - var args = [], hash = options.hash, boundOptions = hash.boundOptions, loc, len, property, boundOption; + var args = [], + hash = options.hash, + boundOptions = hash.boundOptions, + types = slice.call(options.types, 1), + loc, + len, + property, + propertyType, + boundOption; for (boundOption in boundOptions) { if (!boundOptions.hasOwnProperty(boundOption)) { continue; } hash[boundOption] = Ember.Handlebars.get(context, boundOptions[boundOption], options); } for(loc = 0, len = normalizedProperties.length; loc < len; ++loc) { property = normalizedProperties[loc]; - args.push(Ember.Handlebars.get(property.root, property.path, options)); + propertyType = types[loc]; + if(propertyType === "ID") { + args.push(Ember.Handlebars.get(property.root, property.path, options)); + } else { + args.push(property.path); + } } args.push(options); return fn.apply(context, args); } /** - @private - Overrides Handlebars.template so that we can distinguish user-created, top-level templates from inner contexts. + @private @method template @for Ember.Handlebars @param {String} spec */ Ember.Handlebars.template = function(spec) { @@ -26270,11 +27025,11 @@ replace: function(view) { var morph = view.morph; view.transitionTo('preRender'); - Ember.run.schedule('render', this, function() { + Ember.run.schedule('render', this, function renderMetamorphView() { if (view.isDestroying) { return; } view.clearRenderedChildren(); var buffer = view.renderToBuffer(); @@ -26639,16 +27394,10 @@ var shouldDisplay = get(this, 'shouldDisplayFunc'), preserveContext = get(this, 'preserveContext'), context = get(this, 'previousContext'); - var contextController; - - if (Ember.FEATURES.isEnabled('with-controller')) { - contextController = get(this, 'contextController'); - } - var inverseTemplate = get(this, 'inverseTemplate'), displayTemplate = get(this, 'displayTemplate'); var result = this.normalizedValue(); this._lastNormalizedValue = result; @@ -26664,16 +27413,10 @@ set(this, '_context', context); } else { // Otherwise, determine if this is a block bind or not. // If so, pass the specified object to the template if (displayTemplate) { - if (Ember.FEATURES.isEnabled('with-controller')) { - if (contextController) { - set(contextController, 'content', result); - result = contextController; - } - } set(this, '_context', result); } else { // This is not a bind block, just push the result of the // expression to the render context and return. if (result === null || result === undefined) { @@ -26770,18 +27513,10 @@ previousContext: currentContext, isEscaped: !options.hash.unescaped, templateData: options.data }); - if (Ember.FEATURES.isEnabled('with-controller')) { - if (options.hash.controller) { - bindView.set('contextController', this.container.lookupFactory('controller:'+options.hash.controller).create({ - container: currentContext - })); - } - } - view.appendChild(bindView); observer = function() { Ember.run.scheduleOnce('render', bindView, 'rerenderIfNeeded'); }; @@ -26852,31 +27587,19 @@ output = handlebarsGet(currentContext, property, options); data.buffer.push((output === null || typeof output === 'undefined') ? '' : output); } } -function shouldDisplayIfHelperContent(result) { - var truthy = result && get(result, 'isTruthy'); - if (typeof truthy === 'boolean') { return truthy; } - - if (Ember.isArray(result)) { - return get(result, 'length') !== 0; - } else { - return !!result; - } -} - /** - @private - '_triageMustache' is used internally select between a binding, helper, or component for the given context. Until this point, it would be hard to determine if the mustache is a property reference or a regular helper reference. This triage helper resolves that. This would not be typically invoked by directly. + @private @method _triageMustache @for Ember.Handlebars.helpers @param {String} property Property/helperID to triage @param {Object} options hash of template/rendering options @return {String} HTML string @@ -26915,12 +27638,10 @@ } return helper; }; /** - @private - `bind` can be used to display a value, then update that value if it changes. For example, if you wanted to print the `title` property of `content`: ```handlebars @@ -26932,10 +27653,11 @@ that if you need to support IE7 and IE8 you must modify the model objects properties using `Ember.get()` and `Ember.set()` for this to work as it relies on Ember's KVO system. For all other browsers this will be handled for you automatically. + @private @method bind @for Ember.Handlebars.helpers @param {String} property Property to bind @param {Function} fn Context to provide for rendering @return {String} HTML string @@ -26951,69 +27673,93 @@ return bind.call(context, property, options, false, exists); }); /** - @private - Use the `boundIf` helper to create a conditional that re-evaluates whenever the truthiness of the bound value changes. ```handlebars {{#boundIf "content.shouldDisplayTitle"}} {{content.title}} {{/boundIf}} ``` + @private @method boundIf @for Ember.Handlebars.helpers @param {String} property Property to bind @param {Function} fn Context to provide for rendering @return {String} HTML string */ EmberHandlebars.registerHelper('boundIf', function boundIfHelper(property, fn) { var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this; + var func = function(result) { + var truthy = result && get(result, 'isTruthy'); + if (typeof truthy === 'boolean') { return truthy; } - return bind.call(context, property, fn, true, shouldDisplayIfHelperContent, shouldDisplayIfHelperContent, ['isTruthy', 'length']); + if (Ember.isArray(result)) { + return get(result, 'length') !== 0; + } else { + return !!result; + } + }; + + return bind.call(context, property, fn, true, func, func, ['isTruthy', 'length']); }); - /** - @private + Use the `{{with}}` helper when you want to scope context. Take the following code as an example: - Use the `unboundIf` helper to create a conditional that evaluates once. + ```handlebars + <h5>{{user.name}}</h5> + <div class="role"> + <h6>{{user.role.label}}</h6> + <span class="role-id">{{user.role.id}}</span> + + <p class="role-desc">{{user.role.description}}</p> + </div> + ``` + + `{{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 - {{#unboundIf "content.shouldDisplayTitle"}} - {{content.title}} - {{/unboundIf}} + <h5>{{user.name}}</h5> + + <div class="role"> + {{#with user.role}} + <h6>{{label}}</h6> + <span class="role-id">{{id}}</span> + + <p class="role-desc">{{description}}</p> + {{/with}} + </div> ``` - @method unboundIf - @for Ember.Handlebars.helpers - @param {String} property Property to bind - @param {Function} fn Context to provide for rendering - @return {String} HTML string -*/ -EmberHandlebars.registerHelper('unboundIf', function unboundIfHelper(property, fn) { - var context = (fn.contexts && fn.contexts.length) ? fn.contexts[0] : this, - data = fn.data, - template = fn.fn, - inverse = fn.inverse, - normalized, propertyValue, result; + ### `as` operator - normalized = normalizePath(context, property, data); - propertyValue = handlebarsGet(context, property, fn); + 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. - if (!shouldDisplayIfHelperContent(propertyValue)) { - template = inverse; - } + ```handlebars + // posts might not be + {{#with user.posts as blogPosts}} + <div class="notice"> + There are {{blogPosts.length}} blog posts written by {{user.name}}. + </div> - template(context, { data: data }); -}); + {{#each post in blogPosts}} + <li>{{post.title}}</li> + {{/each}} + {{/with}} + ``` -/** + Without the `as` operator, it would be impossible to reference `user.name` in the example above. + @method with @for Ember.Handlebars.helpers @param {Function} context @param {Hash} options @return {String} HTML string @@ -27059,26 +27805,22 @@ }); /** 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); - } + + return helpers.boundIf.call(options.contexts[0], context, options); }); /** @method unless @for Ember.Handlebars.helpers @@ -27093,15 +27835,11 @@ 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); - } + 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: @@ -27269,11 +28007,13 @@ 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'); + 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. @@ -27325,12 +28065,10 @@ Ember.warn("The 'bindAttr' view helper is deprecated in favor of 'bind-attr'"); return EmberHandlebars.helpers['bind-attr'].apply(this, arguments); }); /** - @private - 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. @@ -27338,10 +28076,11 @@ "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 @@ -27872,11 +28611,11 @@ <div class="ember-view">Hi Mary</div> <div class="ember-view">Hi Sara</div> </div> ``` - ### Blockless use in a collection + ### Blockless Use If you provide an `itemViewClass` option that has its own `template` you can omit the block. The following template: @@ -27983,14 +28722,21 @@ var collectionPrototype = collectionClass.proto(), itemViewClass; if (hash.itemView) { var 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); + 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); var container = controller.container; itemViewClass = container.resolve('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); + 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; } @@ -28081,11 +28827,11 @@ 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.helperMissing; + 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; } @@ -28181,11 +28927,10 @@ @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; @@ -28214,12 +28959,12 @@ 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)); + Ember.assert("The value that #each loops over must be an Array. You passed " + content.constructor + ", but it should have been an ArrayController", !Ember.ControllerMixin.detect(content) || (content && content.isGenerated) || content instanceof Ember.ArrayController); + Ember.assert("The value that #each loops over must be an Array. You passed " + ((Ember.ControllerMixin.detect(content) && content.get('model') !== undefined) ? ("" + content.get('model') + " (wrapped in " + content + ")") : ("" + content)), Ember.Array.detect(content)); }, disableContentObservers: function(callback) { Ember.removeBeforeObserver(this, 'content', null, '_contentWillChange'); Ember.removeObserver(this, 'content', null, '_contentDidChange'); @@ -29157,11 +29902,11 @@ /** 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. + 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 @@ -29170,16 +29915,15 @@ @class TextField @namespace Ember @extends Ember.Component @uses Ember.TextSupport */ -Ember.TextField = Ember.Component.extend(Ember.TextSupport, - /** @scope Ember.TextField.prototype */ { +Ember.TextField = Ember.Component.extend(Ember.TextSupport, { classNames: ['ember-text-field'], tagName: "input", - attributeBindings: ['type', 'value', 'size', 'pattern', 'name', 'min', 'max'], + attributeBindings: ['type', 'value', 'size', 'pattern', 'name'], /** The `value` attribute of the input element. As the user inputs text, this property is updated live. @@ -29206,35 +29950,145 @@ @default null */ size: null, /** - The `pattern` attribute of input element. + The `pattern` the pattern attribute of input element. @property pattern @type String @default null */ - pattern: 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 +(function() { +/* +@module ember +@submodule ember-handlebars +*/ + +var get = Ember.get, set = Ember.set; + +/* + @class Button + @namespace Ember + @extends Ember.View + @uses Ember.TargetActionSupport + @deprecated +*/ +Ember.Button = Ember.View.extend(Ember.TargetActionSupport, { + classNames: ['ember-button'], + classNameBindings: ['isActive'], + + tagName: 'button', + + propagateEvents: false, + + attributeBindings: ['type', 'disabled', 'href', 'tabindex'], + + /* + @private + + Overrides `TargetActionSupport`'s `targetObject` computed + property to use Handlebars-specific path resolution. + + @property targetObject */ - max: null + targetObject: Ember.computed(function() { + var target = get(this, 'target'), + root = get(this, 'context'), + data = get(this, 'templateData'); + + if (typeof target !== 'string') { return target; } + + return Ember.Handlebars.get(root, target, { data: data }); + }).property('target'), + + // Defaults to 'button' if tagName is 'input' or 'button' + type: Ember.computed(function(key) { + var tagName = this.tagName; + if (tagName === 'input' || tagName === 'button') { return 'button'; } + }), + + disabled: false, + + // Allow 'a' tags to act like buttons + href: Ember.computed(function() { + return this.tagName === 'a' ? '#' : null; + }), + + mouseDown: function() { + if (!get(this, 'disabled')) { + set(this, 'isActive', true); + this._mouseDown = true; + this._mouseEntered = true; + } + return get(this, 'propagateEvents'); + }, + + mouseLeave: function() { + if (this._mouseDown) { + set(this, 'isActive', false); + this._mouseEntered = false; + } + }, + + mouseEnter: function() { + if (this._mouseDown) { + set(this, 'isActive', true); + this._mouseEntered = true; + } + }, + + mouseUp: function(event) { + if (get(this, 'isActive')) { + // Actually invoke the button's target and action. + // This method comes from the Ember.TargetActionSupport mixin. + this.triggerAction(); + set(this, 'isActive', false); + } + + this._mouseDown = false; + this._mouseEntered = false; + return get(this, 'propagateEvents'); + }, + + keyDown: function(event) { + // Handle space or enter + if (event.keyCode === 13 || event.keyCode === 32) { + this.mouseDown(); + } + }, + + keyUp: function(event) { + // Handle space or enter + if (event.keyCode === 13 || event.keyCode === 32) { + this.mouseUp(); + } + }, + + // TODO: Handle proper touch behavior. Including should make inactive when + // finger moves more than 20x outside of the edge of the button (vs mouse + // which goes inactive as soon as mouse goes out of edges.) + + touchStart: function(touch) { + return this.mouseDown(touch); + }, + + touchEnd: function(touch) { + return this.mouseUp(touch); + }, + + init: function() { + Ember.deprecate("Ember.Button is deprecated and will be removed from future releases. Consider using the `{{action}}` helper."); + this._super(); + } }); })(); @@ -29616,27 +30470,26 @@ @class Select @namespace Ember @extends Ember.View */ -Ember.Select = Ember.View.extend( - /** @scope Ember.Select.prototype */ { - +Ember.Select = Ember.View.extend({ tagName: 'select', classNames: ['ember-select'], defaultTemplate: Ember.Handlebars.template(function anonymous(Handlebars,depth0,helpers,partials,data) { this.compilerInfo = [4,'>= 1.0.0']; helpers = this.merge(helpers, Ember.Handlebars.helpers); data = data || {}; var buffer = '', stack1, hashTypes, hashContexts, escapeExpression=this.escapeExpression, self=this; function program1(depth0,data) { - var buffer = '', hashTypes, hashContexts; + var buffer = '', stack1, hashTypes, hashContexts; data.buffer.push("<option value=\"\">"); hashTypes = {}; hashContexts = {}; - data.buffer.push(escapeExpression(helpers._triageMustache.call(depth0, "view.prompt", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}))); + stack1 = helpers._triageMustache.call(depth0, "view.prompt", {hash:{},contexts:[depth0],types:["ID"],hashContexts:hashContexts,hashTypes:hashTypes,data:data}); + if(stack1 || stack1 === 0) { data.buffer.push(stack1); } data.buffer.push("</option>"); return buffer; } function program3(depth0,data) { @@ -30329,21 +31182,20 @@ @module ember @submodule ember-handlebars */ /** - @private - 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 */ @@ -32517,34 +33369,22 @@ } else { this.push(options.path, name, null, options.queryParams); } - if (Ember.FEATURES.isEnabled("ember-routing-named-substates")) { - // For namespace-preserving nested resource (e.g. resource('foo.bar') within - // resource('foo')) we only want to use the last route name segment to determine - // the names of the error/loading substates (e.g. 'bar_loading') - name = name.split('.').pop(); - route(this, name + '_loading'); - route(this, name + '_error', { path: "/_unused_dummy_error_path_route_" + name + "/:error" }); - } - }, + }, push: function(url, name, callback, queryParams) { var parts = name.split('.'); if (url === "" || url === "/" || parts[parts.length-1] === "index") { this.explicitIndex = true; } this.matches.push([url, name, callback, queryParams]); }, route: function(name, options) { route(this, name, options); - if (Ember.FEATURES.isEnabled("ember-routing-named-substates")) { - route(this, name + '_loading'); - route(this, name + '_error', { path: "/_unused_dummy_error_path_route_" + name + "/:error" }); - } - }, + }, generate: function() { var dslMatches = this.matches; if (!this.explicitIndex) { @@ -32553,16 +33393,11 @@ return function(match) { for (var i=0, l=dslMatches.length; i<l; i++) { var dslMatch = dslMatches[i]; var matchObj = match(dslMatch[0]).to(dslMatch[1], dslMatch[2]); - if (Ember.FEATURES.isEnabled("query-params")) { - if(dslMatch[3]) { - matchObj.withQueryParams.apply(matchObj, dslMatch[3]); - } - } - } + } }; } }; function route(dsl, name, options) { @@ -32702,10 +33537,24 @@ @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', init: function() { this.router = this.constructor.router || this.constructor.map(Ember.K); this._activeViews = {}; @@ -32714,14 +33563,27 @@ 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. + + @method startRouting + @private + */ startRouting: function() { this.router = this.router || this.constructor.map(Ember.K); var router = this.router, location = get(this, 'location'), @@ -32738,10 +33600,19 @@ }); this.handleURL(location.getURL()); }, + /** + 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(); @@ -32782,35 +33653,56 @@ 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); }, /** - @private - Resets the state of the router by clearing the current route handlers and deactivating them. + @private @method reset */ reset: function() { this.router.reset(); }, + willDestroy: function(){ + var location = get(this, 'location'); + location.destroy(); + + this._super.apply(this, arguments); + }, + _lookupActiveView: function(templateName) { var active = this._activeViews[templateName]; return active && active[0]; }, @@ -32829,27 +33721,20 @@ view.one('willDestroyElement', this, disconnect); }, _setupLocation: function() { var location = get(this, 'location'), - rootURL = get(this, 'rootURL'); + rootURL = get(this, 'rootURL'), + options = {}; - 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 (typeof rootURL === 'string') { + options.rootURL = rootURL; } - if (typeof rootURL === 'string') { - location.rootURL = rootURL; + if ('string' === typeof location) { + options.implementation = location; + location = set(this, 'location', Ember.Location.create(options)); } // ensure that initState is called AFTER the rootURL is set on // the location instance if (typeof location.initState === 'function') { location.initState(); } @@ -32918,14 +33803,11 @@ args[0] = args[0] || '/'; var passedName = args[0], name, self = this, isQueryParamsOnly = false; - if (Ember.FEATURES.isEnabled("query-params")) { - isQueryParamsOnly = (args.length === 1 && args[0].hasOwnProperty('queryParams')); - } - + if (!isQueryParamsOnly && passedName.charAt(0) === '/') { name = passedName; } else if (!isQueryParamsOnly) { if (!this.router.hasRoute(passedName)) { name = args[0] = passedName + '.index'; @@ -32940,11 +33822,11 @@ transitionPromise.then(null, function(error) { if (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; @@ -32972,17 +33854,17 @@ this._loadingStateTimer = null; } }); /** - @private - 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.handlerInfos, originRouteFound = false; @@ -33071,18 +33953,11 @@ var router = parentRoute.router, childName, targetChildRouteName = originatingChildRoute.routeName.split('.').pop(), namespace = parentRoute.routeName === 'application' ? '' : parentRoute.routeName + '.'; - if (Ember.FEATURES.isEnabled("ember-routing-named-substates")) { - // First, try a named loading state, e.g. 'foo_loading' - childName = namespace + targetChildRouteName + '_' + name; - if (routeHasBeenDefined(router, childName)) { - return childName; - } - } - + // Second, try general loading state, e.g. 'loading' childName = namespace + name; if (routeHasBeenDefined(router, childName)) { return childName; } @@ -33240,10 +34115,11 @@ the [routing guide](http://emberjs.com/guides/routing/) for documentation. @class Route @namespace Ember @extends Ember.Object + @uses Ember.ActionHandler */ Ember.Route = Ember.Object.extend(Ember.ActionHandler, { /** @private @@ -33417,25 +34293,22 @@ `transition.abort()`. ### `error` When attempting to transition into a route, any of the hooks - may throw an error, or return a promise that rejects, at which - point an `error` action will be fired on the partially-entered - routes, allowing for per-route error handling logic, or shared - error handling logic defined on a parent route. + may return a promise that rejects, at which point an `error` + action will be fired on the partially-entered routes, allowing + for per-route error handling logic, or shared error handling + logic defined on a parent route. Here is an example of an error handler that will be invoked - for rejected promises / thrown errors from the various hooks - on the route, as well as any unhandled errors from child - routes: + for rejected promises from the various hooks on the route, + as well as any unhandled errors from child routes: ```js App.AdminRoute = Ember.Route.extend({ beforeModel: function() { - throw "bad things!"; - // ...or, equivalently: return Ember.RSVP.reject("bad things!"); }, actions: { error: function(error, transition) { @@ -33468,18 +34341,18 @@ } } }); ``` - @see {Ember.Route#send} - @see {Handlebars.helpers.action} - @property actions @type Hash @default null */ - actions: null, + _actions: { + finalizeQueryParamChange: function(params, finalParams) { + } + }, /** @deprecated Please use `actions` instead. @@ -33672,14 +34545,13 @@ send: function() { return this.router.send.apply(this.router, arguments); }, /** - @private - This hook is the entry point for router.js + @private @method setup */ setup: function(context, queryParams) { var controllerName = this.controllerName || this.routeName, controller = this.controllerFor(controllerName, true); @@ -33691,14 +34563,11 @@ // referenced in action handlers this.controller = controller; var args = [controller, context]; - if (Ember.FEATURES.isEnabled("query-params")) { - args.push(queryParams); - } - + if (this.setupControllers) { Ember.deprecate("Ember.Route.setupControllers is deprecated. Please use Ember.Route.setupController(controller, model) instead."); this.setupControllers(controller, context); } else { this.setupController.apply(this, args); @@ -33843,14 +34712,13 @@ this.redirect(resolvedModel, transition); }, /** - @private - Called when the context is changed by router.js. + @private @method contextDidChange */ contextDidChange: function() { this.currentModel = this.context; }, @@ -33953,11 +34821,14 @@ return { find: function(name, value) { var modelClass = container.lookupFactory('model:' + name); - Ember.assert("You used the dynamic segment " + name + "_id in your route "+ routeName + ", but " + namespace + "." + classify(name) + " did not exist and you did not override your route's `model` hook.", modelClass); + Ember.assert("You used the dynamic segment " + name + "_id in your route " + + routeName + ", but " + namespace + "." + classify(name) + + " did not exist and you did not override your route's `model` " + + "hook.", modelClass); return modelClass.find(value); } }; }), @@ -34098,11 +34969,15 @@ controller = container.lookup('controller:' + name); // NOTE: We're specifically checking that skipAssert is true, because according // to the old API the second parameter was model. We do not want people who // passed a model to skip the assertion. - Ember.assert("The controller named '"+name+"' could not be found. Make sure that this route exists and has already been entered at least once. If you are accessing a controller not associated with a route, make sure the controller class is explicitly defined.", controller || _skipAssert === true); + Ember.assert("The controller named '"+name+"' could not be found. Make sure " + + "that this route exists and has already been entered at least " + + "once. If you are accessing a controller not associated with a " + + "route, make sure the controller class is explicitly defined.", + controller || _skipAssert === true); return controller; }, /** @@ -34561,11 +35436,14 @@ function fullRouteName(router, name) { var nameWithIndex; if (!router.hasRoute(name)) { nameWithIndex = name + '.index'; - Ember.assert(fmt("The attempt to link-to route '%@' failed (also tried '%@'). The router did not find '%@' in its possible routes: '%@'", [name, nameWithIndex, name, Ember.keys(router.router.recognizer.names).join("', '")]), router.hasRoute(nameWithIndex)); + Ember.assert(fmt("The attempt to link-to route '%@' failed (also tried '%@'). " + + "The router did not find '%@' in its possible routes: '%@'", + [name, nameWithIndex, name, Ember.keys(router.router.recognizer.names).join("', '")]), + router.hasRoute(nameWithIndex)); name = nameWithIndex; } return name; } @@ -34727,35 +35605,27 @@ // Map desired event name to invoke function var eventName = get(this, 'eventName'), i; this.on(eventName, this, this._invoke); - if (Ember.FEATURES.isEnabled("query-params")) { - var queryParams = get(this, '_potentialQueryParams') || []; + }, - for(i=0; i < queryParams.length; i++) { - this.registerObserver(this, queryParams[i], this, this._queryParamsChanged); - } - } - }, - /** - @private - This method is invoked by observers installed during `init` that fire whenever the params change + + @private @method _paramsChanged */ _paramsChanged: function() { this.notifyPropertyChange('resolvedParams'); }, /** - @private - This is called to setup observers that will trigger a rerender. + @private @method _setupPathObservers **/ _setupPathObservers: function(){ var helperParameters = this.parameters, linkTextPath = helperParameters.options.linkTextPath, @@ -34784,26 +35654,24 @@ this._super.apply(this, arguments); this._setupPathObservers(); }, /** - @private - This method is invoked by observers installed during `init` that fire whenever the query params change + @private */ _queryParamsChanged: function (object, path) { this.notifyPropertyChange('queryParams'); }, /** - @private - Even though this isn't a virtual view, we want to treat it as if it is so that you can access the parent with {{view.prop}} + @private @method concreteView **/ concreteView: Ember.computed(function() { return get(this, 'parentView'); }).property('parentView'), @@ -34861,25 +35729,23 @@ loading: Ember.computed(function computeLinkViewLoading() { if (!get(this, 'routeArgs')) { return get(this, 'loadingClass'); } }).property('routeArgs'), /** - @private - Returns the application's main router from the container. + @private @property router **/ router: Ember.computed(function() { return get(this, 'controller').container.lookup('router:main'); }), /** - @private - Event handler that invokes the link, activating the associated route. + @private @method _invoke @param {Event} event */ _invoke: function(event) { if (!isSimpleClick(event)) { return true; } @@ -34903,42 +35769,32 @@ router.transitionTo.apply(router, routeArgs); } }, /** - @private - Computed property that returns the resolved parameters. + @private @property @return {Array} */ resolvedParams: Ember.computed(function() { var parameters = this.parameters, options = parameters.options, types = options.types, data = options.data; - if (Ember.FEATURES.isEnabled("query-params")) { - 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 }); - } - } - + // Original implementation if query params not enabled return resolveParams(parameters.context, parameters.params, { types: types, data: data }); }).property(), /** - @private - 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), @@ -34956,16 +35812,11 @@ // If contexts aren't present, consider the linkView unloaded. return; } } - if (Ember.FEATURES.isEnabled("query-params")) { - var queryParams = get(this, 'queryParams'); - - if (queryParams || queryParams === false) { resolvedParams.push({queryParams: queryParams}); } - } - + return resolvedParams; }).property('resolvedParams', 'queryParams', 'router.url'), _potentialQueryParams: Ember.computed(function () { @@ -35248,11 +36099,11 @@ 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']}} + {{#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 @@ -35399,15 +36250,15 @@ this.render('posts', { outlet: 'posts' }); } }); ``` - You can specify the view class that the outlet uses to contain and manage the + You can specify the view that the outlet uses to contain and manage the templates rendered into it. ``` handlebars - {{outlet viewClass=App.SectionContainer}} + {{outlet view='sectionContainer'}} ``` ``` javascript App.SectionContainer = Ember.ContainerView.extend({ tagName: 'section', @@ -35420,28 +36271,44 @@ @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, outletContainerClass; + + 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'); } - outletContainerClass = options.hash.viewClass || Handlebars.OutletView; + // 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, outletContainerClass, options); + return Handlebars.helpers.view.call(this, viewClass, options); }); }); })(); @@ -35526,11 +36393,11 @@ @param {Hash} options @return {String} HTML string */ Ember.Handlebars.registerHelper('render', function renderHelper(name, contextString, options) { var length = arguments.length; - + Ember.assert("You must pass a template to render", length >= 2); var contextProvided = length === 3, container, router, controller, view, context, lookupOptions; container = (options || contextString).data.keywords.controller.container; router = container.lookup('router:main'); @@ -35560,27 +36427,31 @@ if (options.hash.controller) { Ember.assert("The controller name you supplied '" + controllerName + "' did not resolve to a controller.", container.has(controllerFullName)); } - var target = options.data.keywords.controller; + 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, - target: target + parentController: parentController, + target: parentController }); } else { controller = container.lookup(controllerFullName) || Ember.generateController(container, controllerName); - controller.set('target', target); + controller.setProperties({ + target: parentController, + parentController: parentController + }); } var root = options.contexts[1]; if (root) { @@ -35588,11 +36459,15 @@ controller.set('model', Ember.Handlebars.get(root, contextString, options)); }); } options.hash.viewName = Ember.String.camelize(name); - options.hash.template = container.lookup('template:' + 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.hash.template = container.lookup(templateName); + options.hash.controller = controller; if (router && !context) { router._connectActiveView(name, view); } @@ -36075,13 +36950,15 @@ var myView = MyView.create(); myView.appendTo('body'); // The html for myView now looks like: // <div id="ember228" class="ember-view">Child view: </div> - myView.connectOutlet('main', Ember.View.extend({ + var FooView = Ember.View.extend({ template: Ember.Handlebars.compile('<h1>Foo</h1> ') - })); + }); + var fooView = FooView.create(); + myView.connectOutlet('main', fooView); // The html for myView now looks like: // <div id="ember228" class="ember-view">Child view: // <div id="ember234" class="ember-view"><h1>Foo</h1> </div> // </div> ``` @@ -36110,16 +36987,15 @@ router._connectActiveView(renderedName, view); } }, /** - @private - 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} */ @@ -36143,13 +37019,15 @@ var myView = MyView.create(); myView.appendTo('body'); // myView's html: // <div id="ember228" class="ember-view">Child view: </div> - myView.connectOutlet('main', Ember.View.extend({ + var FooView = Ember.View.extend({ template: Ember.Handlebars.compile('<h1>Foo</h1> ') - })); + }); + var fooView = FooView.create(); + myView.connectOutlet('main', fooView); // myView's html: // <div id="ember228" class="ember-view">Child view: // <div id="ember234" class="ember-view"><h1>Foo</h1> </div> // </div> @@ -36168,15 +37046,14 @@ this._pendingDisconnections[outletName] = true; Ember.run.once(this, '_finishDisconnections'); }, /** - @private - Gets an outlet that is pending disconnection and then nullifys the object on the `_outlet` object. + @private @method _finishDisconnections */ _finishDisconnections: function() { var outlets = get(this, '_outlets'); var pendingDisconnections = this._pendingDisconnections; @@ -36217,53 +37094,105 @@ @submodule ember-routing */ var get = Ember.get, set = Ember.set; -/* - This file implements the `location` API used by Ember's router. +/** + Ember.Location returns an instance of the correct implementation of + the `location` API. - That API is: + ## Implementations - 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 + You can pass an implementation name (`hash`, `history`, `none`) to force a + particular implementation to be used in your application. - Calling setURL or replaceURL will not trigger onUpdateURL callbacks. + ### HashLocation - TODO: This should perhaps be moved so that it's visible in the doc output. -*/ + 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. -/** - Ember.Location returns an instance of the correct implementation of - the `location` API. + Example: - You can pass it a `implementation` ('hash', 'history', 'none') to force a - particular implementation. + ```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`. + + ### 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 = { /** - Create an instance of a an implementation of the `location` API. Requires - an options object with an `implementation` property. + This is deprecated in favor of using the container to lookup the location + implementation as desired. - Example + For example: ```javascript - var hashLocation = Ember.Location.create({implementation: 'hash'}); - var historyLocation = Ember.Location.create({implementation: 'history'}); - var noneLocation = Ember.Location.create({implementation: 'none'}); + // 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); @@ -36272,31 +37201,32 @@ return implementationClass.create.apply(implementationClass, arguments); }, /** - Registers a class that implements the `location` API with an implementation - name. This implementation name can then be specified by the location property on - the application's router class. + This is deprecated in favor of using the container to register the + location implementation as desired. - Example + Example: ```javascript - Ember.Location.registerImplementation('history', Ember.HistoryLocation); + Application.initializer({ + name: "history-test-location", - App.Router.reopen({ - location: 'history' + initialize: function(container, application) { + application.register('location:history-test', HistoryTestLocation); + } }); ``` - @method registerImplementation - @param {String} name - @param {Object} implementation of the `location` API + @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: {} }; @@ -36322,74 +37252,68 @@ @class NoneLocation @namespace Ember @extends Ember.Object */ Ember.NoneLocation = Ember.Object.extend({ - implementation: 'none', path: '', /** - @private - Returns the current path. + @private @method getURL @return {String} path */ getURL: function() { return get(this, 'path'); }, /** - @private - 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); }, /** - @private - 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; }, /** - @private - Sets the path and calls the `updateURL` callback. + @private @method handleURL @param callback {Function} */ handleURL: function(url) { set(this, 'path', url); this.updateCallback(url); }, /** - @private - 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) { @@ -36398,10 +37322,12 @@ // helpers. return url; } }); +Ember.Location.registerImplementation('none', Ember.NoneLocation); + })(); (function() { @@ -36411,84 +37337,67 @@ */ 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 + `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 the current `location.hash`, minus the '#' at the front. + @private @method getURL */ getURL: function() { - if (Ember.FEATURES.isEnabled("query-params")) { - // location.hash is not used because it is inconsistently - // URL-decoded between browsers. - var href = get(this, 'location').href, - hashIndex = href.indexOf('#'); - - if ( hashIndex === -1 ) { - return ""; - } else { - return href.substr(hashIndex + 1); - } - } - // Default implementation without feature flag enabled + // Default implementation without feature flag enabled return get(this, 'location').hash.substr(1); }, /** - @private - 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); }, /** - @private - 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); }, /** - @private - 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; @@ -36505,39 +37414,39 @@ }); }); }, /** - @private - 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; }, /** - @private - Cleans up the HashLocation event listener. + @private @method willDestroy */ willDestroy: function() { var guid = Ember.guidFor(this); Ember.$(window).off('hashchange.ember-location-'+guid); } }); +Ember.Location.registerImplementation('hash', Ember.HashLocation); + })(); (function() { @@ -36557,21 +37466,19 @@ @class HistoryLocation @namespace Ember @extends Ember.Object */ Ember.HistoryLocation = Ember.Object.extend({ - implementation: 'history', init: function() { set(this, 'location', get(this, 'location') || window.location); }, /** - @private - 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())); @@ -36584,14 +37491,13 @@ @default '/' */ rootURL: '/', /** - @private + Returns the current `location.pathname` without `rootURL`. - Returns the current `location.pathname` without rootURL - + @private @method getURL @return url {String} */ getURL: function() { var rootURL = get(this, 'rootURL'), @@ -36599,23 +37505,18 @@ path = location.pathname; rootURL = rootURL.replace(/\/$/, ''); var url = path.replace(rootURL, ''); - if (Ember.FEATURES.isEnabled("query-params")) { - var search = location.search || ''; - url += search; - } - + return url; }, /** - @private - Uses `history.pushState` to update the url without a page reload. + @private @method setURL @param path {String} */ setURL: function(path) { var state = this.getState(); @@ -36625,15 +37526,14 @@ this.pushState(path); } }, /** - @private - 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(); @@ -36643,28 +37543,26 @@ this.replaceState(path); } }, /** - @private - Get the current `history.state` Polyfill checks for native browser support and falls back to retrieving from a private _historyState variable + @private @method getState @return state {Object} */ getState: function() { return supportsHistoryState ? get(this, 'history').state : this._historyState; }, /** - @private + Pushes a new state. - Pushes a new state - + @private @method pushState @param path {String} */ pushState: function(path) { var state = { path: path }; @@ -36679,14 +37577,13 @@ // used for webkit workaround this._previousURL = this.getURL(); }, /** - @private + Replaces the current state. - Replaces the current state - + @private @method replaceState @param path {String} */ replaceState: function(path) { var state = { path: path }; @@ -36701,15 +37598,14 @@ // used for webkit workaround this._previousURL = this.getURL(); }, /** - @private - 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), @@ -36724,14 +37620,13 @@ callback(self.getURL()); }); }, /** - @private - 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) { @@ -36743,23 +37638,24 @@ return rootURL + url; }, /** - @private - Cleans up the HistoryLocation event listener. + @private @method willDestroy */ willDestroy: function() { var guid = Ember.guidFor(this); Ember.$(window).off('popstate.ember-location-'+guid); } }); +Ember.Location.registerImplementation('history', Ember.HistoryLocation); + })(); (function() { @@ -36981,11 +37877,14 @@ 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); + 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) { @@ -37015,11 +37914,11 @@ resolve: function(fullName) { var parsedName = this.parseName(fullName), typeSpecificResolveMethod = this[parsedName.resolveMethodName]; if (!parsedName.name || !parsedName.type) { - throw new TypeError("Invalid fullName: `" + fullName + "`, must of of the form `type:name` "); + throw new TypeError("Invalid fullName: `" + fullName + "`, must be of the form `type:name` "); } if (typeSpecificResolveMethod) { var resolved = typeSpecificResolveMethod.call(this, parsedName); if (resolved) { return resolved; } @@ -37451,29 +38350,26 @@ Ember.debug('-------------------------------'); } }, /** - @private - 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; }, /** - @private - 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: @@ -37484,10 +38380,11 @@ App.Router.map(function() { this.resource('posts'); }); ``` + @private @method defaultRouter @return {Ember.Router} the default router */ defaultRouter: function() { @@ -37501,12 +38398,10 @@ return container.lookupFactory('router:main'); }, /** - @private - 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 @@ -37515,10 +38410,11 @@ 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; @@ -37610,19 +38506,13 @@ Example: ```javascript App.inject(<full_name or type>, <property name>, <full_name>) - App.inject('controller:application', 'email', 'model:email') - App.inject('controller', 'source', 'source:main') + App.inject('model:user', 'email', 'model:email') + App.inject('model', 'source', 'source:main') ``` - Please note that injections on models are currently disabled. - This was done because ember-data was not ready for fully a container aware ecosystem. - - You can enable injections on models by setting `Ember.MODEL_FACTORY_INJECTIONS` flag to `true` - If model factory injections are enabled, models should not be - accessed globally (only through `container.lookupFactory('model:modelName'))`); @method inject @param factoryNameOrType {String} @param property {String} @param injectionName {String} @@ -37631,32 +38521,30 @@ var container = this.__container__; container.injection.apply(container, arguments); }, /** - @private - @deprecated - 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'); }, /** - @private - 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; } @@ -37806,16 +38694,15 @@ this.resolve(this); }, /** - @private - 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'), @@ -37824,15 +38711,14 @@ set(this, 'eventDispatcher', dispatcher); dispatcher.setup(customEvents, rootElement); }, /** - @private - 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'); @@ -37901,12 +38787,10 @@ this.initializers[initializer.name] = initializer; }, /** - @private - 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 @@ -37920,10 +38804,11 @@ * 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 @@ -37953,36 +38838,31 @@ container.register('event_dispatcher:main', Ember.EventDispatcher); container.register('router:main', Ember.Router); container.injection('router:main', 'namespace', 'application:main'); - 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'); return container; } }); /** - @private - 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) { @@ -38038,11 +38918,11 @@ */ var get = Ember.get, set = Ember.set; function verifyNeedsDependencies(controller, container, needs) { - var dependency, i, l; + var dependency, i, l, missing = []; for (i=0, l=needs.length; i<l; i++) { dependency = needs[i]; Ember.assert(Ember.inspect(controller) + "#needs must not specify dependencies with periods in their names (" + dependency + ")", dependency.indexOf('.') === -1); @@ -38051,13 +38931,16 @@ dependency = "controller:" + dependency; } // Structure assert to still do verification but not string concat in production if (!container.has(dependency)) { - Ember.assert(Ember.inspect(controller) + " needs " + dependency + " but it does not exist", false); + missing.push(dependency); } } + if (missing.length) { + throw new Ember.Error(Ember.inspect(controller) + " needs [ " + missing.join(', ') + " ] but " + (missing.length > 1 ? 'they' : 'it') + " could not be found"); + } } var defaultControllersComputedProperty = Ember.computed(function() { var controller = this; @@ -38142,11 +39025,14 @@ init: function() { var needs = get(this, 'needs'), length = get(needs, 'length'); if (length > 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); + 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); } @@ -38216,26 +39102,34 @@ @submodule ember-extension-support */ /** The `DataAdapter` helps a data persistence library interface with tools that debug Ember such - as the Chrome Ember Extension. + 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 overriden are - `getFilters`, `detect`, `columnsForType`, - `getRecords`, `getRecordColumnValues`, - `getRecordKeywords`, `getRecordFilterValues`, - `getRecordColor`, `observeRecord` + 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: "dataAdapter", initialize: function(container, application) { @@ -38256,50 +39150,54 @@ /** The container of the application being debugged. This property will be injected on creation. + + @property container + @default null */ container: null, /** - @private - Number of attributes to send as columns. (Enough to make the record identifiable). + + @private + @property attributeLimit + @default 3 */ attributeLimit: 3, /** - @private - Stores all methods that clear observers. These methods will be called on destruction. + + @private + @property releaseMethods */ releaseMethods: Ember.A(), /** - @public - 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(); }, /** - @public - 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`). @@ -38327,14 +39225,13 @@ this.releaseMethods.pushObject(release); return release; }, /** - @public - 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: @@ -38391,42 +39288,39 @@ this.releaseMethods.pushObject(release); return release; }, /** - @private - Clear all observers before destruction + @private */ willDestroy: function() { this._super(); this.releaseMethods.forEach(function(fn) { fn(); }); }, /** - @private - 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; }, /** - @private - 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) @@ -38434,14 +39328,13 @@ columnsForType: function(type) { return Ember.A(); }, /** - @private - 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 */ @@ -38468,14 +39361,13 @@ return release; }, /** - @private - Wraps a given model type and observes changes to it. + @private @method wrapModelType @param {Class} type A model class @param {Function} typesUpdated callback to call when the type changes @return {Object} contains the wrapped type and the function to remove observers Format: @@ -38502,18 +39394,18 @@ return typeToSend; }, /** - @private - Fetches all models defined in the application. - TODO: Use the resolver instead of looping over namespaces. + @private @method getModelTypes @return {Array} Array of model types */ + + // TODO: Use the resolver instead of looping over namespaces. getModelTypes: function() { var namespaces = Ember.A(Ember.Namespace.NAMESPACES), types = Ember.A(), self = this; namespaces.forEach(function(namespace) { for (var key in namespace) { @@ -38526,31 +39418,29 @@ }); return types; }, /** - @private - Fetches all loaded records for a given type. + @private @method getRecords - @return {Array} array of records. + @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(); }, /** - @private + Wraps a record and observers changes to it. - Wraps a record and observers changes to it - + @private @method wrapRecord - @param {Object} record The record instance - @return {Object} the wrapped record. Format: + @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; @@ -38562,67 +39452,62 @@ return recordToSend; }, /** - @private - 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 {}; }, /** - @private - Returns keywords to match when searching records. + @private @method getRecordKeywords @return {Array} Relevant keywords for search. */ getRecordKeywords: function(record) { return Ember.A(); }, /** - @private - 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 {}; }, /** - @private - 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; }, /** - @private - 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. */ @@ -38676,23 +39561,24 @@ The helper method will always be called with the current Application as the first parameter. For example: + ```javascript - Ember.Test.registerHelper('boot', function(app) { - Ember.run(app, app.advanceReadiness); - }); + Ember.Test.registerHelper('boot', function(app) { + Ember.run(app, app.advanceReadiness); + }); ``` This helper can later be called without arguments because it will be called with `app` as the first parameter. ```javascript - App = Ember.Application.create(); - App.injectTestHelpers(); - boot(); + App = Ember.Application.create(); + App.injectTestHelpers(); + boot(); ``` @public @method registerHelper @param {String} name The name of the helper method to add. @@ -38712,32 +39598,34 @@ The helper method will always be called with the current Application as the first parameter. For example: + ```javascript - Ember.Test.registerAsyncHelper('boot', function(app) { - Ember.run(app, app.advanceReadiness); - }); + Ember.Test.registerAsyncHelper('boot', function(app) { + Ember.run(app, app.advanceReadiness); + }); ``` The advantage of an async helper is that it will not run until the last async helper has completed. All async helpers after it will wait for it complete before running. For example: + ```javascript - Ember.Test.registerAsyncHelper('deletePost', function(app, postId) { - click('.delete-' + postId); - }); + Ember.Test.registerAsyncHelper('deletePost', function(app, postId) { + click('.delete-' + postId); + }); - // ... in your test - visit('/post/2'); - deletePost(2); - visit('/post/3'); - deletePost(3); + // ... in your test + visit('/post/2'); + deletePost(2); + visit('/post/3'); + deletePost(3); ``` @public @method registerAsyncHelper @param {String} name The name of the helper method to add. @@ -38752,10 +39640,11 @@ /** Remove a previously added helper method. Example: + ``` Ember.Test.unregisterHelper('wait'); ``` @public @@ -38814,10 +39703,11 @@ framework. You can manually set it before calling `App.setupForTesting()`. Example: + ``` Ember.Test.adapter = MyCustomAdapter.create() ``` If you do not set it, ember-testing will default to `Ember.Test.QUnitAdapter`. @@ -38843,12 +39733,10 @@ return resolve(val); }); }, /** - @public - This allows ember-testing to play nicely with other asynchronous events, such as an application that is waiting for a CSS3 transition or an IndexDB transaction. For example: @@ -38989,35 +39877,32 @@ location to 'none', so that the window's location will not be modified (preventing both accidental leaking of state between tests and interference with your testing framework). Example: + ``` App.setupForTesting(); ``` @method setupForTesting */ setupForTesting: function() { Ember.testing = true; - - this.testing = true; - + this.testing = true; + this.Router.reopen({ location: 'none' }); // if adapter is not manually set default to QUnit if (!Ember.Test.adapter) { Ember.Test.adapter = Ember.Test.QUnitAdapter.create(); } - if (Ember.FEATURES.isEnabled('ember-testing-simple-setup')){ - this.testingSetup = true; - } - }, + }, /** This will be used as the container to inject the test helpers into. By default the helpers are injected into `window`. @@ -39062,11 +39947,12 @@ /** This removes all helpers that have been registered, and resets and functions that were overridden by the helpers. Example: - ``` + + ```javascript App.removeTestHelpers(); ``` @public @method removeTestHelpers @@ -39124,11 +40010,11 @@ var value, lastPromise; // Reset lastPromise for nested helpers Ember.Test.lastPromise = null; - value = fn(val); + value = fn.call(null, val); lastPromise = Ember.Test.lastPromise; // If the method returned a promise // return that promise. If not, @@ -39149,36 +40035,22 @@ (function() { Ember.onLoad('Ember.Application', function(Application) { - - Application.initializer({ - name: 'deferReadiness in `testing` mode', + Application.initializer({ + name: 'deferReadiness in `testing` mode', - initialize: function(container, application){ - if (application.testing) { - application.deferReadiness(); - } + initialize: function(container, application){ + if (application.testing) { + application.deferReadiness(); } - }); - + } + }); - if (Ember.FEATURES.isEnabled('ember-testing-simple-setup')){ - Application.initializer({ - name: 'setupForTesting and injectTestHelpers when created with testing = true', + }); - initialize: function(container, application){ - if (application.testing && !application.testingSetup) { - application.setupForTesting(); - application.injectTestHelpers(); - } - } - }); - } -}); - })(); (function() { @@ -39338,11 +40210,14 @@ Ember.$(document).ajaxStart(function() { Test.pendingAjaxRequests++; }); Ember.$(document).ajaxStop(function() { - Ember.assert("An ajaxStop event which would cause the number of pending AJAX requests to be negative has been triggered. This is most likely caused by AJAX events that were started before calling `injectTestHelpers()`.", Test.pendingAjaxRequests !== 0); + Ember.assert("An ajaxStop event which would cause the number of pending AJAX " + + "requests to be negative has been triggered. This is most likely " + + "caused by AJAX events that were started before calling " + + "`injectTestHelpers()`.", Test.pendingAjaxRequests !== 0); Test.pendingAjaxRequests--; }); }); function currentRouteName(app){ @@ -39362,13 +40237,11 @@ return get(router, 'location').getURL(); } function visit(app, url) { - - Ember.run(app, 'advanceReadiness'); - + Ember.run(app, 'advanceReadiness'); app.__container__.lookup('router:main').location.setURL(url); Ember.run(app, app.handleURL, url); return wait(app); } @@ -39474,17 +40347,15 @@ // 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; } - + 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) { @@ -39504,11 +40375,11 @@ * 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 * }); * ``` * @@ -39522,11 +40393,11 @@ * Clicks an element and triggers any actions triggered by the element's `click` * event. * * Example: * -* ``` +* ```javascript * click('.some-jQuery-selector').then(function() { * // assert something * }); * ``` * @@ -39539,11 +40410,11 @@ /** * 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 * }); * ``` * @@ -39558,11 +40429,11 @@ /** * Fills in an input element with some text. * * Example: * -* ``` +* ```javascript * fillIn('#email', 'you@example.com').then(function() { * // assert something * }); * ``` * @@ -39578,27 +40449,26 @@ * 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. * -* 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 @@ -39615,11 +40485,11 @@ 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') @@ -39628,91 +40498,15 @@ }); @method wait @param {Object} value The value to be returned. @return {RSVP.Promise} - ``` */ asyncHelper('wait', wait); asyncHelper('andThen', andThen); -if (Ember.FEATURES.isEnabled('ember-testing-routing-helpers')){ - /** - Returns the currently active route name. - Example: - - ``` - 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: - - ``` - 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: - - ``` - 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); -} - -if (Ember.FEATURES.isEnabled('ember-testing-triggerEvent-helper')) { - /** - Triggers the given event on the element identified by the provided selector. - - Example: - - ```javascript - triggerEvent('#some-elem-id', 'blur'); - ``` - - @method triggerEvent - @param {String} selector jQuery selector for finding element on the DOM - @param {String} event The event to be triggered. - @return {RSVP.Promise} - */ - asyncHelper('triggerEvent', triggerEvent); -} })();