dist/ember-runtime.js in ember-source-1.2.2 vs dist/ember-runtime.js in ember-source-1.3.0.beta.1

- old
+ new

@@ -6,11 +6,11 @@ // License: Licensed under MIT license // See https://raw.github.com/emberjs/ember.js/master/LICENSE // ========================================================================== - // Version: 1.2.2 + // Version: 1.3.0-beta.1 (function() { /*global __fail__*/ /** @@ -192,11 +192,11 @@ // License: Licensed under MIT license // See https://raw.github.com/emberjs/ember.js/master/LICENSE // ========================================================================== - // Version: 1.2.2 + // Version: 1.3.0-beta.1 (function() { var define, requireModule; (function() { @@ -257,11 +257,11 @@ The core Runtime framework is based on the jQuery API with a number of performance optimizations. @class Ember @static - @version 1.2.2 + @version 1.3.0-beta.1 */ 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. @@ -284,14 +284,14 @@ /** @property VERSION @type String - @default '1.2.2' + @default '1.3.0-beta.1' @final */ -Ember.VERSION = '1.2.2'; +Ember.VERSION = '1.3.0-beta.1'; /** Standard environmental variables. You can define these in a global `ENV` variable before loading Ember to control various configuration settings. @@ -3973,15 +3973,11 @@ })(); (function() { -/** - @module ember-metal -*/ - })(); (function() { @@ -4045,11 +4041,10 @@ unwatchPath = Ember.unwatchPath, typeOf = Ember.typeOf, // utils.js generateGuid = Ember.generateGuid, IS_PATH = /[\.\*]/; - // returns true if the passed path is just a keyName function isKeyName(path) { return path==='*' || !IS_PATH.test(path); } @@ -4069,17 +4064,15 @@ */ Ember.watch = function(obj, _keyPath) { // can't watch length on Array - it is special... if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } - - if (isKeyName(_keyPath)) { - watchKey(obj, _keyPath); - } else { - watchPath(obj, _keyPath); - } - + if (isKeyName(_keyPath)) { + watchKey(obj, _keyPath); + } else { + watchPath(obj, _keyPath); + } }; Ember.isWatching = function isWatching(obj, key) { var meta = obj[META_KEY]; return (meta && meta.watching[key]) > 0; @@ -4089,17 +4082,15 @@ Ember.unwatch = function(obj, _keyPath) { // can't watch length on Array - it is special... if (_keyPath === 'length' && typeOf(obj) === 'array') { return; } - - if (isKeyName(_keyPath)) { - unwatchKey(obj, _keyPath); - } else { - unwatchPath(obj, _keyPath); - } - + if (isKeyName(_keyPath)) { + unwatchKey(obj, _keyPath); + } else { + unwatchPath(obj, _keyPath); + } }; /** @private @@ -4441,19 +4432,16 @@ @param {String} path* zero or more property paths @return {Ember.ComputedProperty} this @chainable */ ComputedPropertyPrototype.property = function() { - var addArg; + var args; - var args = []; - for (var i = 0, l = arguments.length; i < l; i++) { - - args.push(arguments[i]); - - } + args = a_slice.call(arguments); + + this._dependentKeys = args; return this; }; /** @@ -5302,11 +5290,10 @@ */ var AFTER_OBSERVERS = ':change', BEFORE_OBSERVERS = ':before'; - function changeEvent(keyName) { return keyName+AFTER_OBSERVERS; } function beforeEvent(keyName) { @@ -5319,14 +5306,12 @@ @param {String} path @param {Object|Function} targetOrMethod @param {Function|String} [method] */ Ember.addObserver = function(obj, _path, target, method) { - - Ember.addListener(obj, changeEvent(_path), target, method); - Ember.watch(obj, _path); - + Ember.addListener(obj, changeEvent(_path), target, method); + Ember.watch(obj, _path); return this; }; Ember.observersFor = function(obj, path) { @@ -5339,14 +5324,13 @@ @param {String} path @param {Object|Function} targetOrMethod @param {Function|String} [method] */ Ember.removeObserver = function(obj, _path, target, method) { - - Ember.unwatch(obj, _path); - Ember.removeListener(obj, changeEvent(_path), target, method); - + Ember.unwatch(obj, _path); + Ember.removeListener(obj, changeEvent(_path), target, method); + return this; }; /** @method addBeforeObserver @@ -5354,14 +5338,13 @@ @param {String} path @param {Object|Function} targetOrMethod @param {Function|String} [method] */ Ember.addBeforeObserver = function(obj, _path, target, method) { - - Ember.addListener(obj, beforeEvent(_path), target, method); - Ember.watch(obj, _path); - + Ember.addListener(obj, beforeEvent(_path), target, method); + Ember.watch(obj, _path); + return this; }; // Suspend observer during callback. // @@ -5397,14 +5380,13 @@ @param {String} path @param {Object|Function} targetOrMethod @param {Function|String} [method] */ Ember.removeBeforeObserver = function(obj, _path, target, method) { - - Ember.unwatch(obj, _path); - Ember.removeListener(obj, beforeEvent(_path), target, method); - + Ember.unwatch(obj, _path); + Ember.removeListener(obj, beforeEvent(_path), target, method); + return this; }; })(); @@ -6084,11 +6066,10 @@ return ret; }; /** - If no run-loop is present, it creates a new one. If a run loop is present it will queue itself to run on the existing run-loops action queue. Please note: This is not for normal usage, and should be used sparingly. @@ -7030,10 +7011,11 @@ a_slice = [].slice, o_create = Ember.create, defineProperty = Ember.defineProperty, guidFor = Ember.guidFor; + function mixinsMeta(obj) { var m = Ember.meta(obj, true), ret = m.mixins; if (!ret) { ret = m.mixins = {}; } else if (!m.hasOwnProperty('mixins')) { @@ -7683,19 +7665,23 @@ @param {Function} func @return func */ Ember.observer = function() { var func = a_slice.call(arguments, -1)[0]; - var paths = a_slice.call(arguments, 0, -1); + var paths; - if (typeof func !== "function") { - // revert to old, soft-deprecated argument ordering + + paths = a_slice.call(arguments, 0, -1); - func = arguments[0]; - paths = a_slice.call(arguments, 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"); } func.__ember_observes__ = paths; @@ -7777,19 +7763,23 @@ @param {Function} func @return func */ Ember.beforeObserver = function() { var func = a_slice.call(arguments, -1)[0]; - var paths = a_slice.call(arguments, 0, -1); + var paths; - if (typeof func !== "function") { - // revert to old, soft-deprecated argument ordering + + paths = a_slice.call(arguments, 0, -1); - func = arguments[0]; - paths = a_slice.call(arguments, 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"); } func.__ember_observesBefore__ = paths; @@ -7908,97 +7898,149 @@ __exports__.all = all; }); define("rsvp/async", - ["exports"], - function(__exports__) { + ["rsvp/config","exports"], + function(__dependency1__, __exports__) { "use strict"; + var config = __dependency1__.config; + var browserGlobal = (typeof window !== 'undefined') ? window : {}; var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver; - var async; var local = (typeof global !== 'undefined') ? global : this; - // old node + // node function useNextTick() { - return function(callback, arg) { - process.nextTick(function() { - callback(arg); - }); + return function() { + process.nextTick(flush); }; } - // node >= 0.10.x - function useSetImmediate() { - return function(callback, arg) { - /* global setImmediate */ - setImmediate(function(){ - callback(arg); - }); - }; - } - function useMutationObserver() { - var queue = []; - - var observer = new BrowserMutationObserver(function() { - var toProcess = queue.slice(); - queue = []; - - toProcess.forEach(function(tuple) { - var callback = tuple[0], arg= tuple[1]; - callback(arg); - }); - }); - + var observer = new BrowserMutationObserver(flush); var element = document.createElement('div'); observer.observe(element, { attributes: true }); // Chrome Memory Leak: https://bugs.webkit.org/show_bug.cgi?id=93661 window.addEventListener('unload', function(){ observer.disconnect(); observer = null; }, false); - return function(callback, arg) { - queue.push([callback, arg]); + return function() { element.setAttribute('drainQueue', 'drainQueue'); }; } function useSetTimeout() { - return function(callback, arg) { - local.setTimeout(function() { - callback(arg); - }, 1); + return function() { + local.setTimeout(flush, 1); }; } - if (typeof setImmediate === 'function') { - async = useSetImmediate(); - } else if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { - async = useNextTick(); + var queue = []; + function flush() { + for (var i = 0; i < queue.length; i++) { + var tuple = queue[i]; + var callback = tuple[0], arg = tuple[1]; + callback(arg); + } + queue = []; + } + + var scheduleFlush; + + // Decide what async method to use to triggering processing of queued callbacks: + if (typeof process !== 'undefined' && {}.toString.call(process) === '[object process]') { + scheduleFlush = useNextTick(); } else if (BrowserMutationObserver) { - async = useMutationObserver(); + scheduleFlush = useMutationObserver(); } else { - async = useSetTimeout(); + scheduleFlush = useSetTimeout(); } + function asyncDefault(callback, arg) { + var length = queue.push([callback, arg]); + if (length === 1) { + // If length is 1, that means that we need to schedule an async flush. + // If additional callbacks are queued before the queue is flushed, they + // will be processed by this flush that we are scheduling. + scheduleFlush(); + } + } + config.async = asyncDefault; + + function async(callback, arg) { + config.async(callback, arg); + } + + __exports__.async = async; + __exports__.asyncDefault = asyncDefault; }); +define("rsvp/cast", + ["exports"], + function(__exports__) { + "use strict"; + 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); + }); + } + + + __exports__.cast = cast; + }); define("rsvp/config", - ["rsvp/async","exports"], + ["rsvp/events","exports"], function(__dependency1__, __exports__) { "use strict"; - var async = __dependency1__.async; + var EventTarget = __dependency1__.EventTarget; var config = {}; - config.async = async; + EventTarget.mixin(config); + function configure(name, value) { + if (name === 'onerror') { + // handle for legacy users that expect the actual + // error to be passed to their function added via + // `RSVP.configure('onerror', someFunctionHere);` + config.on('error', function(event){ + value(event.detail); + }); + } else { + config[name] = value; + } + } + function on(){ + return config.on.apply(config, arguments); + } + + function off(){ + return config.off.apply(config, arguments); + } + + function trigger(){ + return config.trigger.apply(config, arguments); + } + + __exports__.config = config; + __exports__.configure = configure; + __exports__.on = on; + __exports__.off = off; + __exports__.trigger = trigger; }); define("rsvp/defer", ["rsvp/promise","exports"], function(__dependency1__, __exports__) { "use strict"; @@ -8223,25 +8265,26 @@ __exports__.denodeify = denodeify; }); define("rsvp/promise", - ["rsvp/config","rsvp/events","exports"], - function(__dependency1__, __dependency2__, __exports__) { + ["rsvp/config","rsvp/events","rsvp/cast","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __exports__) { "use strict"; var config = __dependency1__.config; var EventTarget = __dependency2__.EventTarget; + var cast = __dependency3__.cast; function objectOrFunction(x) { return isFunction(x) || (typeof x === "object" && x !== null); } function isFunction(x){ return typeof x === "function"; } - var Promise = function(resolver) { + function Promise(resolver) { var promise = this, resolved = false; if (typeof resolver !== 'function') { throw new TypeError('You must pass a resolver function as the sole argument to the promise constructor'); @@ -8261,31 +8304,15 @@ if (resolved) { return; } resolved = true; reject(promise, value); }; - this.on('promise:resolved', function(event) { - this.trigger('success', { detail: event.detail }); - }, this); - - this.on('promise:failed', function(event) { - this.trigger('error', { detail: event.detail }); - }, this); - - this.on('error', onerror); - try { resolver(resolvePromise, rejectPromise); } catch(e) { rejectPromise(e); } - }; - - function onerror(event) { - if (config.onerror) { - config.onerror(event.detail); - } } var invokeCallback = function(type, promise, callback, event) { var hasCallback = isFunction(callback), value, error, succeeded, failed; @@ -8316,18 +8343,23 @@ } }; Promise.prototype = { constructor: Promise, - isRejected: undefined, isFulfilled: undefined, rejectedReason: undefined, fulfillmentValue: undefined, + _onerror: function (reason) { + config.trigger('error', { + detail: reason + }); + }, + then: function(done, fail) { - this.off('error', onerror); + this._onerror = null; var thenPromise = new this.constructor(function() {}); if (this.isFulfilled) { config.async(function(promise) { @@ -8350,15 +8382,31 @@ }); return thenPromise; }, - fail: function(fail) { - return this.then(null, fail); + fail: function(onRejection) { + return this.then(null, onRejection); + }, + 'finally': function(callback) { + var constructor = this.constructor; + + return this.then(function(value) { + return constructor.cast(callback()).then(function(){ + return value; + }); + }, function(reason) { + return constructor.cast(callback()).then(function(){ + throw reason; + }); + }); } }; + Promise.prototype['catch'] = Promise.prototype.fail; + Promise.cast = cast; + EventTarget.mixin(Promise.prototype); function resolve(promise, value) { if (promise === value) { fulfill(promise, value); @@ -8415,10 +8463,11 @@ }); } function reject(promise, value) { config.async(function() { + if (promise._onerror) { promise._onerror(value); } promise.trigger('promise:failed', { detail: value }); promise.isRejected = true; promise.rejectedReason = value; }); } @@ -8471,40 +8520,48 @@ __exports__.rethrow = rethrow; }); define("rsvp", - ["rsvp/events","rsvp/promise","rsvp/node","rsvp/all","rsvp/hash","rsvp/rethrow","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","exports"], - function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __exports__) { + ["rsvp/events","rsvp/promise","rsvp/node","rsvp/all","rsvp/hash","rsvp/rethrow","rsvp/defer","rsvp/config","rsvp/resolve","rsvp/reject","rsvp/async","exports"], + function(__dependency1__, __dependency2__, __dependency3__, __dependency4__, __dependency5__, __dependency6__, __dependency7__, __dependency8__, __dependency9__, __dependency10__, __dependency11__, __exports__) { "use strict"; var EventTarget = __dependency1__.EventTarget; var Promise = __dependency2__.Promise; var denodeify = __dependency3__.denodeify; var all = __dependency4__.all; var hash = __dependency5__.hash; var rethrow = __dependency6__.rethrow; var defer = __dependency7__.defer; var config = __dependency8__.config; + var configure = __dependency8__.configure; + var on = __dependency8__.on; + var off = __dependency8__.off; + var trigger = __dependency8__.trigger; var resolve = __dependency9__.resolve; var reject = __dependency10__.reject; + var async = __dependency11__.async; + var asyncDefault = __dependency11__.asyncDefault; - function configure(name, value) { - config[name] = value; - } - __exports__.Promise = Promise; __exports__.EventTarget = EventTarget; __exports__.all = all; __exports__.hash = hash; __exports__.rethrow = rethrow; __exports__.defer = defer; __exports__.denodeify = denodeify; __exports__.configure = configure; + __exports__.trigger = trigger; + __exports__.on = on; + __exports__.off = off; __exports__.resolve = resolve; __exports__.reject = reject; + __exports__.async = async; + __exports__.asyncDefault = asyncDefault; }); + })(); (function() { /** @private @@ -9117,11 +9174,10 @@ one would do the following: ```javascript var container = new Container(); - container.registerFactory('model:user', User); container.register('store:main', SomeStore); container.factoryTypeInjection('model', 'store', 'store:main'); var store = container.lookup('store:main'); @@ -9205,11 +9261,10 @@ their managed objects. @method destroy */ destroy: function() { - this.isDestroyed = true; for (var i=0, l=this.children.length; i<l; i++) { this.children[i].destroy(); } @@ -10645,16 +10700,18 @@ for (var i = 0, l = props.length; i < l; i++) { var properties = props[i]; Ember.assert("Ember.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Ember.Mixin)); - if (properties === null || typeof properties !== 'object') { - Ember.assert("Ember.Object.create only accepts objects."); - continue; + if (typeof properties !== 'object' && properties !== undefined) { + throw new Ember.Error("Ember.Object.create only accepts objects."); } + if (!properties) { continue; } + var keyNames = Ember.keys(properties); + for (var j = 0, ll = keyNames.length; j < ll; j++) { var keyName = keyNames[j]; if (!properties.hasOwnProperty(keyName)) { continue; } var value = properties[keyName], @@ -11761,11 +11818,11 @@ 1. You must have a length property. This property should change whenever the number of items in your enumerable object changes. If you using this with an `Ember.Object` subclass, you should be sure to change the length property using `set().` - 2. If you must implement `nextObject().` See documentation. + 2. You must implement `nextObject().` See documentation. Once you have these two methods implement, apply the `Ember.Enumerable` mixin to your class and you will be able to enumerate the contents of your object like any other collection. @@ -12261,33 +12318,39 @@ return !callback.call(target, x, idx, i); }); }, /** - Returns `true` if the passed property resolves to `true` for all items in - the enumerable. This method is often simpler/faster than using a callback. - @method everyBy @param {String} key the property to test @param {String} [value] optional value to test against. + @deprecated Use `isEvery` instead @return {Boolean} */ - everyBy: function(key, value) { - return this.every(iter.apply(this, arguments)); - }, + everyBy: Ember.aliasMethod('isEvery'), /** + @method everyProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @deprecated Use `isEvery` instead + @return {Boolean} + */ + everyProperty: Ember.aliasMethod('isEvery'), + + /** Returns `true` if the passed property resolves to `true` for all items in the enumerable. This method is often simpler/faster than using a callback. - @method everyProperty + @method isEvery @param {String} key the property to test @param {String} [value] optional value to test against. @return {Boolean} - @deprecated Use `everyBy` instead */ - everyProperty: Ember.aliasMethod('everyBy'), + isEvery: function(key, value) { + return this.every(iter.apply(this, arguments)); + }, /** Returns `true` if the passed function returns true for any item in the enumeration. This corresponds with the `some()` method in JavaScript 1.6. @@ -12371,25 +12434,31 @@ @method anyBy @param {String} key the property to test @param {String} [value] optional value to test against. @return {Boolean} `true` if the passed function returns `true` for any item */ - anyBy: function(key, value) { + isAny: function(key, value) { return this.any(iter.apply(this, arguments)); }, /** - Returns `true` if the passed property resolves to `true` for any item in - the enumerable. This method is often simpler/faster than using a callback. + @method someProperty + @param {String} key the property to test + @param {String} [value] optional value to test against. + @return {Boolean} `true` if the passed function returns `true` for any item + @deprecated Use `isAny` instead + */ + anyBy: Ember.aliasMethod('isAny'), + /** @method someProperty @param {String} key the property to test @param {String} [value] optional value to test against. @return {Boolean} `true` if the passed function returns `true` for any item - @deprecated Use `anyBy` instead + @deprecated Use `isAny` instead */ - someProperty: Ember.aliasMethod('anyBy'), + someProperty: Ember.aliasMethod('isAny'), /** This will combine the values of the enumerator into a single value. It is a useful way to collect a summary value from an enumeration. This corresponds to the `reduce()` method defined in JavaScript 1.8. @@ -12673,43 +12742,38 @@ Ember.sendEvent(this, '@enumerable:change', [this, removing, adding]); if (hasDelta) Ember.propertyDidChange(this, 'length'); Ember.propertyDidChange(this, '[]'); return this ; - } + }, -}); + /** + Converts the enumerable into an array and sorts by the keys + specified in the argument. + You may provide multiple arguments to sort by multiple properties. - Ember.Enumerable.reopen({ - /** - Converts the enumerable into an array and sorts by the keys - specified in the argument. - - You may provide multiple arguments to sort by multiple properties. - - @method sortBy - @param {String} property name(s) to sort on - @return {Array} The sorted array. + @method sortBy + @param {String} property name(s) to sort on + @return {Array} The sorted array. */ - sortBy: function() { - var sortKeys = arguments; - return this.toArray().sort(function(a, b){ - for(var i = 0; i < sortKeys.length; i++) { - var key = sortKeys[i], - propA = get(a, key), - propB = get(b, key); - // return 1 or -1 else continue to the next sortKey - var compareValue = Ember.compare(propA, propB); - if (compareValue) { return compareValue; } - } - return 0; - }); - } - }); + sortBy: function() { + var sortKeys = arguments; + return this.toArray().sort(function(a, b){ + for(var i = 0; i < sortKeys.length; i++) { + var key = sortKeys[i], + propA = get(a, key), + propB = get(b, key); + // return 1 or -1 else continue to the next sortKey + var compareValue = Ember.compare(propA, propB); + if (compareValue) { return compareValue; } + } + return 0; + }); + } +}); - })(); (function() { @@ -13157,18 +13221,18 @@ o_create = Ember.create, forEach = Ember.EnumerableUtils.forEach, // Here we explicitly don't allow `@each.foo`; it would require some special // testing, but there's no particular reason why it should be disallowed. eachPropertyPattern = /^(.*)\.@each\.(.*)/, - doubleEachPropertyPattern = /(.*\.@each){2,}/; + doubleEachPropertyPattern = /(.*\.@each){2,}/, + arrayBracketPattern = /\.\[\]$/; + function get(obj, key) { - - if (key === '@this') { - return obj; - } - + if (key === '@this') { + return obj; + } return e_get(obj, key); } /* @@ -13528,10 +13592,19 @@ if (cp.options.initialize) { cp.options.initialize.call(this, meta.getValue(), { property: cp, propertyName: propertyName }, meta.sugarMeta); } } +function partiallyRecomputeFor(obj, dependentKey) { + if (arrayBracketPattern.test(dependentKey)) { + return false; + } + + var value = get(obj, dependentKey); + return Ember.Array.detect(value); +} + function ReduceComputedPropertyInstanceMeta(context, propertyName, initialValue) { this.context = context; this.propertyName = propertyName; this.cache = metaFor(context).cache; @@ -13608,10 +13681,14 @@ reset.call(this, cp, propertyName); meta.dependentArraysObserver.suspendArrayObservers(function () { forEach(cp._dependentKeys, function (dependentKey) { + + if (!partiallyRecomputeFor(this, dependentKey)) { return; } + + var dependentArray = get(this, dependentKey), previousDependentArray = meta.dependentArrays[dependentKey]; if (dependentArray === previousDependentArray) { // The array may be the same, but our item property keys may have @@ -13635,10 +13712,14 @@ } }, this); }, this); forEach(cp._dependentKeys, function(dependentKey) { + + if (!partiallyRecomputeFor(this, dependentKey)) { return; } + + var dependentArray = get(this, dependentKey); if (dependentArray) { addItems.call(this, dependentArray, callbacks, cp, propertyName, meta); } }, this); @@ -13727,19 +13808,23 @@ forEach(a_slice.call(arguments), function (dependentKey) { if (doubleEachPropertyPattern.test(dependentKey)) { throw new Ember.Error("Nested @each properties not supported: " + dependentKey); } else if (match = eachPropertyPattern.exec(dependentKey)) { dependentArrayKey = match[1]; - itemPropertyKey = match[2]; - cp.itemPropertyKey(dependentArrayKey, itemPropertyKey); + + + itemPropertyKey = match[2]; + cp.itemPropertyKey(dependentArrayKey, itemPropertyKey); + propertyArgs.add(dependentArrayKey); } else { propertyArgs.add(dependentKey); } }); return ComputedProperty.prototype.property.apply(this, propertyArgs.toArray()); + }; /** Creates a computed property which operates on dependent arrays and is updated with "one at a time" semantics. When items are added or @@ -13885,10 +13970,38 @@ return reverse(get(this, 'name')); }.property('name') }) ``` + Dependent keys whose values are not arrays are treated as regular + dependencies: when they change, the computed property is completely + recalculated. It is sometimes useful to have dependent arrays with similar + semantics. Dependent keys which end in `.[]` do not use "one at a time" + semantics. When an item is added or removed from such a dependency, the + computed property is completely recomputed. + + Example + + ```javascript + Ember.Object.extend({ + // When `string` is changed, `computed` is completely recomputed. + string: 'a string', + + // When an item is added to `array`, `addedItem` is called. + array: [], + + // When an item is added to `anotherArray`, `computed` is completely + // recomputed. + anotherArray: [], + + computed: Ember.reduceComputed('string', 'array', 'anotherArray.[]', { + addedItem: addedItemCallback, + removedItem: removedItemCallback + }) + }); + ``` + @method reduceComputed @for Ember @param {String} [dependentKeys*] @param {Object} options @return {Ember.ComputedProperty} @@ -14819,10 +14932,26 @@ @namespace Ember @constructor */ Ember.RSVP = requireModule('rsvp'); +Ember.RSVP.onerrorDefault = function(event) { + var error = event.detail; + + if (error instanceof Error) { + Ember.Logger.error(error.stack); + + if (Ember.testing) { + throw error; + } else { + Ember.assert(error, false); + } + } +}; + +Ember.RSVP.on('error', Ember.RSVP.onerrorDefault); + })(); (function() { @@ -14831,10 +14960,11 @@ @submodule ember-runtime */ var a_slice = Array.prototype.slice; + if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) { /** The `property` extension of Javascript's Function prototype is available when `Ember.EXTEND_PROTOTYPES` or `Ember.EXTEND_PROTOTYPES.Function` is @@ -14895,10 +15025,12 @@ @method property @for Function */ Function.prototype.property = function() { var ret = Ember.computed(this); + // ComputedProperty.prototype.property expands properties; no need for us to + // do so here. return ret.property.apply(ret, arguments); }; /** The `observes` extension of Javascript's Function prototype is available @@ -14924,11 +15056,14 @@ @method observes @for Function */ Function.prototype.observes = function() { - this.__ember_observes__ = a_slice.call(arguments); + + this.__ember_observes__ = a_slice.call(arguments); + + return this; }; /** The `observesImmediately` extension of Javascript's Function prototype is @@ -14959,10 +15094,11 @@ for (var i=0, l=arguments.length; i<l; i++) { var arg = arguments[i]; Ember.assert("Immediate observers must observe internal properties only, not properties on other objects.", arg.indexOf('.') === -1); } + // observes handles property expansion return this.observes.apply(this, arguments); }; /** The `observesBefore` extension of Javascript's Function prototype is @@ -14985,10 +15121,13 @@ @method observesBefore @for Function */ Function.prototype.observesBefore = function() { - this.__ember_observesBefore__ = a_slice.call(arguments); + + this.__ember_observesBefore__ = a_slice.call(arguments); + + return this; }; /** The `on` extension of Javascript's Function prototype is available