diff --git a/packages/ember-extension-support/lib/data_adapter.js b/packages/ember-extension-support/lib/data_adapter.js index 7d41666cc03..d71fd4b38a5 100644 --- a/packages/ember-extension-support/lib/data_adapter.js +++ b/packages/ember-extension-support/lib/data_adapter.js @@ -5,7 +5,10 @@ import Namespace from 'ember-runtime/system/namespace'; import EmberObject from 'ember-runtime/system/object'; import { A as emberA } from 'ember-runtime/system/native_array'; import Application from 'ember-application/system/application'; - +import { + addArrayObserver, + objectAt +} from 'ember-runtime/mixins/array'; /** @module ember @submodule ember-extension-support @@ -40,7 +43,7 @@ import Application from 'ember-application/system/application'; ```javascript Application.initializer({ - name: "data-adapter", + name: 'data-adapter', initialize: function(container, application) { application.register('data-adapter:main', DS.DataAdapter); @@ -162,7 +165,7 @@ export default EmberObject.extend({ typesAdded(typesToSend); var release = () => { - releaseMethods.forEach((fn) => fn() ); + releaseMethods.forEach(fn => fn()); this.releaseMethods.removeObject(release); }; this.releaseMethods.pushObject(release); @@ -218,7 +221,7 @@ export default EmberObject.extend({ var contentDidChange = (array, idx, removedCount, addedCount) => { for (var i = idx; i < idx + addedCount; i++) { - var record = array.objectAt(i); + var record = objectAt(array, i); var wrapped = this.wrapRecord(record); releaseMethods.push(this.observeRecord(record, recordUpdated)); recordsAdded([wrapped]); @@ -230,10 +233,10 @@ export default EmberObject.extend({ }; var observer = { didChange: contentDidChange, willChange() { return this; } }; - records.addArrayObserver(this, observer); + addArrayObserver(records, this, observer); release = () => { - releaseMethods.forEach(function(fn) { fn(); }); + releaseMethods.forEach(fn => fn()); records.removeArrayObserver(this, observer); this.releaseMethods.removeObject(release); }; @@ -251,9 +254,7 @@ export default EmberObject.extend({ */ willDestroy() { this._super(...arguments); - this.releaseMethods.forEach(function(fn) { - fn(); - }); + this.releaseMethods.forEach(fn => fn()); }, /** @@ -309,7 +310,7 @@ export default EmberObject.extend({ willChange() { return this; } }; - records.addArrayObserver(this, observer); + addArrayObserver(records, this, observer); var release = () => { records.removeArrayObserver(this, observer); diff --git a/packages/ember-htmlbars/tests/helpers/collection_test.js b/packages/ember-htmlbars/tests/helpers/collection_test.js index ccfae95dc2f..5f3e5e1cab4 100644 --- a/packages/ember-htmlbars/tests/helpers/collection_test.js +++ b/packages/ember-htmlbars/tests/helpers/collection_test.js @@ -14,10 +14,10 @@ import CollectionView from 'ember-views/views/collection_view'; import EmberView from 'ember-views/views/view'; import jQuery from 'ember-views/system/jquery'; import compile from 'ember-template-compiler/system/compile'; +import { objectAt } from 'ember-runtime/mixins/array'; var trim = jQuery.trim; - var view; var originalLookup = Ember.lookup; @@ -25,13 +25,13 @@ var TemplateTests, registry, container, lookup; function nthChild(view, nth) { - return get(view, 'childViews').objectAt(nth || 0); + return objectAt(get(view, 'childViews'), nth || 0); } var firstChild = nthChild; function firstGrandchild(view) { - return get(get(view, 'childViews').objectAt(0), 'childViews').objectAt(0); + return objectAt(get(objectAt(get(view, 'childViews'), 0), 'childViews'), 0); } QUnit.module('collection helper', { diff --git a/packages/ember-htmlbars/tests/helpers/if_unless_test.js b/packages/ember-htmlbars/tests/helpers/if_unless_test.js index 3bd0e840d92..621c6c77340 100644 --- a/packages/ember-htmlbars/tests/helpers/if_unless_test.js +++ b/packages/ember-htmlbars/tests/helpers/if_unless_test.js @@ -13,6 +13,7 @@ import { set } from 'ember-metal/property_set'; import { fmt } from 'ember-runtime/system/string'; import { typeOf } from 'ember-runtime/utils'; import { runAppend, runDestroy } from 'ember-runtime/tests/utils'; +import { replace } from 'ember-runtime/system/native_array'; var originalLookup = Ember.lookup; @@ -859,7 +860,7 @@ if (isEnabled('ember-htmlbars-inline-if-helper')) { equal(view.$().text(), 'truthy'); run(function() { - list.replace(0, 1); + replace(list, 0, 1); }); equal(view.$().text(), 'falsy'); diff --git a/packages/ember-htmlbars/tests/helpers/view_test.js b/packages/ember-htmlbars/tests/helpers/view_test.js index add0f99297b..d1733e47edb 100644 --- a/packages/ember-htmlbars/tests/helpers/view_test.js +++ b/packages/ember-htmlbars/tests/helpers/view_test.js @@ -23,16 +23,18 @@ import { set } from 'ember-metal/property_set'; import { get } from 'ember-metal/property_get'; import { computed } from 'ember-metal/computed'; +import { objectAt } from 'ember-runtime/mixins/array'; + var view, originalLookup, registry, container, lookup; var trim = jQuery.trim; function firstGrandchild(view) { - return get(get(view, 'childViews').objectAt(0), 'childViews').objectAt(0); + return objectAt(get(objectAt(get(view, 'childViews'), 0), 'childViews'), 0); } function nthChild(view, nth) { - return get(view, 'childViews').objectAt(nth || 0); + return objectAt(get(view, 'childViews'), nth || 0); } function viewClass(options) { diff --git a/packages/ember-metal/lib/replace.js b/packages/ember-metal/lib/replace.js index b3aec54e5d8..3d96144d3d0 100644 --- a/packages/ember-metal/lib/replace.js +++ b/packages/ember-metal/lib/replace.js @@ -29,13 +29,13 @@ export function _replace(array, idx, amt, objects) { ```javascript var array = [1,2,3]; - Ember.EnumerableUtils.replace(array, 1, 2, [4, 5]); // [1, 4, 5] + replace(array, 1, 2, [4, 5]); // [1, 4, 5] var array = [1,2,3]; - Ember.EnumerableUtils.replace(array, 1, 1, [4, 5]); // [1, 4, 5, 3] + replace(array, 1, 1, [4, 5]); // [1, 4, 5, 3] var array = [1,2,3]; - Ember.EnumerableUtils.replace(array, 10, 1, [4, 5]); // [1, 2, 3, 4, 5] + replace(array, 10, 1, [4, 5]); // [1, 2, 3, 4, 5] ``` @method replace @@ -52,6 +52,7 @@ export function _replace(array, idx, amt, objects) { @public */ export default function replace(array, idx, amt, objects) { + // TODO: FIXME if (array.replace) { return array.replace(idx, amt, objects); } else { diff --git a/packages/ember-runtime/lib/computed/reduce_computed.js b/packages/ember-runtime/lib/computed/reduce_computed.js index a84adb1530e..5736195a1a6 100644 --- a/packages/ember-runtime/lib/computed/reduce_computed.js +++ b/packages/ember-runtime/lib/computed/reduce_computed.js @@ -21,7 +21,10 @@ import { cacheFor } from 'ember-metal/computed'; import TrackedArray from 'ember-runtime/system/tracked_array'; -import EmberArray from 'ember-runtime/mixins/array'; +import EmberArray, { + addArrayObserver, + objectAt +} from 'ember-runtime/mixins/array'; import run from 'ember-metal/run_loop'; var cacheSet = cacheFor.set; @@ -88,7 +91,7 @@ function ItemPropertyObserverContext(dependentArray, index, trackedArray) { this.dependentArray = dependentArray; this.index = index; - this.item = dependentArray.objectAt(index); + this.item = objectAt(dependentArray, index); this.trackedArray = trackedArray; this.beforeObserver = null; this.observer = null; @@ -107,7 +110,7 @@ DependentArraysObserver.prototype = { setupObservers(dependentArray, dependentKey) { this.dependentKeysByGuid[guidFor(dependentArray)] = dependentKey; - dependentArray.addArrayObserver(this, { + addArrayObserver(dependentArray, this, { willChange: 'dependentArrayWillChange', didChange: 'dependentArrayDidChange' }); @@ -266,7 +269,7 @@ DependentArraysObserver.prototype = { itemIndex = normalizedIndex + sliceIndex; if (itemIndex >= length) { break; } - item = dependentArray.objectAt(itemIndex); + item = objectAt(dependentArray, itemIndex); itemPropertyKeys.forEach(removeObservers, this); diff --git a/packages/ember-runtime/lib/computed/reduce_computed_macros.js b/packages/ember-runtime/lib/computed/reduce_computed_macros.js index 755669f0f0b..7220cd1ca66 100644 --- a/packages/ember-runtime/lib/computed/reduce_computed_macros.js +++ b/packages/ember-runtime/lib/computed/reduce_computed_macros.js @@ -15,6 +15,7 @@ import { arrayComputed } from 'ember-runtime/computed/array_computed'; import { reduceComputed } from 'ember-runtime/computed/reduce_computed'; import SubArray from 'ember-runtime/system/subarray'; import compare from 'ember-runtime/compare'; +import { objectAt } from 'ember-runtime/mixins/array'; var a_slice = [].slice; @@ -625,7 +626,7 @@ function binarySearch(array, item, low, high) { } mid = low + Math.floor((high - low) / 2); - midItem = array.objectAt(mid); + midItem = objectAt(array, mid); guidMid = guidFor(midItem); guidItem = guidFor(item); diff --git a/packages/ember-runtime/lib/controllers/array_controller.js b/packages/ember-runtime/lib/controllers/array_controller.js index 9385f5439e0..d99082b1ebd 100644 --- a/packages/ember-runtime/lib/controllers/array_controller.js +++ b/packages/ember-runtime/lib/controllers/array_controller.js @@ -10,8 +10,8 @@ import SortableMixin from 'ember-runtime/mixins/sortable'; import ControllerMixin from 'ember-runtime/mixins/controller'; import { computed } from 'ember-metal/computed'; import EmberError from 'ember-metal/error'; -import EmberArray from 'ember-runtime/mixins/array'; import replace from 'ember-metal/replace'; +import { objectAt } from 'ember-runtime/mixins/array'; export var arrayControllerDeprecation = '`Ember.ArrayController` is deprecated.'; @@ -155,7 +155,7 @@ export default ArrayProxy.extend(ControllerMixin, SortableMixin, { objectAtContent(idx) { var length = get(this, 'length'); var arrangedContent = get(this, 'arrangedContent'); - var object = arrangedContent && arrangedContent.objectAt(idx); + var object = arrangedContent && objectAt(arrangedContent, idx); var controllerClass; if (idx >= 0 && idx < length) { @@ -186,7 +186,7 @@ export default ArrayProxy.extend(ControllerMixin, SortableMixin, { if (subControllers.length) { var subControllersToRemove = subControllers.slice(idx, idx + removedCnt); - subControllersToRemove.forEach(function(subController) { + subControllersToRemove.forEach(subController => { if (subController) { subController.destroy(); } @@ -212,12 +212,6 @@ export default ArrayProxy.extend(ControllerMixin, SortableMixin, { return Ember.A(); }, set(key, value) { - Ember.assert( - 'ArrayController expects `model` to implement the Ember.Array mixin. ' + - 'This can often be fixed by wrapping your model with `Ember.A()`.', - EmberArray.detect(value) || !value - ); - return value; } }), diff --git a/packages/ember-runtime/lib/mixins/array.js b/packages/ember-runtime/lib/mixins/array.js index 297cdfc975c..2b90d2527b6 100644 --- a/packages/ember-runtime/lib/mixins/array.js +++ b/packages/ember-runtime/lib/mixins/array.js @@ -28,6 +28,83 @@ import { } from 'ember-metal/events'; import { isWatching } from 'ember-metal/watching'; +export function arrayContentDidChange(array, startIdx, removeAmt, addAmt) { + var adding; + + // if no args are passed assume everything changes + if (startIdx === undefined) { + startIdx = 0; + removeAmt = addAmt = -1; + } else { + if (removeAmt === undefined) { + removeAmt = -1; + } + + if (addAmt === undefined) { + addAmt = -1; + } + } + + adding = addAmt; + + var hasDelta = addAmt < 0 || removeAmt < 0 || addAmt - removeAmt !== 0; + + propertyDidChange(array, '[]'); + + if (hasDelta) { + propertyDidChange(array, 'length'); + } + sendEvent(array, '@array:change', [array, startIdx, removeAmt, addAmt]); + + var length = get(array, 'length'); + var cachedFirst = cacheFor(array, 'firstObject'); + var cachedLast = cacheFor(array, 'lastObject'); + + if (objectAt(array, 0) !== cachedFirst) { + propertyWillChange(array, 'firstObject'); + propertyDidChange(array, 'firstObject'); + } + + if (objectAt(array, length-1) !== cachedLast) { + propertyWillChange(array, 'lastObject'); + propertyDidChange(array, 'lastObject'); + } +} + +export function arrayContentWillChange(array, startIdx, removeAmt, addAmt) { + var removing; + + // if no args are passed assume everything changes + if (startIdx === undefined) { + startIdx = 0; + removeAmt = addAmt = -1; + } else { + if (removeAmt === undefined) { + removeAmt = -1; + } + + if (addAmt === undefined) { + addAmt = -1; + } + } + + // Make sure the @each proxy is set up if anyone is observing @each + if (isWatching(array, '@each')) { + get(array, '@each'); + } + + sendEvent(array, '@array:before', [array, startIdx, removeAmt, addAmt]); + + removing = removeAmt; + + var hasDelta = addAmt < 0 || removeAmt < 0 || addAmt - removeAmt !== 0; + propertyWillChange(array, '[]'); + + if (hasDelta) { + propertyWillChange(array, 'length'); + } +} + function arrayObserversHelper(obj, target, opts, operation, notify) { var willChange = (opts && opts.willChange) || 'arrayWillChange'; var didChange = (opts && opts.didChange) || 'arrayDidChange'; @@ -47,6 +124,20 @@ function arrayObserversHelper(obj, target, opts, operation, notify) { return obj; } +export function addArrayObserver(array, target, opts) { + return arrayObserversHelper(array, target, opts, addListener, false); +} + +export function removeArrayObserver(array, target, opts) { + return arrayObserversHelper(array, target, opts, removeListener, true); +} + +export function objectAt(content, idx) { + if (content.objectAt) { return content.objectAt(idx); } + return content[idx]; +} + + // .......................................................... // ARRAY // @@ -78,9 +169,6 @@ function arrayObserversHelper(obj, target, opts, operation, notify) { To support `Ember.Array` in your own class, you must override two primitives to use it: `replace()` and `objectAt()`. - Note that the Ember.Array mixin also incorporates the `Ember.Enumerable` - mixin. All `Ember.Array`-like objects are also enumerable. - @class Array @namespace Ember @uses Ember.Enumerable @@ -148,16 +236,11 @@ export default Mixin.create(Enumerable, { @public */ objectsAt(indexes) { - var self = this; - - return indexes.map(function(idx) { - return self.objectAt(idx); - }); + return indexes.map(idx => objectAt(this, idx)); }, - // overrides Ember.Enumerable version nextObject(idx) { - return this.objectAt(idx); + return objectAt(this, idx); }, /** @@ -165,8 +248,6 @@ export default Mixin.create(Enumerable, { this property, it will return this. If you set this property to a new array, it will replace the current content. - This property overrides the default property defined in `Ember.Enumerable`. - @property [] @return this @public @@ -189,7 +270,6 @@ export default Mixin.create(Enumerable, { return this.objectAt(get(this, 'length') - 1); }), - // optimized version from Enumerable contains(obj) { return this.indexOf(obj) >= 0; }, @@ -406,42 +486,7 @@ export default Mixin.create(Enumerable, { @public */ arrayContentWillChange(startIdx, removeAmt, addAmt) { - var removing, lim; - - // if no args are passed assume everything changes - if (startIdx === undefined) { - startIdx = 0; - removeAmt = addAmt = -1; - } else { - if (removeAmt === undefined) { - removeAmt = -1; - } - - if (addAmt === undefined) { - addAmt = -1; - } - } - - // Make sure the @each proxy is set up if anyone is observing @each - if (isWatching(this, '@each')) { - get(this, '@each'); - } - - sendEvent(this, '@array:before', [this, startIdx, removeAmt, addAmt]); - - if (startIdx >= 0 && removeAmt >= 0 && get(this, 'hasEnumerableObservers')) { - removing = []; - lim = startIdx + removeAmt; - - for (var idx = startIdx; idx < lim; idx++) { - removing.push(this.objectAt(idx)); - } - } else { - removing = removeAmt; - } - - this.enumerableContentWillChange(removing, addAmt); - + arrayContentWillChange(this, startIdx, removeAmt, addAmt); return this; }, @@ -461,50 +506,7 @@ export default Mixin.create(Enumerable, { @public */ arrayContentDidChange(startIdx, removeAmt, addAmt) { - var adding, lim; - - // if no args are passed assume everything changes - if (startIdx === undefined) { - startIdx = 0; - removeAmt = addAmt = -1; - } else { - if (removeAmt === undefined) { - removeAmt = -1; - } - - if (addAmt === undefined) { - addAmt = -1; - } - } - - if (startIdx >= 0 && addAmt >= 0 && get(this, 'hasEnumerableObservers')) { - adding = []; - lim = startIdx + addAmt; - - for (var idx = startIdx; idx < lim; idx++) { - adding.push(this.objectAt(idx)); - } - } else { - adding = addAmt; - } - - this.enumerableContentDidChange(removeAmt, adding); - sendEvent(this, '@array:change', [this, startIdx, removeAmt, addAmt]); - - var length = get(this, 'length'); - var cachedFirst = cacheFor(this, 'firstObject'); - var cachedLast = cacheFor(this, 'lastObject'); - - if (this.objectAt(0) !== cachedFirst) { - propertyWillChange(this, 'firstObject'); - propertyDidChange(this, 'firstObject'); - } - - if (this.objectAt(length-1) !== cachedLast) { - propertyWillChange(this, 'lastObject'); - propertyDidChange(this, 'lastObject'); - } - + arrayContentDidChange(this, startIdx, removeAmt, addAmt); return this; }, @@ -515,7 +517,7 @@ export default Mixin.create(Enumerable, { /** Returns a special object that can be used to observe individual properties on the array. Just get an equivalent property on this object and it will - return an enumerable that maps automatically to the named key on the + return an that maps automatically to the named key on the member objects. If you merely want to watch for any items being added or removed to the array, diff --git a/packages/ember-runtime/lib/mixins/enumerable.js b/packages/ember-runtime/lib/mixins/enumerable.js index e06e5bc467d..e94bb6faae1 100644 --- a/packages/ember-runtime/lib/mixins/enumerable.js +++ b/packages/ember-runtime/lib/mixins/enumerable.js @@ -15,16 +15,6 @@ import { aliasMethod } from 'ember-metal/mixin'; import { computed } from 'ember-metal/computed'; -import { - propertyWillChange, - propertyDidChange -} from 'ember-metal/property_events'; -import { - addListener, - removeListener, - sendEvent, - hasListeners -} from 'ember-metal/events'; import compare from 'ember-runtime/compare'; var contexts = []; @@ -220,11 +210,7 @@ export default Mixin.create({ @private */ contains(obj) { - var found = this.find(function(item) { - return item === obj; - }); - - return found !== undefined; + return this.find(item => item === obj) !== undefined; }, /** @@ -301,9 +287,7 @@ export default Mixin.create({ @private */ setEach(key, value) { - return this.forEach(function(item) { - set(item, key, value); - }); + return this.forEach(item => set(item, key, value)); }, /** @@ -336,7 +320,7 @@ export default Mixin.create({ map(callback, target) { var ret = Ember.A(); - this.forEach(function(x, idx, i) { + this.forEach((x, idx, i) => { ret[idx] = callback.call(target, x, idx, i); }); @@ -353,9 +337,7 @@ export default Mixin.create({ @private */ mapBy(key) { - return this.map(function(next) { - return get(next, key); - }); + return this.map(next => get(next, key)); }, /** @@ -403,7 +385,7 @@ export default Mixin.create({ filter(callback, target) { var ret = Ember.A(); - this.forEach(function(x, idx, i) { + this.forEach((x, idx, i) => { if (callback.call(target, x, idx, i)) { ret.push(x); } @@ -850,9 +832,9 @@ export default Mixin.create({ var ret = initialValue; - this.forEach(function(item, i) { + this.forEach((item, i) => { ret = callback(ret, item, i, this, reducerProperty); - }, this); + }); return ret; }, @@ -871,7 +853,7 @@ export default Mixin.create({ invoke(methodName, ...args) { var ret = Ember.A(); - this.forEach(function(x, idx) { + this.forEach((x, idx) => { var method = x && x[methodName]; if ('function' === typeof method) { @@ -893,9 +875,7 @@ export default Mixin.create({ toArray() { var ret = Ember.A(); - this.forEach(function(o, idx) { - ret[idx] = o; - }); + this.forEach((o, idx) => ret[idx] = o); return ret; }, @@ -913,9 +893,7 @@ export default Mixin.create({ @private */ compact() { - return this.filter(function(value) { - return value != null; - }); + return this.filter((value) => value != null); }, /** @@ -940,7 +918,7 @@ export default Mixin.create({ var ret = Ember.A(); - this.forEach(function(k) { + this.forEach((k) => { if (k !== value) { ret[ret.length] = k; } @@ -993,188 +971,6 @@ export default Mixin.create({ get(key) { return this; } }), - // .......................................................... - // ENUMERABLE OBSERVERS - // - - /** - Registers an enumerable observer. Must implement `Ember.EnumerableObserver` - mixin. - - @method addEnumerableObserver - @param {Object} target - @param {Object} [opts] - @return this - @private - */ - addEnumerableObserver(target, opts) { - var willChange = (opts && opts.willChange) || 'enumerableWillChange'; - var didChange = (opts && opts.didChange) || 'enumerableDidChange'; - var hasObservers = get(this, 'hasEnumerableObservers'); - - if (!hasObservers) { - propertyWillChange(this, 'hasEnumerableObservers'); - } - - addListener(this, '@enumerable:before', target, willChange); - addListener(this, '@enumerable:change', target, didChange); - - if (!hasObservers) { - propertyDidChange(this, 'hasEnumerableObservers'); - } - - return this; - }, - - /** - Removes a registered enumerable observer. - - @method removeEnumerableObserver - @param {Object} target - @param {Object} [opts] - @return this - @private - */ - removeEnumerableObserver(target, opts) { - var willChange = (opts && opts.willChange) || 'enumerableWillChange'; - var didChange = (opts && opts.didChange) || 'enumerableDidChange'; - var hasObservers = get(this, 'hasEnumerableObservers'); - - if (hasObservers) { - propertyWillChange(this, 'hasEnumerableObservers'); - } - - removeListener(this, '@enumerable:before', target, willChange); - removeListener(this, '@enumerable:change', target, didChange); - - if (hasObservers) { - propertyDidChange(this, 'hasEnumerableObservers'); - } - - return this; - }, - - /** - Becomes true whenever the array currently has observers watching changes - on the array. - - @property hasEnumerableObservers - @type Boolean - @private - */ - hasEnumerableObservers: computed(function() { - return hasListeners(this, '@enumerable:change') || hasListeners(this, '@enumerable:before'); - }), - - - /** - Invoke this method just before the contents of your enumerable will - change. You can either omit the parameters completely or pass the objects - to be removed or added if available or just a count. - - @method enumerableContentWillChange - @param {Ember.Enumerable|Number} removing An enumerable of the objects to - be removed or the number of items to be removed. - @param {Ember.Enumerable|Number} adding An enumerable of the objects to be - added or the number of items to be added. - @chainable - @private - */ - enumerableContentWillChange(removing, adding) { - var removeCnt, addCnt, hasDelta; - - if ('number' === typeof removing) { - removeCnt = removing; - } else if (removing) { - removeCnt = get(removing, 'length'); - } else { - removeCnt = removing = -1; - } - - if ('number' === typeof adding) { - addCnt = adding; - } else if (adding) { - addCnt = get(adding, 'length'); - } else { - addCnt = adding = -1; - } - - hasDelta = addCnt < 0 || removeCnt < 0 || addCnt - removeCnt !== 0; - - if (removing === -1) { - removing = null; - } - - if (adding === -1) { - adding = null; - } - - propertyWillChange(this, '[]'); - - if (hasDelta) { - propertyWillChange(this, 'length'); - } - - sendEvent(this, '@enumerable:before', [this, removing, adding]); - - return this; - }, - - /** - Invoke this method when the contents of your enumerable has changed. - This will notify any observers watching for content changes. If you are - implementing an ordered enumerable (such as an array), also pass the - start and end values where the content changed so that it can be used to - notify range observers. - - @method enumerableContentDidChange - @param {Ember.Enumerable|Number} removing An enumerable of the objects to - be removed or the number of items to be removed. - @param {Ember.Enumerable|Number} adding An enumerable of the objects to - be added or the number of items to be added. - @chainable - @private - */ - enumerableContentDidChange(removing, adding) { - var removeCnt, addCnt, hasDelta; - - if ('number' === typeof removing) { - removeCnt = removing; - } else if (removing) { - removeCnt = get(removing, 'length'); - } else { - removeCnt = removing = -1; - } - - if ('number' === typeof adding) { - addCnt = adding; - } else if (adding) { - addCnt = get(adding, 'length'); - } else { - addCnt = adding = -1; - } - - hasDelta = addCnt < 0 || removeCnt < 0 || addCnt - removeCnt !== 0; - - if (removing === -1) { - removing = null; - } - - if (adding === -1) { - adding = null; - } - - sendEvent(this, '@enumerable:change', [this, removing, adding]); - - if (hasDelta) { - propertyDidChange(this, 'length'); - } - - propertyDidChange(this, '[]'); - - return this; - }, - /** Converts the enumerable into an array and sorts by the keys specified in the argument. @@ -1190,7 +986,7 @@ export default Mixin.create({ sortBy() { var sortKeys = arguments; - return this.toArray().sort(function(a, b) { + return this.toArray().sort((a, b) => { for (var i = 0; i < sortKeys.length; i++) { var key = sortKeys[i]; var propA = get(a, key); diff --git a/packages/ember-runtime/lib/mixins/sortable.js b/packages/ember-runtime/lib/mixins/sortable.js index 624f94a180c..20842ca96a0 100644 --- a/packages/ember-runtime/lib/mixins/sortable.js +++ b/packages/ember-runtime/lib/mixins/sortable.js @@ -19,6 +19,9 @@ import { beforeObserver, observer } from 'ember-metal/mixin'; //ES6TODO: should we access these directly from their package or from how their exposed in ember-metal? +import { + objectAt +} from 'ember-runtime/mixins/array'; /** `Ember.SortableMixin` provides a standard interface for array proxies @@ -272,8 +275,8 @@ export default Mixin.create(MutableEnumerable, { contentItemSortPropertyDidChange(item) { var arrangedContent = get(this, 'arrangedContent'); var oldIndex = arrangedContent.indexOf(item); - var leftItem = arrangedContent.objectAt(oldIndex - 1); - var rightItem = arrangedContent.objectAt(oldIndex + 1); + var leftItem = objectAt(arrangedContent, oldIndex - 1); + var rightItem = objectAt(arrangedContent, oldIndex + 1); var leftResult = leftItem && this.orderBy(item, leftItem); var rightResult = rightItem && this.orderBy(item, rightItem); @@ -293,7 +296,7 @@ export default Mixin.create(MutableEnumerable, { arrangedContent = get(this, 'arrangedContent'); mid = low + Math.floor((high - low) / 2); - midItem = arrangedContent.objectAt(mid); + midItem = objectAt(arrangedContent, mid); res = this.orderBy(midItem, item); diff --git a/packages/ember-runtime/lib/system/array_proxy.js b/packages/ember-runtime/lib/system/array_proxy.js index 94d38deb33b..6239a99ed9b 100644 --- a/packages/ember-runtime/lib/system/array_proxy.js +++ b/packages/ember-runtime/lib/system/array_proxy.js @@ -15,9 +15,13 @@ import { import EmberError from 'ember-metal/error'; import EmberObject from 'ember-runtime/system/object'; import MutableArray from 'ember-runtime/mixins/mutable_array'; -import Enumerable from 'ember-runtime/mixins/enumerable'; -import { fmt } from 'ember-runtime/system/string'; import alias from 'ember-metal/alias'; +import { + addArrayObserver, + removeArrayObserver, + objectAt +} from 'ember-runtime/mixins/array'; +import { replace } from 'ember-runtime/system/native_array'; /** @module ember @@ -27,8 +31,6 @@ import alias from 'ember-metal/alias'; var OUT_OF_RANGE_EXCEPTION = 'Index out of range'; var EMPTY = []; -function K() { return this; } - /** An ArrayProxy wraps any other object that implements `Ember.Array` and/or `Ember.MutableArray,` forwarding all requests. This makes it very useful for @@ -68,7 +70,7 @@ function K() { return this; } @uses Ember.MutableArray @private */ -var ArrayProxy = EmberObject.extend(MutableArray, { +export default EmberObject.extend(MutableArray, { /** The content array. Must be an object that implements `Ember.Array` and/or @@ -103,7 +105,7 @@ var ArrayProxy = EmberObject.extend(MutableArray, { @private */ objectAtContent(idx) { - return get(this, 'arrangedContent').objectAt(idx); + return objectAt(get(this, 'arrangedContent'), idx); }, /** @@ -122,7 +124,7 @@ var ArrayProxy = EmberObject.extend(MutableArray, { @private */ replaceContent(idx, amt, objects) { - get(this, 'content').replace(idx, amt, objects); + replace(get(this, 'content'), idx, amt, objects); }, /** @@ -136,11 +138,16 @@ var ArrayProxy = EmberObject.extend(MutableArray, { this._teardownContent(); }), + /** + + @private + @method _teardownContent + */ _teardownContent() { var content = get(this, 'content'); if (content) { - content.removeArrayObserver(this, { + removeArrayObserver(content, this, { willChange: 'contentArrayWillChange', didChange: 'contentArrayDidChange' }); @@ -158,7 +165,8 @@ var ArrayProxy = EmberObject.extend(MutableArray, { @param {Number} addCount count of items added @private */ - contentArrayWillChange: K, + contentArrayWillChange() {}, + /** Override to implement content array `didChange` observer. @@ -170,7 +178,7 @@ var ArrayProxy = EmberObject.extend(MutableArray, { @param {Number} addCount count of items added @private */ - contentArrayDidChange: K, + contentArrayDidChange() {}, /** Invoked when the content property changes. Notifies observers that the @@ -191,11 +199,10 @@ var ArrayProxy = EmberObject.extend(MutableArray, { var content = get(this, 'content'); if (content) { - Ember.assert(fmt('ArrayProxy expects an Array or ' + - 'Ember.ArrayProxy, but you passed %@', [typeof content]), - isArray(content) || content.isDestroyed); + Ember.assert(`ArrayProxy expects an Array or Ember.ArrayProxy, but you passed ${typeof content}`, + isArray(content) || content.isDestroyed); - content.addArrayObserver(this, { + addArrayObserver(content, this, { willChange: 'contentArrayWillChange', didChange: 'contentArrayDidChange' }); @@ -228,11 +235,10 @@ var ArrayProxy = EmberObject.extend(MutableArray, { var arrangedContent = get(this, 'arrangedContent'); if (arrangedContent) { - Ember.assert(fmt('ArrayProxy expects an Array or ' + - 'Ember.ArrayProxy, but you passed %@', [typeof arrangedContent]), + Ember.assert(`ArrayProxy expects an Array or Ember.ArrayProxy, but you passed ${typeof arrangedContent}`, isArray(arrangedContent) || arrangedContent.isDestroyed); - arrangedContent.addArrayObserver(this, { + addArrayObserver(arrangedContent, this, { willChange: 'arrangedContentArrayWillChange', didChange: 'arrangedContentArrayDidChange' }); @@ -243,15 +249,15 @@ var ArrayProxy = EmberObject.extend(MutableArray, { var arrangedContent = get(this, 'arrangedContent'); if (arrangedContent) { - arrangedContent.removeArrayObserver(this, { + removeArrayObserver(arrangedContent, this, { willChange: 'arrangedContentArrayWillChange', didChange: 'arrangedContentArrayDidChange' }); } }, - arrangedContentWillChange: K, - arrangedContentDidChange: K, + arrangedContentWillChange() { }, + arrangedContentDidChange() { }, objectAt(idx) { return get(this, 'content') && this.objectAtContent(idx); @@ -260,12 +266,11 @@ var ArrayProxy = EmberObject.extend(MutableArray, { length: computed(function() { var arrangedContent = get(this, 'arrangedContent'); return arrangedContent ? get(arrangedContent, 'length') : 0; - // No dependencies since Enumerable notifies length of change }), _replace(idx, amt, objects) { var content = get(this, 'content'); - Ember.assert('The content property of '+ this.constructor + ' should be set before modifying it', content); + Ember.assert(`The content property of ${this.constructor} should be set before modifying it`, content); if (content) { this.replaceContent(idx, amt, objects); } @@ -316,7 +321,7 @@ var ArrayProxy = EmberObject.extend(MutableArray, { // Get a list of indices in original content to remove for (i=start; i= idx) { - var item = content.objectAt(loc); + var item = objectAt(content, loc); if (item) { Ember.assert('When using @each to observe the array ' + content + ', the array must return an object', typeOf(item) === 'instance' || typeOf(item) === 'object'); addBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); @@ -85,7 +88,7 @@ function removeObserverForContentKey(content, keyName, proxy, idx, loc) { var indices, guid; while (--loc >= idx) { - var item = content.objectAt(loc); + var item = objectAt(content, loc); if (item) { removeBeforeObserver(item, keyName, proxy, 'contentKeyWillChange'); removeObserver(item, keyName, proxy, 'contentKeyDidChange'); @@ -109,13 +112,11 @@ var EachProxy = EmberObject.extend({ init(content) { this._super(...arguments); this._content = content; - content.addArrayObserver(this); + addArrayObserver(content, this); // in case someone is already observing some keys make sure they are // added - watchedEvents(this).forEach((eventName) => { - this.didAddListener(eventName); - }); + watchedEvents(this).forEach(eventName => this.didAddListener(eventName)); }, /** diff --git a/packages/ember-runtime/lib/system/native_array.js b/packages/ember-runtime/lib/system/native_array.js index bcd93c7fcf8..82ca361fdd1 100644 --- a/packages/ember-runtime/lib/system/native_array.js +++ b/packages/ember-runtime/lib/system/native_array.js @@ -4,16 +4,39 @@ */ import Ember from 'ember-metal/core'; // Ember.EXTEND_PROTOTYPES -import { _replace as replace } from 'ember-metal/replace'; +import { _replace } from 'ember-metal/replace'; import { get } from 'ember-metal/property_get'; import { Mixin } from 'ember-metal/mixin'; -import EmberArray from 'ember-runtime/mixins/array'; +import EmberArray, { + arrayContentWillChange, + arrayContentDidChange +} from 'ember-runtime/mixins/array'; import MutableArray from 'ember-runtime/mixins/mutable_array'; import Observable from 'ember-runtime/mixins/observable'; import Copyable from 'ember-runtime/mixins/copyable'; import { FROZEN_ERROR } from 'ember-runtime/mixins/freezable'; import copy from 'ember-runtime/copy'; +export function replace(array, index, amt, objects) { + if (this.isFrozen) { + throw FROZEN_ERROR; + } + + // if we replaced exactly the same number of items, then pass only the + // replaced range. Otherwise, pass the full remaining array length + // since everything has shifted + var len = objects ? get(objects, 'length') : 0; + arrayContentWillChange(array, index, amt, len); + + if (len === 0) { + array.splice(index, amt); + } else { + _replace(array, index, amt, objects); + } + + arrayContentDidChange(array, index, amt, len); +} + // Add Ember.Array to Array.prototype. Remove methods with native // implementations and supply some more optimized versions of generic methods // because they are so common. @@ -52,24 +75,8 @@ var NativeArray = Mixin.create(MutableArray, Observable, Copyable, { // primitive for array support. replace(idx, amt, objects) { + replace(this, idx, amt, objects); - if (this.isFrozen) { - throw FROZEN_ERROR; - } - - // if we replaced exactly the same number of items, then pass only the - // replaced range. Otherwise, pass the full remaining array length - // since everything has shifted - var len = objects ? get(objects, 'length') : 0; - this.arrayContentWillChange(idx, amt, len); - - if (len === 0) { - this.splice(idx, amt); - } else { - replace(this, idx, amt, objects); - } - - this.arrayContentDidChange(idx, amt, len); return this; }, diff --git a/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js b/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js index 48c9232ff1c..38c033a3751 100644 --- a/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js +++ b/packages/ember-runtime/tests/computed/reduce_computed_macros_test.js @@ -26,6 +26,9 @@ import { intersect as computedIntersect } from 'ember-runtime/computed/reduce_computed_macros'; +import { objectAt } from 'ember-runtime/mixins/array'; +import { replace } from 'ember-runtime/system/native_array'; + var obj, sorted, sortProps, items, userFnCalls, todos, filtered, union; QUnit.module('computedMap', { @@ -146,7 +149,7 @@ QUnit.test('it maps objects', function() { deepEqual(get(obj, 'mappedObjects'), [{ name: 'Robert' }, { name: 'Eddard' }]); run(function() { - obj.get('arrayObjects').objectAt(0).set('v', { name: 'Stannis' }); + objectAt(obj.get('arrayObjects'), 0).set('v', { name: 'Stannis' }); }); deepEqual(get(obj, 'mappedObjects'), [{ name: 'Stannis' }, { name: 'Eddard' }]); @@ -413,11 +416,11 @@ QUnit.test('properties can be filtered by truthiness', function() { deepEqual(bs.mapBy('name'), ['three', 'four'], 'booleans can be filtered'); run(function() { - set(array.objectAt(0), 'a', undefined); - set(array.objectAt(3), 'a', true); + set(objectAt(array, 0), 'a', undefined); + set(objectAt(array, 3), 'a', true); - set(array.objectAt(0), 'b', true); - set(array.objectAt(3), 'b', false); + set(objectAt(array, 0), 'b', true); + set(objectAt(array, 3), 'b', false); }); deepEqual(as.mapBy('name'), ['two', 'three', 'four'], 'arrays computed by filter property respond to property changes'); deepEqual(bs.mapBy('name'), ['one', 'three'], 'arrays computed by filtered property respond to property changes'); @@ -458,8 +461,8 @@ QUnit.test('properties can be filtered by values', function() { deepEqual(a1s.mapBy('name'), ['one', 'three'], 'arrays computed by matching value respond to removed objects'); run(function() { - set(array.objectAt(1), 'a', 1); - set(array.objectAt(2), 'a', 2); + set(objectAt(array, 1), 'a', 1); + set(objectAt(array, 2), 'a', 2); }); deepEqual(a1s.mapBy('name'), ['one', 'two'], 'arrays computed by matching value respond to modified properties'); }); @@ -781,8 +784,8 @@ function commonSortTests() { }); items = get(obj, 'items'); - items.replace(0, 1, jaime); - items.replace(1, 1, jaimeInDisguise); + replace(items, 0, 1, jaime); + replace(items, 1, 1, jaimeInDisguise); sorted = get(obj, 'sortedItems'); }); @@ -932,7 +935,7 @@ QUnit.test('updating new sort properties in place updates the sorted array', fun run(function() { items = get(obj, 'items'); - var cersei = items.objectAt(1); + var cersei = objectAt(items, 1); set(cersei, 'age', 29); // how vain }); @@ -961,7 +964,7 @@ QUnit.test('updating an item\'s sort properties updates the sorted array', funct items = get(obj, 'items'); }); - tyrionInDisguise = items.objectAt(1); + tyrionInDisguise = objectAt(items, 1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted'); @@ -980,7 +983,7 @@ QUnit.test('updating several of an item\'s sort properties updated the sorted ar items = get(obj, 'items'); }); - sansaInDisguise = items.objectAt(1); + sansaInDisguise = objectAt(items, 1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted'); @@ -1176,7 +1179,7 @@ QUnit.test('changing item properties specified via @each triggers a resort of th items = get(obj, 'items'); }); - tyrionInDisguise = items.objectAt(1); + tyrionInDisguise = objectAt(items, 1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted'); @@ -1195,7 +1198,7 @@ QUnit.test('changing item properties not specified via @each does not trigger a items = get(obj, 'items'); }); - cersei = items.objectAt(1); + cersei = objectAt(items, 1); deepEqual(sorted.mapBy('fname'), ['Cersei', 'Jaime', 'Bran', 'Robb'], 'precond - array is initially sorted'); @@ -1241,7 +1244,7 @@ QUnit.test('sorts correctly as only one property changes', function() { sorted = obj.get('sortedItems'); }); deepEqual(sorted.mapBy('name'), ['A', 'B', 'C', 'D'], 'initial'); - obj.get('items').objectAt(3).set('count', 2); + objectAt(obj.get('items'), 3).set('count', 2); run(function() { sorted = obj.get('sortedItems'); }); @@ -1284,8 +1287,8 @@ QUnit.test('sorts correctly when there are concurrent changes', function() { }); deepEqual(sorted.mapBy('name'), ['A', 'B', 'C', 'D'], 'initial'); Ember.changeProperties(function() { - obj.get('items').objectAt(1).set('count', 5); - obj.get('items').objectAt(2).set('count', 6); + objectAt(obj.get('items'), 1).set('count', 5); + objectAt(obj.get('items'), 2).set('count', 6); }); run(function() { sorted = obj.get('sortedItems'); @@ -1302,8 +1305,8 @@ QUnit.test('sorts correctly with a user-provided comparator when there are concu run(function() { Ember.changeProperties(function() { - obj.get('items').objectAt(1).set('count', 5); - obj.get('items').objectAt(2).set('count', 6); + objectAt(obj.get('items'), 1).set('count', 5); + objectAt(obj.get('items'), 2).set('count', 6); }); sorted = obj.get('customSortedItems'); deepEqual(sorted.mapBy('name'), ['A', 'D', 'B', 'C'], 'final'); @@ -1541,7 +1544,7 @@ QUnit.test('it can filter and sort when both depend on the same item property', // 2. update filtered from item property change // // If 1.b happens before 2 it should invalidate 2 - todos.objectAt(1).set('priority', 6); + objectAt(todos, 1).set('priority', 6); endPropertyChanges(); }); diff --git a/packages/ember-runtime/tests/computed/reduce_computed_test.js b/packages/ember-runtime/tests/computed/reduce_computed_test.js index d7f62b55142..d039d3508e6 100644 --- a/packages/ember-runtime/tests/computed/reduce_computed_test.js +++ b/packages/ember-runtime/tests/computed/reduce_computed_test.js @@ -16,6 +16,8 @@ import { arrayComputed } from 'ember-runtime/computed/array_computed'; import { reduceComputed } from 'ember-runtime/computed/reduce_computed'; import ArrayProxy from 'ember-runtime/system/array_proxy'; import SubArray from 'ember-runtime/system/subarray'; +import { objectAt } from 'ember-runtime/mixins/array'; +import { replace } from 'ember-runtime/system/native_array'; var obj, addCalls, removeCalls, callbackItems, shared; @@ -168,7 +170,7 @@ QUnit.test('after first retrieval, array computed properties can observe propert deepEqual(evenNestedNumbers, [2, 4, 6], 'precond -- starts off with correct values'); run(function() { - nestedNumbers.objectAt(0).set('v', 22); + objectAt(nestedNumbers, 0).set('v', 22); }); deepEqual(nestedNumbers.mapBy('v'), [22, 2, 3, 4, 5, 6], 'nested numbers is updated'); @@ -182,7 +184,7 @@ QUnit.test('changes to array computed properties happen synchronously', function deepEqual(evenNestedNumbers, [2, 4, 6], 'precond -- starts off with correct values'); run(function() { - nestedNumbers.objectAt(0).set('v', 22); + objectAt(nestedNumbers, 0).set('v', 22); deepEqual(nestedNumbers.mapBy('v'), [22, 2, 3, 4, 5, 6], 'nested numbers is updated'); deepEqual(evenNestedNumbers, [2, 4, 6, 22], 'adds new number'); }); @@ -319,14 +321,16 @@ QUnit.test('array observers are torn down when dependent arrays change', functio equal(addCalls, 6, 'precond - add has been called for each item in the array'); equal(removeCalls, 0, 'precond - removed has not been called'); - run(function() { + Ember.run(() => { set(obj, 'numbers', Ember.A([20, 23, 28])); }); equal(addCalls, 9, 'add is called for each item in the new array'); equal(removeCalls, 0, 'remove is not called when the array is reset'); - numbers.replace(0, numbers.get('length'), Ember.A([7,8,9,10])); + Ember.run(() => { + replace(numbers, 0, numbers.get('length'), Ember.A([7,8,9,10])); + }); equal(addCalls, 9, 'add is not called'); equal(removeCalls, 0, 'remove is not called'); @@ -339,8 +343,8 @@ QUnit.test('modifying properties on dependent array items triggers observers exa equal(addCalls, 6, 'precond - add has not been called for each item in the array'); equal(removeCalls, 0, 'precond - removed has not been called'); - run(function() { - numbers.replace(0, 2, [7,8,9,10]); + Ember.run(() => { + replace(numbers, 0, 2, [7,8,9,10]); }); equal(addCalls, 10, 'add is called for each item added'); @@ -431,11 +435,11 @@ QUnit.test('dependent arrays can use `replace` with an out of bounds index to ad deepEqual(array, [], 'precond - computed array is initially empty'); - dependentArray.replace(100, 0, [1, 2]); + replace(dependentArray, 100, 0, [1, 2]); deepEqual(array, [1, 2], 'index >= length treated as a push'); - dependentArray.replace(-100, 0, [3, 4]); + replace(dependentArray, -100, 0, [3, 4]); deepEqual(array, [3, 4, 1, 2], 'index < 0 treated as an unshift'); }); @@ -459,7 +463,7 @@ QUnit.test('dependent arrays can use `replace` with a negative index to remove i deepEqual(array, [], 'precond - no items have been removed initially'); - dependentArray.replace(-3, 2); + replace(dependentArray, -3, 2); deepEqual(array, [4,3], 'index < 0 used as a right index for removal'); }); @@ -482,7 +486,7 @@ QUnit.test('dependent arrays that call `replace` with an out of bounds index to deepEqual(array, [], 'precond - computed array is initially empty'); - dependentArray.replace(100, 2); + replace(dependentArray, 100, 2); }); QUnit.test('dependent arrays that call `replace` with a too-large removedCount a) works and b) still right-truncates', function() { @@ -504,7 +508,7 @@ QUnit.test('dependent arrays that call `replace` with a too-large removedCount a deepEqual(array, [], 'precond - computed array is initially empty'); - dependentArray.replace(1, 200); + replace(dependentArray, 1, 200); deepEqual(array, [2], 'array was correctly right-truncated'); }); @@ -638,7 +642,7 @@ QUnit.test('@this can be used to treat the object as the array itself', function deepEqual(names, ['a', 'b'], 'precond - names is initially correct'); run(function() { - obj.objectAt(1).set('name', 'c'); + objectAt(obj, 1).set('name', 'c'); }); deepEqual(names, ['a', 'c'], '@this can be used with item property observers'); @@ -714,7 +718,7 @@ QUnit.test('changeMeta includes item and index', function() { // remove0 add0 run(function() { - items.objectAt(0).set('n', 'zero\'\''); + objectAt(items, 0).set('n', `zero''`); }); expected = expected.concat(['add:2:five', 'add:3:six', 'remove:0:zero\'\'', 'add:0:zero\'\'']); @@ -723,7 +727,7 @@ QUnit.test('changeMeta includes item and index', function() { // [zero'', one, five, six] -> [zero'', five, six] // remove1 run(function() { - item = items.objectAt(1); + item = objectAt(items, 1); items.removeAt(1, 1); }); @@ -735,7 +739,7 @@ QUnit.test('changeMeta includes item and index', function() { // [zero'', five, six] -> [zero'', five, seven] // remove2 add2 run(function() { - items.objectAt(2).set('n', 'seven'); + objectAt(items, 2).set('n', 'seven'); }); // observer should have been added to the new item @@ -744,7 +748,7 @@ QUnit.test('changeMeta includes item and index', function() { // reset (does not call remove) run(function() { - item = items.objectAt(1); + item = objectAt(items, 1); set(obj, 'items', Ember.A([])); }); @@ -775,7 +779,7 @@ QUnit.test('changeMeta includes changedCount and arrayChanged', function() { obj.get('lettersArrayComputed'); letters.pushObject('c'); letters.popObject(); - letters.replace(0, 1, ['d']); + replace(letters, 0, 1, ['d']); letters.removeAt(0, letters.length); var expected = ['add:2:ab', 'add:2:ab', 'add:1:abc', 'remove:1:abc', 'remove:1:ab', 'add:1:db', 'remove:2:db', 'remove:2:db']; diff --git a/packages/ember-runtime/tests/controllers/array_controller_test.js b/packages/ember-runtime/tests/controllers/array_controller_test.js index c94be92abd2..3be868290d5 100644 --- a/packages/ember-runtime/tests/controllers/array_controller_test.js +++ b/packages/ember-runtime/tests/controllers/array_controller_test.js @@ -13,7 +13,7 @@ MutableArrayTests.extend({ var ret = ary ? ary.slice() : this.newFixture(3); expectDeprecation(arrayControllerDeprecation); return ArrayController.create({ - model: Ember.A(ret) + model: ret }); }, @@ -56,15 +56,9 @@ QUnit.test('works properly when model is set to a plain array', function() { expectDeprecation(arrayControllerDeprecation); var controller = ArrayController.create(); - if (Ember.EXTEND_PROTOTYPES) { - set(controller, 'model', ['red', 'green']); + set(controller, 'model', ['red', 'green']); - deepEqual(get(controller, 'model'), ['red', 'green'], 'can set model as a plain array'); - } else { - expectAssertion(function() { - set(controller, 'model', ['red', 'green']); - }, /ArrayController expects `model` to implement the Ember.Array mixin. This can often be fixed by wrapping your model with `Ember\.A\(\)`./); - } + deepEqual(get(controller, 'model'), ['red', 'green'], 'can set model as a plain array'); }); QUnit.test('works properly when model is set to `null`', function() { diff --git a/packages/ember-runtime/tests/controllers/item_controller_class_test.js b/packages/ember-runtime/tests/controllers/item_controller_class_test.js index 2d8738474f5..fe197c6dd5b 100644 --- a/packages/ember-runtime/tests/controllers/item_controller_class_test.js +++ b/packages/ember-runtime/tests/controllers/item_controller_class_test.js @@ -9,6 +9,10 @@ import ArrayController, { arrayControllerDeprecation } from 'ember-runtime/contr import Controller from 'ember-runtime/controllers/controller'; import {sort} from 'ember-runtime/computed/reduce_computed_macros'; import Registry from 'container/registry'; +import { + addArrayObserver, + objectAt +} from 'ember-runtime/mixins/array'; var lannisters, arrayController, controllerClass, otherControllerClass, registry, container, itemControllerCount, tywin, jaime, cersei, tyrion; @@ -311,15 +315,16 @@ QUnit.test('array observers can invoke `objectAt` without overwriting existing i lannistersWillChange() { return this; }, lannistersDidChange(_, idx, removedAmt, addedAmt) { arrayObserverCalled = true; - equal(this.objectAt(idx).get('model.name'), 'Tyrion', 'Array observers get the right object via `objectAt`'); + equal(objectAt(this, idx).get('model.name'), 'Tyrion', 'Array observers get the right object via `objectAt`'); } }); - arrayController.addArrayObserver(arrayController, { + + addArrayObserver(arrayController, arrayController, { willChange: 'lannistersWillChange', didChange: 'lannistersDidChange' }); - run(function() { + run(() => { lannisters.unshiftObject(tyrion); }); diff --git a/packages/ember-runtime/tests/legacy_1x/mixins/observable/observable_test.js b/packages/ember-runtime/tests/legacy_1x/mixins/observable/observable_test.js index 6b7450bdca1..260036e63b0 100644 --- a/packages/ember-runtime/tests/legacy_1x/mixins/observable/observable_test.js +++ b/packages/ember-runtime/tests/legacy_1x/mixins/observable/observable_test.js @@ -6,7 +6,7 @@ import { observer } from 'ember-metal/mixin'; import { fmt, w } from 'ember-runtime/system/string'; import EmberObject from 'ember-runtime/system/object'; import Observable from 'ember-runtime/mixins/observable'; - +import { replace } from 'ember-runtime/system/native_array'; /* NOTE: This test is adapted from the 1.x series of unit tests. The tests are the same except for places where we intend to break the API we instead @@ -753,7 +753,7 @@ QUnit.test('toggle function, should be boolean', function() { }); QUnit.test('should notify array observer when array changes', function() { - get(object, 'normalArray').replace(0, 0, 6); + replace(get(object, 'normalArray'), 0, 0, 6); equal(object.abnormal, 'notifiedObserver', 'observer should be notified'); }); diff --git a/packages/ember-runtime/tests/mixins/array_test.js b/packages/ember-runtime/tests/mixins/array_test.js index 23fa9bbbe8a..3511078191c 100644 --- a/packages/ember-runtime/tests/mixins/array_test.js +++ b/packages/ember-runtime/tests/mixins/array_test.js @@ -7,11 +7,12 @@ import { computed } from 'ember-metal/computed'; import { testBoth } from 'ember-metal/tests/props_helper'; import { ArrayTests } from 'ember-runtime/tests/suites/array'; import EmberObject from 'ember-runtime/system/object'; -import EmberArray from 'ember-runtime/mixins/array'; +import EmberArray, { + objectAt +} from 'ember-runtime/mixins/array'; /* - Implement a basic fake mutable array. This validates that any non-native - enumerable can impl this API. + Implement a basic fake mutable array. */ var TestArray = EmberObject.extend(EmberArray, { @@ -103,7 +104,7 @@ var DummyArray = EmberObject.extend(EmberArray, { objectAt(idx) { return 'ITEM-'+idx; } }); -var obj, observer; +var obj; // .......................................................... @@ -115,7 +116,7 @@ QUnit.module('mixins/array/arrayContent[Will|Did]Change'); QUnit.test('should notify observers of []', function() { obj = DummyArray.extend({ - enumerablePropertyDidChange: emberObserver('[]', function() { + arrayPropertyDidChange: emberObserver('[]', function() { this._count++; }) }).create({ @@ -178,137 +179,6 @@ QUnit.test('should notify when passed lengths are different', function() { equal(obj._after, 1); }); - -// .......................................................... -// NOTIFY ARRAY OBSERVER -// - -QUnit.module('notify array observers', { - setup() { - obj = DummyArray.create(); - - observer = EmberObject.extend({ - arrayWillChange() { - equal(this._before, null); // should only call once - this._before = Array.prototype.slice.call(arguments); - }, - - arrayDidChange() { - equal(this._after, null); // should only call once - this._after = Array.prototype.slice.call(arguments); - } - }).create({ - _before: null, - _after: null - }); - - obj.addArrayObserver(observer); - }, - - teardown() { - obj = observer = null; - } -}); - -QUnit.test('should notify enumerable observers when called with no params', function() { - obj.arrayContentWillChange(); - deepEqual(observer._before, [obj, 0, -1, -1]); - - obj.arrayContentDidChange(); - deepEqual(observer._after, [obj, 0, -1, -1]); -}); - -// API variation that included items only -QUnit.test('should notify when called with same length items', function() { - obj.arrayContentWillChange(0, 1, 1); - deepEqual(observer._before, [obj, 0, 1, 1]); - - obj.arrayContentDidChange(0, 1, 1); - deepEqual(observer._after, [obj, 0, 1, 1]); -}); - -QUnit.test('should notify when called with diff length items', function() { - obj.arrayContentWillChange(0, 2, 1); - deepEqual(observer._before, [obj, 0, 2, 1]); - - obj.arrayContentDidChange(0, 2, 1); - deepEqual(observer._after, [obj, 0, 2, 1]); -}); - -QUnit.test('removing enumerable observer should disable', function() { - obj.removeArrayObserver(observer); - obj.arrayContentWillChange(); - deepEqual(observer._before, null); - - obj.arrayContentDidChange(); - deepEqual(observer._after, null); -}); - -// .......................................................... -// NOTIFY ENUMERABLE OBSERVER -// - -QUnit.module('notify enumerable observers as well', { - setup() { - obj = DummyArray.create(); - - observer = EmberObject.extend({ - enumerableWillChange() { - equal(this._before, null); // should only call once - this._before = Array.prototype.slice.call(arguments); - }, - - enumerableDidChange() { - equal(this._after, null); // should only call once - this._after = Array.prototype.slice.call(arguments); - } - }).create({ - _before: null, - _after: null - }); - - obj.addEnumerableObserver(observer); - }, - - teardown() { - obj = observer = null; - } -}); - -QUnit.test('should notify enumerable observers when called with no params', function() { - obj.arrayContentWillChange(); - deepEqual(observer._before, [obj, null, null], 'before'); - - obj.arrayContentDidChange(); - deepEqual(observer._after, [obj, null, null], 'after'); -}); - -// API variation that included items only -QUnit.test('should notify when called with same length items', function() { - obj.arrayContentWillChange(0, 1, 1); - deepEqual(observer._before, [obj, ['ITEM-0'], 1], 'before'); - - obj.arrayContentDidChange(0, 1, 1); - deepEqual(observer._after, [obj, 1, ['ITEM-0']], 'after'); -}); - -QUnit.test('should notify when called with diff length items', function() { - obj.arrayContentWillChange(0, 2, 1); - deepEqual(observer._before, [obj, ['ITEM-0', 'ITEM-1'], 1], 'before'); - - obj.arrayContentDidChange(0, 2, 1); - deepEqual(observer._after, [obj, 2, ['ITEM-0']], 'after'); -}); - -QUnit.test('removing enumerable observer should disable', function() { - obj.removeEnumerableObserver(observer); - obj.arrayContentWillChange(); - deepEqual(observer._before, null, 'before'); - - obj.arrayContentDidChange(); - deepEqual(observer._after, null, 'after'); -}); - // .......................................................... // @each // @@ -410,15 +280,14 @@ QUnit.test('modifying the array should also indicate the isDone prop itself has var each = get(ary, '@each'); var count = 0; - addObserver(each, 'isDone', function() { count++; }); + addObserver(each, 'isDone', () => count++ ); count = 0; - var item = ary.objectAt(2); + var item = objectAt(ary, 2); set(item, 'isDone', !get(item, 'isDone')); equal(count, 1, '@each.isDone should have notified'); }); - testBoth('should be clear caches for computed properties that have dependent keys on arrays that are changed after object initialization', function(get, set) { var obj = EmberObject.extend({ init() { @@ -427,14 +296,14 @@ testBoth('should be clear caches for computed properties that have dependent key }, common: computed('resources.@each.common', function() { - return get(get(this, 'resources').objectAt(0), 'common'); + return get(objectAt(get(this, 'resources'), 0), 'common'); }) }).create(); get(obj, 'resources').pushObject(EmberObject.create({ common: 'HI!' })); equal('HI!', get(obj, 'common')); - set(get(obj, 'resources').objectAt(0), 'common', 'BYE!'); + set(objectAt(get(obj, 'resources'), 0), 'common', 'BYE!'); equal('BYE!', get(obj, 'common')); }); @@ -456,7 +325,7 @@ testBoth('observers that contain @each in the path should fire only once the fir // Observer fires second time when new object is added get(obj, 'resources').pushObject(EmberObject.create({ common: 'HI!' })); // Observer fires third time when property on an object is changed - set(get(obj, 'resources').objectAt(0), 'common', 'BYE!'); + set(objectAt(get(obj, 'resources'), 0), 'common', 'BYE!'); equal(count, 2, 'observers should only be called once'); }); diff --git a/packages/ember-runtime/tests/mixins/enumerable_test.js b/packages/ember-runtime/tests/mixins/enumerable_test.js index 239a8acc8eb..63091bd9613 100644 --- a/packages/ember-runtime/tests/mixins/enumerable_test.js +++ b/packages/ember-runtime/tests/mixins/enumerable_test.js @@ -5,21 +5,17 @@ import Enumerable from 'ember-runtime/mixins/enumerable'; import EmberArray from 'ember-runtime/mixins/array'; import { get } from 'ember-metal/property_get'; import { computed } from 'ember-metal/computed'; -import { observer as emberObserver } from 'ember-metal/mixin'; - function K() { return this; } - /* Implement a basic fake enumerable. This validates that any non-native enumerable can impl this API. */ var TestEnumerable = EmberObject.extend(Enumerable, { - _content: null, - init(ary) { + this._super(...arguments); this._content = ary || []; }, @@ -29,7 +25,6 @@ var TestEnumerable = EmberObject.extend(Enumerable, { } this._content.push(obj); - this.enumerableContentDidChange(); }, nextObject(idx) { @@ -117,7 +112,7 @@ QUnit.test('any', function() { }, { color: 'white' }]); - var foundWhite = kittens.any(function(kitten) { return kitten.color === 'white'; }); + var foundWhite = kittens.any(kitten => kitten.color === 'white'); var foundWhite2 = kittens.isAny('color', 'white'); equal(foundWhite, true); @@ -164,190 +159,3 @@ QUnit.test('every', function() { allWhite = allWhiteKittens.isEvery('color', 'white'); equal(allWhite, true); }); - -// .......................................................... -// CONTENT DID CHANGE -// - -var DummyEnum = EmberObject.extend(Enumerable, { - nextObject() {}, - length: 0 -}); - -var obj, observer; - -// .......................................................... -// NOTIFY ENUMERABLE PROPERTY -// - -QUnit.module('mixins/enumerable/enumerableContentDidChange'); - -QUnit.test('should notify observers of []', function() { - - var obj = EmberObject.extend(Enumerable, { - nextObject() {}, // avoid exceptions - - enumerablePropertyDidChange: emberObserver('[]', function() { - this._count++; - }) - }).create({ - _count: 0 - }); - - equal(obj._count, 0, 'should not have invoked yet'); - obj.enumerableContentWillChange(); - obj.enumerableContentDidChange(); - equal(obj._count, 1, 'should have invoked'); - -}); - -// .......................................................... -// NOTIFY CHANGES TO LENGTH -// - -QUnit.module('notify observers of length', { - setup() { - obj = DummyEnum.extend({ - lengthDidChange: emberObserver('length', function() { - this._after++; - }) - }).create({ - _after: 0 - }); - - equal(obj._after, 0, 'should not have fired yet'); - }, - - teardown() { - obj = null; - } -}); - -QUnit.test('should notify observers when call with no params', function() { - obj.enumerableContentWillChange(); - equal(obj._after, 0); - - obj.enumerableContentDidChange(); - equal(obj._after, 1); -}); - -// API variation that included items only -QUnit.test('should not notify when passed arrays of same length', function() { - var added = ['foo']; - var removed = ['bar']; - - obj.enumerableContentWillChange(removed, added); - equal(obj._after, 0); - - obj.enumerableContentDidChange(removed, added); - equal(obj._after, 0); -}); - -QUnit.test('should notify when passed arrays of different length', function() { - var added = ['foo']; - var removed = ['bar', 'baz']; - - obj.enumerableContentWillChange(removed, added); - equal(obj._after, 0); - - obj.enumerableContentDidChange(removed, added); - equal(obj._after, 1); -}); - -// API variation passes indexes only -QUnit.test('should not notify when passed with indexes', function() { - obj.enumerableContentWillChange(1, 1); - equal(obj._after, 0); - - obj.enumerableContentDidChange(1, 1); - equal(obj._after, 0); -}); - -QUnit.test('should notify when passed old index API with delta', function() { - obj.enumerableContentWillChange(1, 2); - equal(obj._after, 0); - - obj.enumerableContentDidChange(1, 2); - equal(obj._after, 1); -}); - - -// .......................................................... -// NOTIFY ENUMERABLE OBSERVER -// - -QUnit.module('notify enumerable observers', { - setup() { - obj = DummyEnum.create(); - - observer = EmberObject.extend({ - enumerableWillChange() { - equal(this._before, null); // should only call once - this._before = Array.prototype.slice.call(arguments); - }, - - enumerableDidChange() { - equal(this._after, null); // should only call once - this._after = Array.prototype.slice.call(arguments); - } - }).create({ - _before: null, - _after: null - }); - - obj.addEnumerableObserver(observer); - }, - - teardown() { - obj = observer = null; - } -}); - -QUnit.test('should notify enumerable observers when called with no params', function() { - obj.enumerableContentWillChange(); - deepEqual(observer._before, [obj, null, null]); - - obj.enumerableContentDidChange(); - deepEqual(observer._after, [obj, null, null]); -}); - -// API variation that included items only -QUnit.test('should notify when called with same length items', function() { - var added = ['foo']; - var removed = ['bar']; - - obj.enumerableContentWillChange(removed, added); - deepEqual(observer._before, [obj, removed, added]); - - obj.enumerableContentDidChange(removed, added); - deepEqual(observer._after, [obj, removed, added]); -}); - -QUnit.test('should notify when called with diff length items', function() { - var added = ['foo', 'baz']; - var removed = ['bar']; - - obj.enumerableContentWillChange(removed, added); - deepEqual(observer._before, [obj, removed, added]); - - obj.enumerableContentDidChange(removed, added); - deepEqual(observer._after, [obj, removed, added]); -}); - -QUnit.test('should not notify when passed with indexes only', function() { - obj.enumerableContentWillChange(1, 2); - deepEqual(observer._before, [obj, 1, 2]); - - obj.enumerableContentDidChange(1, 2); - deepEqual(observer._after, [obj, 1, 2]); -}); - -QUnit.test('removing enumerable observer should disable', function() { - obj.removeEnumerableObserver(observer); - obj.enumerableContentWillChange(); - deepEqual(observer._before, null); - - obj.enumerableContentDidChange(); - deepEqual(observer._after, null); -}); - diff --git a/packages/ember-runtime/tests/mixins/mutable_enumerable_test.js b/packages/ember-runtime/tests/mixins/mutable_enumerable_test.js index d0c5582226d..f2fa9a320e1 100644 --- a/packages/ember-runtime/tests/mixins/mutable_enumerable_test.js +++ b/packages/ember-runtime/tests/mixins/mutable_enumerable_test.js @@ -17,9 +17,7 @@ var TestMutableEnumerable = EmberObject.extend(MutableEnumerable, { return this; } - this.enumerableContentWillChange(null, [obj]); this._content.push(obj); - this.enumerableContentDidChange(null, [obj]); }, removeObject(obj) { @@ -28,9 +26,7 @@ var TestMutableEnumerable = EmberObject.extend(MutableEnumerable, { return this; } - this.enumerableContentWillChange([obj], null); this._content.splice(idx, 1); - this.enumerableContentDidChange([obj], null); return this; }, diff --git a/packages/ember-runtime/tests/mixins/sortable_test.js b/packages/ember-runtime/tests/mixins/sortable_test.js index 62e1f69fde4..9809f10e2f0 100644 --- a/packages/ember-runtime/tests/mixins/sortable_test.js +++ b/packages/ember-runtime/tests/mixins/sortable_test.js @@ -7,6 +7,8 @@ import ArrayProxy from 'ember-runtime/system/array_proxy'; import SortableMixin from 'ember-runtime/mixins/sortable'; import EmberObject from 'ember-runtime/system/object'; import ArrayController, { arrayControllerDeprecation } from 'ember-runtime/controllers/array_controller'; +import { objectAt } from 'ember-runtime/mixins/array'; +import { replace } from 'ember-runtime/system/native_array'; var unsortedArray, sortedArrayController; @@ -272,10 +274,10 @@ QUnit.test('don\'t remove and insert if position didn\'t change', function() { }); QUnit.test('sortProperties observers removed on content removal', function() { - var removedObject = unsortedArray.objectAt(2); + var removedObject = objectAt(unsortedArray, 2); equal(listenersFor(removedObject, 'name:change').length, 1, 'Before removal, there should be one listener for sortProperty change.'); - unsortedArray.replace(2, 1, []); + replace(unsortedArray, 2, 1, []); equal(listenersFor(removedObject, 'name:change').length, 0, 'After removal, there should be no listeners for sortProperty change.'); }); diff --git a/packages/ember-runtime/tests/suites/array.js b/packages/ember-runtime/tests/suites/array.js index 0a1528567ce..dab019c247b 100644 --- a/packages/ember-runtime/tests/suites/array.js +++ b/packages/ember-runtime/tests/suites/array.js @@ -5,16 +5,20 @@ import { import indexOfTests from 'ember-runtime/tests/suites/array/indexOf'; import lastIndexOfTests from 'ember-runtime/tests/suites/array/lastIndexOf'; import objectAtTests from 'ember-runtime/tests/suites/array/objectAt'; +import { + addArrayObserver, + removeArrayObserver +} from 'ember-runtime/mixins/array'; var ObserverClass = EnumerableTestsObserverClass.extend({ observeArray(obj) { - obj.addArrayObserver(this); + addArrayObserver(obj, this); return this; }, stopObserveArray(obj) { - obj.removeArrayObserver(this); + removeArrayObserver(obj, this); return this; }, diff --git a/packages/ember-runtime/tests/suites/array/objectAt.js b/packages/ember-runtime/tests/suites/array/objectAt.js index 6a5d6a16225..af544e07314 100644 --- a/packages/ember-runtime/tests/suites/array/objectAt.js +++ b/packages/ember-runtime/tests/suites/array/objectAt.js @@ -1,5 +1,5 @@ -import {SuiteModuleBuilder} from 'ember-runtime/tests/suites/suite'; -import {fmt} from 'ember-runtime/system/string'; +import { SuiteModuleBuilder } from 'ember-runtime/tests/suites/suite'; +import { objectAt } from 'ember-runtime/mixins/array'; var suite = SuiteModuleBuilder.create(); @@ -12,7 +12,7 @@ suite.test('should return object at specified index', function() { var idx; for (idx=0;idx [A,B] + notify', function() { equal(observer.validate('firstObject'), false, 'should NOT have notified firstObject once'); }); -suite.test('Adding object should notify enumerable observer', function() { - - var fixtures = this.newFixture(4); - var obj = this.newObject(fixtures); - var observer = this.newObserver(obj).observeEnumerable(obj); - var item = this.newFixture(1)[0]; - - obj.replace(2, 2, [item]); - - deepEqual(observer._before, [obj, [fixtures[2], fixtures[3]], 1], 'before'); - deepEqual(observer._after, [obj, 2, [item]], 'after'); -}); - suite.test('Adding object should notify array observer', function() { var fixtures = this.newFixture(4); diff --git a/packages/ember-runtime/tests/suites/mutable_enumerable/addObject.js b/packages/ember-runtime/tests/suites/mutable_enumerable/addObject.js index eea731d32fa..e8efef65d67 100644 --- a/packages/ember-runtime/tests/suites/mutable_enumerable/addObject.js +++ b/packages/ember-runtime/tests/suites/mutable_enumerable/addObject.js @@ -59,15 +59,4 @@ suite.test('[A,B,C].addObject(A) => [A,B,C] + NO notify', function() { } }); -suite.test('Adding object should notify enumerable observer', function() { - var obj = this.newObject(this.newFixture(3)); - var observer = this.newObserver(obj).observeEnumerable(obj); - var item = this.newFixture(1)[0]; - - obj.addObject(item); - - deepEqual(observer._before, [obj, null, [item]]); - deepEqual(observer._after, [obj, null, [item]]); -}); - export default suite; diff --git a/packages/ember-runtime/tests/suites/mutable_enumerable/removeObject.js b/packages/ember-runtime/tests/suites/mutable_enumerable/removeObject.js index 1290ced2de0..817eae5a0ec 100644 --- a/packages/ember-runtime/tests/suites/mutable_enumerable/removeObject.js +++ b/packages/ember-runtime/tests/suites/mutable_enumerable/removeObject.js @@ -60,16 +60,4 @@ suite.test('[A,B,C].removeObject(D) => [A,B,C]', function() { } }); -suite.test('Removing object should notify enumerable observer', function() { - var fixtures = this.newFixture(3); - var obj = this.newObject(fixtures); - var observer = this.newObserver(obj).observeEnumerable(obj); - var item = fixtures[1]; - - obj.removeObject(item); - - deepEqual(observer._before, [obj, [item], null]); - deepEqual(observer._after, [obj, [item], null]); -}); - export default suite; diff --git a/packages/ember-runtime/tests/suites/mutable_enumerable/removeObjects.js b/packages/ember-runtime/tests/suites/mutable_enumerable/removeObjects.js index 3ce69cf1330..b3538465007 100644 --- a/packages/ember-runtime/tests/suites/mutable_enumerable/removeObjects.js +++ b/packages/ember-runtime/tests/suites/mutable_enumerable/removeObjects.js @@ -175,16 +175,4 @@ suite.test('[A,B,C].removeObjects([D]) => [A,B,C]', function() { } }); -suite.test('Removing objects should notify enumerable observer', function() { - var fixtures = this.newFixture(3); - var obj = this.newObject(fixtures); - var observer = this.newObserver(obj).observeEnumerable(obj); - var item = fixtures[1]; - - obj.removeObjects([item]); - - deepEqual(observer._before, [obj, [item], null]); - deepEqual(observer._after, [obj, [item], null]); -}); - export default suite; diff --git a/packages/ember-runtime/tests/system/array_proxy/arranged_content_test.js b/packages/ember-runtime/tests/system/array_proxy/arranged_content_test.js index 6431858f29b..51cfd2b4266 100644 --- a/packages/ember-runtime/tests/system/array_proxy/arranged_content_test.js +++ b/packages/ember-runtime/tests/system/array_proxy/arranged_content_test.js @@ -2,6 +2,8 @@ import Ember from 'ember-metal/core'; import run from 'ember-metal/run_loop'; import {computed} from 'ember-metal/computed'; import ArrayProxy from 'ember-runtime/system/array_proxy'; +import { objectAt } from 'ember-runtime/mixins/array'; +import { replace } from 'ember-runtime/system/native_array'; var array; @@ -11,14 +13,14 @@ QUnit.module('ArrayProxy - arrangedContent', { array = ArrayProxy.extend({ arrangedContent: computed('content.[]', function() { var content = this.get('content'); - return content && Ember.A(content.slice().sort(function(a, b) { + return content && content.slice().sort(function(a, b) { if (a == null) { a = -1; } if (b == null) { b = -1; } return b - a; - })); + }); }) }).create({ - content: Ember.A([1,2,4,5]) + content: [1,2,4,5] }); }); }, @@ -69,7 +71,7 @@ QUnit.test('nextObject - returns object at index in arrangedContent', function() }); QUnit.test('objectAt - returns object at index in arrangedContent', function() { - equal(array.objectAt(1), 4, 'returns object at index'); + equal(objectAt(array, 1), 4, 'returns object at index'); }); // Not sure if we need a specific test for it, since it's internal @@ -115,7 +117,7 @@ QUnit.test('removeObjects - removes objects from content', function() { QUnit.test('replace - raises, indeterminate behavior', function() { throws(function() { - run(function() { array.replace(1, 2, [3]); }); + run(function() { replace(array, 1, 2, [3]); }); }); }); @@ -217,7 +219,7 @@ QUnit.module('ArrayProxy - arrangedContent with transforms', { }).property('content.[]'), objectAtContent(idx) { - var obj = this.get('arrangedContent').objectAt(idx); + var obj = objectAt(this.get('arrangedContent'), idx); return obj && obj.toString(); } }).create({ @@ -246,7 +248,7 @@ QUnit.test('nextObject - returns object at index in arrangedContent', function() }); QUnit.test('objectAt - returns object at index in arrangedContent', function() { - equal(array.objectAt(1), '4', 'returns object at index'); + equal(objectAt(array, 1), '4', 'returns object at index'); }); // Not sure if we need a specific test for it, since it's internal diff --git a/packages/ember-views/lib/system/jquery.js b/packages/ember-views/lib/system/jquery.js index 24b98c3ca5f..bd040b99589 100644 --- a/packages/ember-views/lib/system/jquery.js +++ b/packages/ember-views/lib/system/jquery.js @@ -1,6 +1,5 @@ import Ember from 'ember-metal/core'; // Ember.assert -// ES6TODO: the functions on EnumerableUtils need their own exports import environment from 'ember-metal/environment'; var jQuery; diff --git a/packages/ember-views/lib/views/collection_view.js b/packages/ember-views/lib/views/collection_view.js index 4061f447cbc..857492a694e 100644 --- a/packages/ember-views/lib/views/collection_view.js +++ b/packages/ember-views/lib/views/collection_view.js @@ -6,7 +6,10 @@ import Ember from 'ember-metal/core'; // Ember.assert import ContainerView from 'ember-views/views/container_view'; import View from 'ember-views/views/view'; -import EmberArray from 'ember-runtime/mixins/array'; +import EmberArray, { + addArrayObserver, + objectAt +} from 'ember-runtime/mixins/array'; import { get } from 'ember-metal/property_get'; import { set } from 'ember-metal/property_set'; import { fmt } from 'ember-runtime/system/string'; @@ -244,7 +247,7 @@ var CollectionView = ContainerView.extend(EmptyViewSupport, { if (content) { this._assertArrayLike(content); - content.addArrayObserver(this); + addArrayObserver(content, this); } var len = content ? get(content, 'length') : 0; @@ -326,7 +329,7 @@ var CollectionView = ContainerView.extend(EmptyViewSupport, { itemViewClass = readViewFactory(itemViewClass, this.container); for (idx = start; idx < start+added; idx++) { - item = content.objectAt(idx); + item = objectAt(content, idx); itemViewProps._context = this.keyword ? this.get('context') : item; itemViewProps.content = item; itemViewProps.contentIndex = idx; diff --git a/packages/ember-views/lib/views/select.js b/packages/ember-views/lib/views/select.js index 797b73307ab..f97cca76624 100644 --- a/packages/ember-views/lib/views/select.js +++ b/packages/ember-views/lib/views/select.js @@ -4,7 +4,7 @@ */ import Ember from 'ember-metal/core'; -import replace from 'ember-metal/replace'; +import { replace } from 'ember-runtime/system/native_array'; import { get } from 'ember-metal/property_get'; import { set } from 'ember-metal/property_set'; import View from 'ember-views/views/view'; @@ -14,11 +14,35 @@ import { computed } from 'ember-metal/computed'; import { A as emberA } from 'ember-runtime/system/native_array'; import { observer } from 'ember-metal/mixin'; import { defineProperty } from 'ember-metal/properties'; - +import { objectAt } from 'ember-runtime/mixins/array'; import htmlbarsTemplate from 'ember-htmlbars/templates/select'; import selectOptionDefaultTemplate from 'ember-htmlbars/templates/select-option'; import selectOptgroupDefaultTemplate from 'ember-htmlbars/templates/select-optgroup'; + +// TODO: extract +function find(obj, callback, target) { + if (obj.find) { return obj.find(callback, target); } + + if (!Array.isArray(obj)) { throw new TypeError('TODO: GOOD ERROR!!'); } + + var result = []; + for (let i = 0; i < obj.length; i++) { + let entry = obj[i]; + if (callback.call(target, entry, i, obj)) { + result.push(entry); + } + } + + return result; +} + + +// TODO: extract +function pushObject(array, object) { + replace(array, get(array, 'length'), 0, [object]); +} + var defaultTemplate = htmlbarsTemplate; var SelectOption = View.extend({ @@ -483,7 +507,7 @@ var Select = View.extend({ var label = get(item, groupPath); if (get(groupedContent, 'lastObject.label') !== label) { - groupedContent.pushObject({ + pushObject(groupedContent, { label: label, content: emberA() }); @@ -534,7 +558,7 @@ var Select = View.extend({ var selection; if (value !== selectedValue) { - selection = content ? content.find(function(obj) { + selection = content ? find(content, obj => { return value === (valuePath ? get(obj, valuePath) : obj); }) : null; @@ -566,7 +590,7 @@ var Select = View.extend({ } if (prompt) { selectedIndex -= 1; } - set(this, 'selection', content.objectAt(selectedIndex)); + set(this, 'selection', objectAt(content, selectedIndex)); }, _selectedIndex(value, defaultIndex = 0) { @@ -594,7 +618,10 @@ var Select = View.extend({ var selectedIndexes = options.map(function() { return this.index - offset; }); - var newSelection = content.objectsAt([].slice.call(selectedIndexes)); + + var newSelection = [].slice.call(selectedIndexes).map((index) => { + return objectAt(content, index); + }); if (isArray(selection)) { replace(selection, 0, get(selection, 'length'), newSelection); diff --git a/packages/ember-views/tests/views/select_test.js b/packages/ember-views/tests/views/select_test.js index 82fb6376edc..bf9022e0568 100644 --- a/packages/ember-views/tests/views/select_test.js +++ b/packages/ember-views/tests/views/select_test.js @@ -5,6 +5,9 @@ import EmberSelect from 'ember-views/views/select'; import jQuery from 'ember-views/system/jquery'; import EventDispatcher from 'ember-views/system/event_dispatcher'; import SafeString from 'htmlbars-util/safe-string'; +import { replace } from 'ember-runtime/system/native_array'; +import { get } from 'ember-metal/property_get'; +import { set } from 'ember-metal/property_set'; var trim = jQuery.trim; @@ -91,7 +94,7 @@ QUnit.test('should become disabled if the disabled attribute is changed', functi }); QUnit.test('can have options', function() { - select.set('content', Ember.A([1, 2, 3])); + select.set('content', [1, 2, 3]); append(); @@ -124,10 +127,10 @@ QUnit.test('select name is updated when setting name property of view', function }); QUnit.test('can specify the property path for an option\'s label and value', function() { - select.set('content', Ember.A([ + select.set('content', [ { id: 1, firstName: 'Yehuda' }, { id: 2, firstName: 'Tom' } - ])); + ]); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.id'); @@ -141,10 +144,10 @@ QUnit.test('can specify the property path for an option\'s label and value', fun }); QUnit.test('XSS: does not escape label value when it is a SafeString', function() { - select.set('content', Ember.A([ + select.set('content', [ { id: 1, firstName: new SafeString('

Yehuda

') }, { id: 2, firstName: new SafeString('

Tom

') } - ])); + ]); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.id'); @@ -160,10 +163,10 @@ QUnit.test('XSS: does not escape label value when it is a SafeString', function( }); QUnit.test('XSS: escapes label value content', function() { - select.set('content', Ember.A([ + select.set('content', [ { id: 1, firstName: '

Yehuda

' }, { id: 2, firstName: '

Tom

' } - ])); + ]); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.id'); @@ -182,7 +185,7 @@ QUnit.test('can retrieve the current selected option when multiple=false', funct var yehuda = { id: 1, firstName: 'Yehuda' }; var tom = { id: 2, firstName: 'Tom' }; - select.set('content', Ember.A([yehuda, tom])); + select.set('content', [yehuda, tom]); append(); @@ -200,7 +203,7 @@ QUnit.test('can retrieve the current selected options when multiple=true', funct var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; - select.set('content', Ember.A([yehuda, tom, david, brennain])); + select.set('content', [yehuda, tom, david, brennain]); select.set('multiple', true); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.firstName'); @@ -225,7 +228,7 @@ QUnit.test('selection can be set when multiple=false', function() { var tom = { id: 2, firstName: 'Tom' }; run(function() { - select.set('content', Ember.A([yehuda, tom])); + select.set('content', [yehuda, tom]); select.set('multiple', false); select.set('selection', tom); }); @@ -246,7 +249,7 @@ QUnit.test('selection can be set from a Promise when multiple=false', function() var tom = { id: 2, firstName: 'Tom' }; run(function() { - select.set('content', Ember.A([yehuda, tom])); + select.set('content', [yehuda, tom]); select.set('multiple', false); select.set('selection', Ember.RSVP.Promise.resolve(tom)); }); @@ -266,7 +269,7 @@ QUnit.test('selection from a Promise don\'t overwrite newer selection once resol QUnit.stop(); run(function() { - select.set('content', Ember.A([yehuda, tom, seb])); + select.set('content', [yehuda, tom, seb]); select.set('multiple', false); select.set('selection', new Ember.RSVP.Promise(function(resolve, reject) { Ember.run.later(function() { @@ -296,7 +299,7 @@ QUnit.test('selection from a Promise resolving to null should not select when mu var tom = { id: 2, firstName: 'Tom' }; run(function() { - select.set('content', Ember.A([yehuda, tom])); + select.set('content', [yehuda, tom]); select.set('multiple', false); select.set('selection', Ember.RSVP.Promise.resolve(null)); }); @@ -313,12 +316,12 @@ QUnit.test('selection can be set when multiple=true', function() { var brennain = { id: 4, firstName: 'Brennain' }; run(() => { - select.set('content', Ember.A([ + select.set('content', [ yehuda, tom, david, brennain - ])); + ]); select.set('multiple', true); select.set('selection', tom); }); @@ -339,12 +342,12 @@ QUnit.test('selection can be set when multiple=true and prompt', function() { var brennain = { id: 4, firstName: 'Brennain' }; run(() => { - select.set('content', Ember.A([ + select.set('content', [ yehuda, tom, david, brennain - ])); + ]); select.set('multiple', true); select.set('prompt', 'Pick one!'); select.set('selection', tom); @@ -368,26 +371,26 @@ QUnit.test('multiple selections can be set when multiple=true', function() { var brennain = { id: 4, firstName: 'Brennain' }; run(() => { - select.set('content', Ember.A([ + select.set('content', [ yehuda, tom, david, brennain - ])); + ]); select.set('optionLabelPath', 'content.firstName'); select.set('multiple', true); - select.set('selection', Ember.A([yehuda, david])); + select.set('selection', [yehuda, david]); }); append(); deepEqual(select.get('selection'), [yehuda, david], 'Initial selection should be correct'); - run(() => select.set('selection', Ember.A([ + run(() => select.set('selection', [ tom, brennain - ]))); + ])); deepEqual( select.$(':selected').map((index, element) => trim(jQuery(element).text())).toArray(), @@ -400,10 +403,10 @@ QUnit.test('multiple selections can be set by changing in place the selection ar var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; - var selection = Ember.A([yehuda, tom]); + var selection = [yehuda, tom]; run(function() { - select.set('content', Ember.A([yehuda, tom, david, brennain])); + select.set('content', [yehuda, tom, david, brennain]); select.set('optionLabelPath', 'content.firstName'); select.set('multiple', true); select.set('selection', selection); @@ -414,7 +417,9 @@ QUnit.test('multiple selections can be set by changing in place the selection ar deepEqual(select.get('selection'), [yehuda, tom], 'Initial selection should be correct'); run(function() { - selection.replace(0, selection.get('length'), Ember.A([david, brennain])); + // TODO: pushObject(array, obj); + // TODO: pushObjects(array, [obj]); + replace(selection, 0, selection.length, [david, brennain]); }); deepEqual( @@ -445,8 +450,8 @@ QUnit.test('multiple selections can be set indirectly via bindings and in-place }).create(); indirectContent.set('controller', EmberObject.create({ - content: Ember.A([tom, david, brennain]), - selection: Ember.A([david]) + content: [tom, david, brennain], + selection: [david] })); }); @@ -457,8 +462,8 @@ QUnit.test('multiple selections can be set indirectly via bindings and in-place deepEqual(select.get('selection'), [david], 'Initial selection should be correct'); run(function() { - indirectContent.set('controller.content', Ember.A([david, cyril])); - indirectContent.set('controller.selection', Ember.A([cyril])); + indirectContent.set('controller.content', [david, cyril]); + indirectContent.set('controller.selection', [cyril]); }); deepEqual(select.get('content'), [david, cyril], 'After updating bound content, content should be correct'); @@ -466,11 +471,11 @@ QUnit.test('multiple selections can be set indirectly via bindings and in-place }); QUnit.test('select with group can group options', function() { - var content = Ember.A([ + var content = [ { firstName: 'Yehuda', organization: 'Tilde' }, { firstName: 'Tom', organization: 'Tilde' }, { firstName: 'Keith', organization: 'Envato' } - ]); + ]; run(function() { select.set('content', content); @@ -493,11 +498,11 @@ QUnit.test('select with group can group options', function() { }); QUnit.test('select with group doesn\'t break options', function() { - var content = Ember.A([ + var content = [ { id: 1, firstName: 'Yehuda', organization: 'Tilde' }, { id: 2, firstName: 'Tom', organization: 'Tilde' }, { id: 3, firstName: 'Keith', organization: 'Envato' } - ]); + ]; run(function() { select.set('content', content); @@ -512,7 +517,7 @@ QUnit.test('select with group doesn\'t break options', function() { equal(trim(select.$().text()), 'YehudaTomKeith'); run(function() { - content.set('firstObject.firstName', 'Peter'); + set(content[0], 'firstName', 'Peter'); }); equal(select.$().text(), 'PeterTomKeith\n'); @@ -522,11 +527,11 @@ QUnit.test('select with group doesn\'t break options', function() { }); QUnit.test('select with group works for initial value', function() { - var content = Ember.A([ + var content = [ { id: 1, firstName: 'Yehuda', organization: 'Tilde' }, { id: 2, firstName: 'Tom', organization: 'Tilde' }, { id: 3, firstName: 'Keith', organization: 'Envato' } - ]); + ]; run(function() { select.set('content', content); @@ -542,9 +547,9 @@ QUnit.test('select with group works for initial value', function() { QUnit.test('select with group observes its content', function() { var wycats = { firstName: 'Yehuda', organization: 'Tilde' }; - var content = Ember.A([ + var content = [ wycats - ]); + ]; run(function() { select.set('content', content); @@ -555,7 +560,9 @@ QUnit.test('select with group observes its content', function() { append(); run(function() { - content.pushObject({ firstName: 'Keith', organization: 'Envato' }); + replace(content, get(content, 'length'), [ + { firstName: 'Keith', organization: 'Envato' } + ]); }); equal(select.$('optgroup').length, 2); @@ -590,10 +597,10 @@ QUnit.test('selection uses the same array when multiple=true', function() { var tom = { id: 2, firstName: 'Tom' }; var david = { id: 3, firstName: 'David' }; var brennain = { id: 4, firstName: 'Brennain' }; - var selection = Ember.A([yehuda, david]); + var selection = [yehuda, david]; run(function() { - select.set('content', Ember.A([yehuda, tom, david, brennain])); + select.set('content', [yehuda, tom, david, brennain]); select.set('multiple', true); select.set('optionLabelPath', 'content.firstName'); select.set('selection', selection); @@ -619,7 +626,7 @@ QUnit.test('Ember.SelectedOption knows when it is selected when multiple=false', var brennain = { id: 4, firstName: 'Brennain' }; run(function() { - select.set('content', Ember.A([yehuda, tom, david, brennain])); + select.set('content', [yehuda, tom, david, brennain]); select.set('multiple', false); select.set('selection', david); @@ -641,7 +648,7 @@ QUnit.test('Ember.SelectedOption knows when it is selected when multiple=true', var brennain = { id: 4, firstName: 'Brennain' }; run(function() { - select.set('content', Ember.A([yehuda, tom, david, brennain])); + select.set('content', [yehuda, tom, david, brennain]); select.set('multiple', true); select.set('selection', [yehuda, david]); @@ -660,7 +667,7 @@ QUnit.test('Ember.SelectedOption knows when it is selected when multiple=true', QUnit.test('Ember.SelectedOption knows when it is selected when multiple=true and options are primitives', function() { run(function() { - select.set('content', Ember.A([1, 2, 3, 4])); + select.set('content', [1, 2, 3, 4]); select.set('multiple', true); select.set('selection', [1, 3]); }); @@ -679,7 +686,7 @@ QUnit.test('a prompt can be specified', function() { var tom = { id: 2, firstName: 'Tom' }; run(function() { - select.set('content', Ember.A([yehuda, tom])); + select.set('content', [yehuda, tom]); select.set('prompt', 'Pick a person'); select.set('optionLabelPath', 'content.firstName'); select.set('optionValuePath', 'content.id'); @@ -736,7 +743,7 @@ QUnit.test('valueBinding handles 0 as initiated value (issue #2763)', function() select.destroy(); // Destroy the existing select select = EmberSelect.extend({ - content: Ember.A([1,0]), + content: [1,0], indirectData: indirectData, valueBinding: 'indirectData.value' }).create(); @@ -752,7 +759,7 @@ QUnit.test('valueBinding handles 0 as initiated value (issue #2763)', function() QUnit.test('should be able to select an option and then reselect the prompt', function() { run(function() { - select.set('content', Ember.A(['one', 'two', 'three'])); + select.set('content', ['one', 'two', 'three']); select.set('prompt', 'Select something'); }); @@ -770,12 +777,12 @@ QUnit.test('should be able to select an option and then reselect the prompt', fu QUnit.test('should be able to get the current selection\'s value', function() { run(function() { - select.set('content', Ember.A([ + select.set('content', [ { label: 'Yehuda Katz', value: 'wycats' }, { label: 'Tom Dale', value: 'tomdale' }, { label: 'Peter Wagenet', value: 'wagenet' }, { label: 'Erik Bryn', value: 'ebryn' } - ])); + ]); select.set('optionLabelPath', 'content.label'); select.set('optionValuePath', 'content.value'); }); @@ -789,12 +796,12 @@ QUnit.test('should be able to set the current selection by value', function() { var ebryn = { label: 'Erik Bryn', value: 'ebryn' }; run(function() { - select.set('content', Ember.A([ + select.set('content', [ { label: 'Yehuda Katz', value: 'wycats' }, { label: 'Tom Dale', value: 'tomdale' }, { label: 'Peter Wagenet', value: 'wagenet' }, ebryn - ])); + ]); select.set('optionLabelPath', 'content.label'); select.set('optionValuePath', 'content.value'); select.set('value', 'ebryn'); diff --git a/packages/ember-views/tests/views/view/destroy_element_test.js b/packages/ember-views/tests/views/view/destroy_element_test.js index 546346b7b89..e73d8b9d180 100644 --- a/packages/ember-views/tests/views/view/destroy_element_test.js +++ b/packages/ember-views/tests/views/view/destroy_element_test.js @@ -2,6 +2,7 @@ import { get } from 'ember-metal/property_get'; import run from 'ember-metal/run_loop'; import EmberView from 'ember-views/views/view'; import ContainerView from 'ember-views/views/container_view'; +import { objectAt } from 'ember-runtime/mixins/array'; var view; @@ -57,7 +58,7 @@ QUnit.test('if it has a element, calls willDestroyElement on receiver and child equal(parentCount, 1, 'invoked destroy element on the parent'); equal(childCount, 1, 'invoked destroy element on the child'); ok(!get(view, 'element'), 'view no longer has element'); - ok(!get(get(view, 'childViews').objectAt(0), 'element'), 'child no longer has an element'); + ok(!get(objectAt(get(view, 'childViews'), 0), 'element'), 'child no longer has an element'); }); QUnit.test('returns receiver', function() { diff --git a/packages/ember-views/tests/views/view/element_test.js b/packages/ember-views/tests/views/view/element_test.js index 239d61315ef..6f8104dd947 100644 --- a/packages/ember-views/tests/views/view/element_test.js +++ b/packages/ember-views/tests/views/view/element_test.js @@ -6,6 +6,7 @@ import run from 'ember-metal/run_loop'; import EmberView from 'ember-views/views/view'; import ContainerView from 'ember-views/views/container_view'; +import { objectAt } from 'ember-runtime/mixins/array'; var parentView, view; @@ -30,7 +31,7 @@ QUnit.test('returns null if the view has no element and parent view has no eleme parentView = ContainerView.create({ childViews: [EmberView.extend()] }); - view = get(parentView, 'childViews').objectAt(0); + view = objectAt(get(parentView, 'childViews'), 0); equal(get(view, 'parentView'), parentView, 'precond - has parent view'); equal(get(parentView, 'element'), null, 'parentView has no element'); diff --git a/packages/ember-views/tests/views/view/is_visible_test.js b/packages/ember-views/tests/views/view/is_visible_test.js index 08eaf29a3e0..9c859f13e26 100644 --- a/packages/ember-views/tests/views/view/is_visible_test.js +++ b/packages/ember-views/tests/views/view/is_visible_test.js @@ -5,6 +5,7 @@ import run from 'ember-metal/run_loop'; import { computed } from 'ember-metal/computed'; import EmberView from 'ember-views/views/view'; import ContainerView from 'ember-views/views/container_view'; +import { objectAt } from 'ember-runtime/mixins/array'; var View, view, parentBecameVisible, childBecameVisible, grandchildBecameVisible; var parentBecameHidden, childBecameHidden, grandchildBecameHidden; @@ -238,7 +239,7 @@ QUnit.test('view should be notified after isVisible is set to true and the eleme QUnit.test('if a view descends from a hidden view, making isVisible true should not trigger becameVisible', function() { view = View.create({ isVisible: true }); - var childView = view.get('childViews').objectAt(0); + var childView = objectAt(view.get('childViews'), 0); run(function() { view.append(); @@ -267,7 +268,7 @@ QUnit.test('if a view descends from a hidden view, making isVisible true should QUnit.test('if a child view becomes visible while its parent is hidden, if its parent later becomes visible, it receives a becameVisible callback', function() { view = View.create({ isVisible: false }); - var childView = view.get('childViews').objectAt(0); + var childView = objectAt(view.get('childViews'), 0); run(function() { view.append(); diff --git a/packages/ember-views/tests/views/view/remove_test.js b/packages/ember-views/tests/views/view/remove_test.js index 4da8a03d3bc..5527c63f0ad 100644 --- a/packages/ember-views/tests/views/view/remove_test.js +++ b/packages/ember-views/tests/views/view/remove_test.js @@ -3,6 +3,7 @@ import run from 'ember-metal/run_loop'; import jQuery from 'ember-views/system/jquery'; import View from 'ember-views/views/view'; import ContainerView from 'ember-views/views/container_view'; +import { objectAt } from 'ember-runtime/mixins/array'; // ....................................................... // removeChild() @@ -14,7 +15,7 @@ QUnit.module('View#removeChild', { expectDeprecation('Setting `childViews` on a Container is deprecated.'); parentView = ContainerView.create({ childViews: [View] }); - child = get(parentView, 'childViews').objectAt(0); + child = objectAt(get(parentView, 'childViews'), 0); }, teardown() { run(function() { @@ -89,7 +90,7 @@ QUnit.test('removes view from parent view', function() { expectDeprecation('Setting `childViews` on a Container is deprecated.'); parentView = ContainerView.create({ childViews: [View] }); - child = get(parentView, 'childViews').objectAt(0); + child = objectAt(get(parentView, 'childViews'), 0); ok(get(child, 'parentView'), 'precond - has parentView'); run(function() { @@ -111,7 +112,7 @@ QUnit.test('returns receiver', function() { expectDeprecation('Setting `childViews` on a Container is deprecated.'); parentView = ContainerView.create({ childViews: [View] }); - child = get(parentView, 'childViews').objectAt(0); + child = objectAt(get(parentView, 'childViews'), 0); var removed = run(function() { return child.removeFromParent(); });