ember-source 1.0.0.rc8 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of ember-source might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,15 +1,7 @@
1
1
  ---
2
- !binary "U0hBMQ==":
3
- metadata.gz: !binary |-
4
- ZTk3ZmI1MjQyM2JhNjM3ZTlkNTE1ZmNjNzJmMDA5MjMzZDQ3MjBlNA==
5
- data.tar.gz: !binary |-
6
- MjYzODg3ZDZjNGVlZTZmMWY2MWZhZGQ2NzRjY2YyYjVjMjg3OTU0Mg==
7
- !binary "U0hBNTEy":
8
- metadata.gz: !binary |-
9
- MjhmOTMzMzY4N2ExNDFmZjdjNDY3YjY4YjRkOTc4ZjJmOTYxNmM1ZjVhNzMw
10
- NGI0MzMzNDUzOGE1ZjVhZTE4ZGE4OGMxNTlkNmY2Nzk1N2IwOGUxMDNhNDM5
11
- YjUwZTdmZWRhYzNmMWMwNjRkM2Q0MTdkM2UxMjRiMDliOTgxMWY=
12
- data.tar.gz: !binary |-
13
- ZDBlZDA1NDE0YmJmMTM0YmU0ZDUyNDgyNzNlYWVlZDZlOTE2YTliYzNjOTQx
14
- ZGIxNjUzNzFkZTg4ODhkZTRlYTVmZDAxYzM2Mjg3NDVhMmFhOWY0OWM1NDA2
15
- ZjM3MWFhMTM0MzdjMDM2ZGEzMGM0NmQ2ZjBjMWFkNDg1YjViYTk=
2
+ SHA1:
3
+ metadata.gz: 940d700becd9b7df6c663da37eee9f66639f64d2
4
+ data.tar.gz: ee29f542f023254df299db55f8c1afde45a4498a
5
+ SHA512:
6
+ metadata.gz: 8e53c6da026a631d44262eb78338870274de95a3ff147b55c15c03943f406c181e925b1c235936a90d130acd82aaff710c246655814b100c939fc8c4771e0a53
7
+ data.tar.gz: 289304acc4db08ad5d3206ca3316d9a8c69c53c347e1eb1c332935cc79fd7aaa7d6dc9c52b146e7c8755c5fb0d3af7e72ec8df4150aeabb2835050594586618c
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0-rc.8
1
+ 1.0.0
@@ -1,5 +1,5 @@
1
- // Version: v1.0.0-rc.7-148-g694beed
2
- // Last commit: 694beed (2013-08-26 17:12:34 -0400)
1
+ // Version: v1.0.0
2
+ // Last commit: e2ea0cf (2013-08-31 23:47:39 -0700)
3
3
 
4
4
 
5
5
  (function() {
@@ -156,10 +156,22 @@ Ember.deprecateFunc = function(message, func) {
156
156
  };
157
157
  };
158
158
 
159
+
160
+ // Inform the developer about the Ember Inspector if not installed.
161
+ if (!Ember.testing) {
162
+ if (typeof window !== 'undefined' && window.chrome && window.addEventListener) {
163
+ window.addEventListener("load", function() {
164
+ if (document.body && document.body.dataset && !document.body.dataset.emberExtension) {
165
+ Ember.debug('For more advanced debugging, install the Ember Inspector from https://chrome.google.com/webstore/detail/ember-inspector/bmdblncegkenkacieihfhpjfppoconhi');
166
+ }
167
+ }, false);
168
+ }
169
+ }
170
+
159
171
  })();
160
172
 
161
- // Version: v1.0.0-rc.8-3-gf0c90f8
162
- // Last commit: f0c90f8 (2013-08-29 00:12:09 -0700)
173
+ // Version: v1.0.0
174
+ // Last commit: e2ea0cf (2013-08-31 23:47:39 -0700)
163
175
 
164
176
 
165
177
  (function() {
@@ -225,7 +237,7 @@ var define, requireModule;
225
237
 
226
238
  @class Ember
227
239
  @static
228
- @version 1.0.0-rc.8
240
+ @version 1.0.0
229
241
  */
230
242
 
231
243
  if ('undefined' === typeof Ember) {
@@ -252,10 +264,10 @@ Ember.toString = function() { return "Ember"; };
252
264
  /**
253
265
  @property VERSION
254
266
  @type String
255
- @default '1.0.0-rc.8'
267
+ @default '1.0.0'
256
268
  @final
257
269
  */
258
- Ember.VERSION = '1.0.0-rc.8';
270
+ Ember.VERSION = '1.0.0';
259
271
 
260
272
  /**
261
273
  Standard environmental variables. You can define these in a global `ENV`
@@ -4601,8 +4613,8 @@ function registerComputedWithProperties(name, macro) {
4601
4613
  property is null, an empty string, empty array, or empty function.
4602
4614
 
4603
4615
  Note: When using `Ember.computed.empty` to watch an array make sure to
4604
- use the `array.length` or `array.@each` syntax so the computed can
4605
- subscribe to transitions from empty to non-empty states.
4616
+ use the `array.length` syntax so the computed can subscribe to transitions
4617
+ from empty to non-empty states.
4606
4618
 
4607
4619
  Example
4608
4620
 
@@ -4610,9 +4622,9 @@ function registerComputedWithProperties(name, macro) {
4610
4622
  var ToDoList = Ember.Object.extend({
4611
4623
  done: Ember.computed.empty('todos.length')
4612
4624
  });
4613
- var todoList = ToDoList.create({todoList: ['Unit Test', 'Documentation', 'Release']});
4625
+ var todoList = ToDoList.create({todos: ['Unit Test', 'Documentation', 'Release']});
4614
4626
  todoList.get('done'); // false
4615
- todoList.get('todoList').clear(); // []
4627
+ todoList.get('todos').clear(); // []
4616
4628
  todoList.get('done'); // true
4617
4629
  ```
4618
4630
 
@@ -5714,7 +5726,7 @@ define("backburner",
5714
5726
  clearTimeout(debouncee[2]);
5715
5727
  }
5716
5728
 
5717
- var timer = window.setTimeout(function() {
5729
+ var timer = global.setTimeout(function() {
5718
5730
  if (!immediate) {
5719
5731
  self.run.apply(self, args);
5720
5732
  }
@@ -5783,8 +5795,8 @@ define("backburner",
5783
5795
  function createAutorun(backburner) {
5784
5796
  backburner.begin();
5785
5797
  autorun = global.setTimeout(function() {
5786
- backburner.end();
5787
5798
  autorun = null;
5799
+ backburner.end();
5788
5800
  });
5789
5801
  }
5790
5802
 
@@ -7225,19 +7237,19 @@ Ember.mixin = function(obj) {
7225
7237
  `Ember.Mixin.extend`.
7226
7238
 
7227
7239
  Note that mixins extend a constructor's prototype so arrays and object literals
7228
- defined as properties will be shared amongst objects that implement the mixin.
7240
+ defined as properties will be shared amongst objects that implement the mixin.
7229
7241
  If you want to define an property in a mixin that is not shared, you can define
7230
7242
  it either as a computed property or have it be created on initialization of the object.
7231
-
7243
+
7232
7244
  ```javascript
7233
7245
  //filters array will be shared amongst any object implementing mixin
7234
7246
  App.Filterable = Ember.Mixin.create({
7235
- filters: Ember.A()
7247
+ filters: Ember.A()
7236
7248
  });
7237
7249
 
7238
7250
  //filters will be a separate array for every object implementing the mixin
7239
7251
  App.Filterable = Ember.Mixin.create({
7240
- filters: Ember.computed(function(){return Ember.A();})
7252
+ filters: Ember.computed(function(){return Ember.A();})
7241
7253
  });
7242
7254
 
7243
7255
  //filters will be created as a separate array during the object's initialization
@@ -7484,6 +7496,22 @@ Ember.aliasMethod = function(methodName) {
7484
7496
  //
7485
7497
 
7486
7498
  /**
7499
+ Specify a method that observes property changes.
7500
+
7501
+ ```javascript
7502
+ Ember.Object.extend({
7503
+ valueObserver: Ember.observer(function() {
7504
+ // Executes whenever the "value" property changes
7505
+ }, 'value')
7506
+ });
7507
+ ```
7508
+
7509
+ In the future this method may become asynchronous. If you want to ensure
7510
+ synchronous behavior, use `immediateObserver`.
7511
+
7512
+ Also available as `Function.prototype.observes` if prototype extensions are
7513
+ enabled.
7514
+
7487
7515
  @method observer
7488
7516
  @for Ember
7489
7517
  @param {Function} func
@@ -7496,9 +7524,23 @@ Ember.observer = function(func) {
7496
7524
  return func;
7497
7525
  };
7498
7526
 
7499
- // If observers ever become asynchronous, Ember.immediateObserver
7500
- // must remain synchronous.
7501
7527
  /**
7528
+ Specify a method that observes property changes.
7529
+
7530
+ ```javascript
7531
+ Ember.Object.extend({
7532
+ valueObserver: Ember.immediateObserver(function() {
7533
+ // Executes whenever the "value" property changes
7534
+ }, 'value')
7535
+ });
7536
+ ```
7537
+
7538
+ In the future, `Ember.observer` may become asynchronous. In this event,
7539
+ `Ember.immediateObserver` will maintain the synchronous behavior.
7540
+
7541
+ Also available as `Function.prototype.observesImmediately` if prototype extensions are
7542
+ enabled.
7543
+
7502
7544
  @method immediateObserver
7503
7545
  @for Ember
7504
7546
  @param {Function} func
@@ -7526,24 +7568,31 @@ Ember.immediateObserver = function() {
7526
7568
 
7527
7569
  ```javascript
7528
7570
  App.PersonView = Ember.View.extend({
7571
+
7529
7572
  friends: [{ name: 'Tom' }, { name: 'Stefan' }, { name: 'Kris' }],
7530
- valueWillChange: function (obj, keyName) {
7573
+
7574
+ valueWillChange: Ember.beforeObserver(function(obj, keyName) {
7531
7575
  this.changingFrom = obj.get(keyName);
7532
- }.observesBefore('content.value'),
7533
- valueDidChange: function(obj, keyName) {
7576
+ }, 'content.value'),
7577
+
7578
+ valueDidChange: Ember.observer(function(obj, keyName) {
7534
7579
  // only run if updating a value already in the DOM
7535
7580
  if (this.get('state') === 'inDOM') {
7536
- var color = obj.get(keyName) > this.changingFrom ? 'green' : 'red';
7537
- // logic
7581
+ var color = obj.get(keyName) > this.changingFrom ? 'green' : 'red';
7582
+ // logic
7538
7583
  }
7539
- }.observes('content.value'),
7540
- friendsDidChange: function(obj, keyName) {
7584
+ }, 'content.value'),
7585
+
7586
+ friendsDidChange: Ember.observer(function(obj, keyName) {
7541
7587
  // some logic
7542
7588
  // obj.get(keyName) returns friends array
7543
- }.observes('friends.@each.name')
7589
+ }, 'friends.@each.name')
7544
7590
  });
7545
7591
  ```
7546
7592
 
7593
+ Also available as `Function.prototype.observesBefore` if prototype extensions are
7594
+ enabled.
7595
+
7547
7596
  @method beforeObserver
7548
7597
  @for Ember
7549
7598
  @param {Function} func
@@ -7627,6 +7676,7 @@ define("rsvp/async",
7627
7676
  var browserGlobal = (typeof window !== 'undefined') ? window : {};
7628
7677
  var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
7629
7678
  var async;
7679
+ var local = (typeof global !== 'undefined') ? global : this;
7630
7680
 
7631
7681
  // old node
7632
7682
  function useNextTick() {
@@ -7677,7 +7727,7 @@ define("rsvp/async",
7677
7727
 
7678
7728
  function useSetTimeout() {
7679
7729
  return function(callback, arg) {
7680
- global.setTimeout(function() {
7730
+ local.setTimeout(function() {
7681
7731
  callback(arg);
7682
7732
  }, 1);
7683
7733
  };
@@ -8216,6 +8266,13 @@ define("rsvp",
8216
8266
  })();
8217
8267
 
8218
8268
  (function() {
8269
+ /**
8270
+ Flag to enable/disable model factory injections (disabled by default)
8271
+ If model factory injections are enabled, models should not be
8272
+ accessed globally (only through `container.lookupFactory('model:modelName'))`);
8273
+ */
8274
+ Ember.MODEL_FACTORY_INJECTIONS = false || !!Ember.ENV.MODEL_FACTORY_INJECTIONS;
8275
+
8219
8276
  define("container",
8220
8277
  [],
8221
8278
  function() {
@@ -8494,6 +8551,7 @@ define("container",
8494
8551
 
8495
8552
  container.unregister('model:user')
8496
8553
  container.lookup('model:user') === undefined //=> true
8554
+ ```
8497
8555
 
8498
8556
  @method unregister
8499
8557
  @param {String} fullName
@@ -8577,7 +8635,7 @@ define("container",
8577
8635
  */
8578
8636
  makeToString: function(factory, fullName) {
8579
8637
  return factory.toString();
8580
- },
8638
+ },
8581
8639
 
8582
8640
  /**
8583
8641
  Given a fullName return a corresponding instance.
@@ -8985,6 +9043,7 @@ define("container",
8985
9043
  var factory = container.resolve(name);
8986
9044
  var injectedFactory;
8987
9045
  var cache = container.factoryCache;
9046
+ var type = fullName.split(":")[0];
8988
9047
 
8989
9048
  if (!factory) { return; }
8990
9049
 
@@ -8992,7 +9051,7 @@ define("container",
8992
9051
  return cache.get(fullName);
8993
9052
  }
8994
9053
 
8995
- if (typeof factory.extend !== 'function') {
9054
+ if (typeof factory.extend !== 'function' || (!Ember.MODEL_FACTORY_INJECTIONS && type === 'model')) {
8996
9055
  // TODO: think about a 'safe' merge style extension
8997
9056
  // for now just fallback to create time injection
8998
9057
  return factory;
@@ -10796,6 +10855,9 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
10796
10855
  return an enumerable that maps automatically to the named key on the
10797
10856
  member objects.
10798
10857
 
10858
+ If you merely want to watch for any items being added or removed to the array,
10859
+ use the `[]` property instead of `@each`.
10860
+
10799
10861
  @property @each
10800
10862
  */
10801
10863
  '@each': Ember.computed(function() {
@@ -10827,7 +10889,7 @@ var get = Ember.get,
10827
10889
  eachPropertyPattern = /^(.*)\.@each\.(.*)/,
10828
10890
  doubleEachPropertyPattern = /(.*\.@each){2,}/;
10829
10891
 
10830
- /**
10892
+ /*
10831
10893
  Tracks changes to dependent arrays, as well as to properties of items in
10832
10894
  dependent arrays.
10833
10895
 
@@ -11016,22 +11078,29 @@ DependentArraysObserver.prototype = {
11016
11078
  guid = guidFor(dependentArray),
11017
11079
  dependentKey = this.dependentKeysByGuid[guid],
11018
11080
  itemPropertyKeys = this.cp._itemPropertyKeys[dependentKey] || [],
11081
+ item,
11019
11082
  itemIndex,
11083
+ sliceIndex,
11020
11084
  observerContexts;
11021
11085
 
11022
11086
  observerContexts = this.removeTransformation(dependentKey, index, removedCount);
11023
- forEach(dependentArray.slice(index, index + removedCount), function (item, sliceIndex) {
11087
+
11088
+
11089
+ function removeObservers(propertyKey) {
11090
+ removeBeforeObserver(item, propertyKey, this, observerContexts[sliceIndex].beforeObserver);
11091
+ removeObserver(item, propertyKey, this, observerContexts[sliceIndex].observer);
11092
+ }
11093
+
11094
+ for (sliceIndex = removedCount - 1; sliceIndex >= 0; --sliceIndex) {
11024
11095
  itemIndex = index + sliceIndex;
11096
+ item = dependentArray.objectAt(itemIndex);
11025
11097
 
11026
- forEach(itemPropertyKeys, function (propertyKey) {
11027
- removeBeforeObserver(item, propertyKey, this, observerContexts[sliceIndex].beforeObserver);
11028
- removeObserver(item, propertyKey, this, observerContexts[sliceIndex].observer);
11029
- }, this);
11098
+ forEach(itemPropertyKeys, removeObservers, this);
11030
11099
 
11031
11100
  changeMeta = createChangeMeta(dependentArray, item, itemIndex, this.instanceMeta.propertyName, this.cp);
11032
11101
  this.setValue( removedItem.call(
11033
11102
  this.instanceMeta.context, this.getValue(), item, changeMeta, this.instanceMeta.sugarMeta));
11034
- }, this);
11103
+ }
11035
11104
  },
11036
11105
 
11037
11106
  dependentArrayDidChange: function (dependentArray, index, removedCount, addedCount) {
@@ -11045,8 +11114,8 @@ DependentArraysObserver.prototype = {
11045
11114
 
11046
11115
  forEach(dependentArray.slice(index, index + addedCount), function (item, sliceIndex) {
11047
11116
  if (itemPropertyKeys) {
11048
- observerContext =
11049
- observerContexts[sliceIndex] =
11117
+ observerContext =
11118
+ observerContexts[sliceIndex] =
11050
11119
  this.createPropertyObserverContext(dependentArray, index + sliceIndex, this.trackedArraysByGuid[dependentKey]);
11051
11120
  forEach(itemPropertyKeys, function (propertyKey) {
11052
11121
  addBeforeObserver(item, propertyKey, this, observerContext.beforeObserver);
@@ -11063,30 +11132,29 @@ DependentArraysObserver.prototype = {
11063
11132
  },
11064
11133
 
11065
11134
  itemPropertyWillChange: function (obj, keyName, array, index) {
11066
- var changeId = guidFor(obj)+":"+keyName;
11135
+ var guid = guidFor(obj);
11067
11136
 
11068
- if (!this.changedItems[changeId]) {
11069
- this.changedItems[changeId] = {
11070
- array: array,
11137
+ if (!this.changedItems[guid]) {
11138
+ this.changedItems[guid] = {
11139
+ array: array,
11071
11140
  index: index,
11072
11141
  obj: obj,
11073
- keyChanged: keyName,
11074
- previousValue: get(obj, keyName)
11142
+ previousValues: {}
11075
11143
  };
11076
11144
  }
11145
+
11146
+ this.changedItems[guid].previousValues[keyName] = get(obj, keyName);
11077
11147
  },
11078
11148
 
11079
11149
  itemPropertyDidChange: function(obj, keyName, array, index) {
11080
11150
  Ember.run.once(this, 'flushChanges');
11081
11151
  },
11082
11152
 
11083
- // TODO: it probably makes more sense to remove the item during `willChange`
11084
- // and add it back (with the new value) during `didChange`
11085
11153
  flushChanges: function() {
11086
11154
  var changedItems = this.changedItems, key, c, changeMeta;
11087
11155
  for (key in changedItems) {
11088
11156
  c = changedItems[key];
11089
- changeMeta = createChangeMeta(c.array, c.obj, c.index, this.instanceMeta.propertyName, this.cp, c.keyChanged, c.previousValue);
11157
+ changeMeta = createChangeMeta(c.array, c.obj, c.index, this.instanceMeta.propertyName, this.cp, c.previousValues);
11090
11158
  this.setValue(
11091
11159
  this.callbacks.removedItem.call(this.instanceMeta.context, this.getValue(), c.obj, changeMeta, this.instanceMeta.sugarMeta));
11092
11160
  this.setValue(
@@ -11096,18 +11164,19 @@ DependentArraysObserver.prototype = {
11096
11164
  }
11097
11165
  };
11098
11166
 
11099
- function createChangeMeta(dependentArray, item, index, propertyName, property, key, previousValue) {
11167
+ function createChangeMeta(dependentArray, item, index, propertyName, property, previousValues) {
11100
11168
  var meta = {
11101
11169
  arrayChanged: dependentArray,
11102
11170
  index: index,
11103
11171
  item: item,
11104
11172
  propertyName: propertyName,
11105
- property: property,
11106
- // previous value is only available for item property changes!
11107
- previousValue: previousValue
11173
+ property: property
11108
11174
  };
11109
11175
 
11110
- if (key) { meta.keyChanged = key; }
11176
+ if (previousValues) {
11177
+ // previous values only available for item property changes
11178
+ meta.previousValues = previousValues;
11179
+ }
11111
11180
 
11112
11181
  return meta;
11113
11182
  }
@@ -11231,7 +11300,9 @@ function ReduceComputedProperty(options) {
11231
11300
 
11232
11301
  forEach(cp._dependentKeys, function(dependentKey) {
11233
11302
  var dependentArray = get(this, dependentKey);
11234
- addItems.call(this, dependentArray, callbacks, cp, propertyName, meta);
11303
+ if (dependentArray) {
11304
+ addItems.call(this, dependentArray, callbacks, cp, propertyName, meta);
11305
+ }
11235
11306
  }, this);
11236
11307
  };
11237
11308
 
@@ -11333,7 +11404,122 @@ ReduceComputedProperty.prototype.property = function () {
11333
11404
  return ComputedProperty.prototype.property.apply(this, propertyArgs);
11334
11405
  };
11335
11406
 
11407
+ /**
11408
+ Creates a computed property which operates on dependent arrays and
11409
+ is updated with "one at a time" semantics. When items are added or
11410
+ removed from the dependent array(s) a reduce computed only operates
11411
+ on the change instead of re-evaluating the entire array.
11412
+
11413
+ If there are more than one arguments the first arguments are
11414
+ considered to be dependent property keys. The last argument is
11415
+ required to be an options object. The options object can have the
11416
+ following four properties.
11417
+
11418
+ `initialValue` - A value or function that will be used as the initial
11419
+ value for the computed. If this property is a function the result of calling
11420
+ the function will be used as the initial value. This property is required.
11421
+
11422
+ `initialize` - An optional initialize function. Typically this will be used
11423
+ to set up state on the instanceMeta object.
11424
+
11425
+ `removedItem` - A function that is called each time an element is removed
11426
+ from the array.
11427
+
11428
+ `addedItem` - A function that is called each time an element is added to
11429
+ the array.
11430
+
11431
+
11432
+ The `initialize` function has the following signature:
11433
+
11434
+ ```javascript
11435
+ function (initialValue, changeMeta, instanceMeta)
11436
+ ```
11437
+
11438
+ `initialValue` - The value of the `initialValue` property from the
11439
+ options object.
11440
+
11441
+ `changeMeta` - An object which contains meta information about the
11442
+ computed. It contains the following properties:
11443
+
11444
+ - `property` the computed property
11445
+ - `propertyName` the name of the property on the object
11446
+
11447
+ `instanceMeta` - An object that can be used to store meta
11448
+ information needed for calculating your computed. For example a
11449
+ unique computed might use this to store the number of times a given
11450
+ element is found in the dependent array.
11451
+
11452
+
11453
+ The `removedItem` and `addedItem` functions both have the following signature:
11454
+
11455
+ ```javascript
11456
+ function (accumulatedValue, item, changeMeta, instanceMeta)
11457
+ ```
11458
+
11459
+ `accumulatedValue` - The value returned from the last time
11460
+ `removedItem` or `addedItem` was called or `initialValue`.
11461
+
11462
+ `item` - the element added or removed from the array
11463
+
11464
+ `changeMeta` - An object which contains meta information about the
11465
+ change. It contains the following properties:
11466
+
11467
+ - `property` the computed property
11468
+ - `propertyName` the name of the property on the object
11469
+ - `index` the index of the added or removed item
11470
+ - `item` the added or removed item: this is exactly the same as
11471
+ the second arg
11472
+ - `arrayChanged` the array that triggered the change. Can be
11473
+ useful when depending on multiple arrays.
11474
+
11475
+ For property changes triggered on an item property change (when
11476
+ depKey is something like `someArray.@each.someProperty`),
11477
+ `changeMeta` will also contain the following property:
11478
+
11479
+ - `previousValues` an object whose keys are the properties that changed on
11480
+ the item, and whose values are the item's previous values.
11481
+
11482
+ `previousValues` is important Ember coalesces item property changes via
11483
+ Ember.run.once. This means that by the time removedItem gets called, item has
11484
+ the new values, but you may need the previous value (eg for sorting &
11485
+ filtering).
11336
11486
 
11487
+ `instanceMeta` - An object that can be used to store meta
11488
+ information needed for calculating your computed. For example a
11489
+ unique computed might use this to store the number of times a given
11490
+ element is found in the dependent array.
11491
+
11492
+ The `removedItem` and `addedItem` functions should return the accumulated
11493
+ value. It is acceptable to not return anything (ie return undefined)
11494
+ to invalidate the computation. This is generally not a good idea for
11495
+ arrayComputed but it's used in eg max and min.
11496
+
11497
+ Example
11498
+
11499
+ ```javascript
11500
+ Ember.computed.max = function (dependentKey) {
11501
+ return Ember.reduceComputed.call(null, dependentKey, {
11502
+ initialValue: -Infinity,
11503
+
11504
+ addedItem: function (accumulatedValue, item, changeMeta, instanceMeta) {
11505
+ return Math.max(accumulatedValue, item);
11506
+ },
11507
+
11508
+ removedItem: function (accumulatedValue, item, changeMeta, instanceMeta) {
11509
+ if (item < accumulatedValue) {
11510
+ return accumulatedValue;
11511
+ }
11512
+ }
11513
+ });
11514
+ };
11515
+ ```
11516
+
11517
+ @method reduceComputed
11518
+ @for Ember
11519
+ @param {String} [dependentKeys*]
11520
+ @param {Object} options
11521
+ @returns {Ember.ComputedProperty}
11522
+ */
11337
11523
  Ember.reduceComputed = function (options) {
11338
11524
  var args;
11339
11525
 
@@ -11359,7 +11545,6 @@ Ember.reduceComputed = function (options) {
11359
11545
  return cp;
11360
11546
  };
11361
11547
 
11362
-
11363
11548
  })();
11364
11549
 
11365
11550
 
@@ -11404,7 +11589,120 @@ ArrayComputedProperty.prototype.resetValue = function (array) {
11404
11589
  return array;
11405
11590
  };
11406
11591
 
11592
+ /**
11593
+ Creates a computed property which operates on dependent arrays and
11594
+ is updated with "one at a time" semantics. When items are added or
11595
+ removed from the dependent array(s) an array computed only operates
11596
+ on the change instead of re-evaluating the entire array. This should
11597
+ return an array, if you'd like to use "one at a time" semantics and
11598
+ compute some value other then an array look at
11599
+ `Ember.reduceComputed`.
11600
+
11601
+ If there are more than one arguments the first arguments are
11602
+ considered to be dependent property keys. The last argument is
11603
+ required to be an options object. The options object can have the
11604
+ following three properties.
11605
+
11606
+ `initialize` - An optional initialize function. Typically this will be used
11607
+ to set up state on the instanceMeta object.
11608
+
11609
+ `removedItem` - A function that is called each time an element is
11610
+ removed from the array.
11611
+
11612
+ `addedItem` - A function that is called each time an element is
11613
+ added to the array.
11614
+
11615
+
11616
+ The `initialize` function has the following signature:
11617
+
11618
+ ```javascript
11619
+ function (array, changeMeta, instanceMeta)
11620
+ ```
11621
+
11622
+ `array` - The initial value of the arrayComputed, an empty array.
11623
+
11624
+ `changeMeta` - An object which contains meta information about the
11625
+ computed. It contains the following properties:
11626
+
11627
+ - `property` the computed property
11628
+ - `propertyName` the name of the property on the object
11629
+
11630
+ `instanceMeta` - An object that can be used to store meta
11631
+ information needed for calculating your computed. For example a
11632
+ unique computed might use this to store the number of times a given
11633
+ element is found in the dependent array.
11634
+
11635
+
11636
+ The `removedItem` and `addedItem` functions both have the following signature:
11637
+
11638
+ ```javascript
11639
+ function (accumulatedValue, item, changeMeta, instanceMeta)
11640
+ ```
11641
+
11642
+ `accumulatedValue` - The value returned from the last time
11643
+ `removedItem` or `addedItem` was called or an empty array.
11644
+
11645
+ `item` - the element added or removed from the array
11646
+
11647
+ `changeMeta` - An object which contains meta information about the
11648
+ change. It contains the following properties:
11649
+
11650
+ - `property` the computed property
11651
+ - `propertyName` the name of the property on the object
11652
+ - `index` the index of the added or removed item
11653
+ - `item` the added or removed item: this is exactly the same as
11654
+ the second arg
11655
+ - `arrayChanged` the array that triggered the change. Can be
11656
+ useful when depending on multiple arrays.
11657
+
11658
+ For property changes triggered on an item property change (when
11659
+ depKey is something like `someArray.@each.someProperty`),
11660
+ `changeMeta` will also contain the following property:
11661
+
11662
+ - `previousValues` an object whose keys are the properties that changed on
11663
+ the item, and whose values are the item's previous values.
11664
+
11665
+ `previousValues` is important Ember coalesces item property changes via
11666
+ Ember.run.once. This means that by the time removedItem gets called, item has
11667
+ the new values, but you may need the previous value (eg for sorting &
11668
+ filtering).
11669
+
11670
+ `instanceMeta` - An object that can be used to store meta
11671
+ information needed for calculating your computed. For example a
11672
+ unique computed might use this to store the number of times a given
11673
+ element is found in the dependent array.
11674
+
11675
+ The `removedItem` and `addedItem` functions should return the accumulated
11676
+ value. It is acceptable to not return anything (ie return undefined)
11677
+ to invalidate the computation. This is generally not a good idea for
11678
+ arrayComputed but it's used in eg max and min.
11679
+
11680
+ Example
11681
+
11682
+ ```javascript
11683
+ Ember.computed.map = function(dependentKey, callback) {
11684
+ var options = {
11685
+ addedItem: function(array, item, changeMeta, instanceMeta) {
11686
+ var mapped = callback(item);
11687
+ array.insertAt(changeMeta.index, mapped);
11688
+ return array;
11689
+ },
11690
+ removedItem: function(array, item, changeMeta, instanceMeta) {
11691
+ array.removeAt(changeMeta.index, 1);
11692
+ return array;
11693
+ }
11694
+ };
11407
11695
 
11696
+ return Ember.arrayComputed(dependentKey, options);
11697
+ };
11698
+ ```
11699
+
11700
+ @method arrayComputed
11701
+ @for Ember
11702
+ @param {String} [dependentKeys*]
11703
+ @param {Object} options
11704
+ @returns {Ember.ComputedProperty}
11705
+ */
11408
11706
  Ember.arrayComputed = function (options) {
11409
11707
  var args;
11410
11708
 
@@ -11431,12 +11729,45 @@ Ember.arrayComputed = function (options) {
11431
11729
 
11432
11730
 
11433
11731
  (function() {
11732
+ /**
11733
+ @module ember
11734
+ @submodule ember-runtime
11735
+ */
11736
+
11434
11737
  var get = Ember.get,
11435
11738
  set = Ember.set,
11739
+ guidFor = Ember.guidFor,
11740
+ merge = Ember.merge,
11436
11741
  a_slice = [].slice,
11437
11742
  forEach = Ember.EnumerableUtils.forEach,
11438
11743
  map = Ember.EnumerableUtils.map;
11439
11744
 
11745
+ /**
11746
+ A computed property that calculates the maximum value in the
11747
+ dependent array. This will return `-Infinity` when the dependent
11748
+ array is empty.
11749
+
11750
+ Example
11751
+
11752
+ ```javascript
11753
+ App.Person = Ember.Object.extend({
11754
+ childAges: Ember.computed.mapBy('children', 'age'),
11755
+ maxChildAge: Ember.computed.max('childAges')
11756
+ });
11757
+
11758
+ var lordByron = App.Person.create({children: []});
11759
+ lordByron.get('maxChildAge'); // -Infinity
11760
+ lordByron.get('children').pushObject({name: 'Augusta Ada Byron', age: 7});
11761
+ lordByron.get('maxChildAge'); // 7
11762
+ lordByron.get('children').pushObjects([{name: 'Allegra Byron', age: 5}, {name: 'Elizabeth Medora Leigh', age: 8}]);
11763
+ lordByron.get('maxChildAge'); // 8
11764
+ ```
11765
+
11766
+ @method computed.max
11767
+ @for Ember
11768
+ @param {String} dependentKey
11769
+ @return {Ember.ComputedProperty} computes the largest value in the dependentKey's array
11770
+ */
11440
11771
  Ember.computed.max = function (dependentKey) {
11441
11772
  return Ember.reduceComputed.call(null, dependentKey, {
11442
11773
  initialValue: -Infinity,
@@ -11453,6 +11784,32 @@ Ember.computed.max = function (dependentKey) {
11453
11784
  });
11454
11785
  };
11455
11786
 
11787
+ /**
11788
+ A computed property that calculates the minimum value in the
11789
+ dependent array. This will return `Infinity` when the dependent
11790
+ array is empty.
11791
+
11792
+ Example
11793
+
11794
+ ```javascript
11795
+ App.Person = Ember.Object.extend({
11796
+ childAges: Ember.computed.mapBy('children', 'age'),
11797
+ minChildAge: Ember.computed.min('childAges')
11798
+ });
11799
+
11800
+ var lordByron = App.Person.create({children: []});
11801
+ lordByron.get('minChildAge'); // Infinity
11802
+ lordByron.get('children').pushObject({name: 'Augusta Ada Byron', age: 7});
11803
+ lordByron.get('minChildAge'); // 7
11804
+ lordByron.get('children').pushObjects([{name: 'Allegra Byron', age: 5}, {name: 'Elizabeth Medora Leigh', age: 8}]);
11805
+ lordByron.get('minChildAge'); // 5
11806
+ ```
11807
+
11808
+ @method computed.min
11809
+ @for Ember
11810
+ @param {String} dependentKey
11811
+ @return {Ember.ComputedProperty} computes the smallest value in the dependentKey's array
11812
+ */
11456
11813
  Ember.computed.min = function (dependentKey) {
11457
11814
  return Ember.reduceComputed.call(null, dependentKey, {
11458
11815
  initialValue: Infinity,
@@ -11469,6 +11826,36 @@ Ember.computed.min = function (dependentKey) {
11469
11826
  });
11470
11827
  };
11471
11828
 
11829
+ /**
11830
+ Returns an array mapped via the callback
11831
+
11832
+ The callback method you provide should have the following signature:
11833
+
11834
+ ```javascript
11835
+ function(item);
11836
+ ```
11837
+
11838
+ - `item` is the current item in the iteration.
11839
+
11840
+ Example
11841
+
11842
+ ```javascript
11843
+ App.Hampster = Ember.Object.extend({
11844
+ excitingChores: Ember.computed.map('chores', function(chore) {
11845
+ return chore.toUpperCase() + '!';
11846
+ })
11847
+ });
11848
+
11849
+ var hampster = App.Hampster.create({chores: ['cook', 'clean', 'write more unit tests']});
11850
+ hampster.get('excitingChores'); // ['COOK!', 'CLEAN!', 'WRITE MORE UNIT TESTS!']
11851
+ ```
11852
+
11853
+ @method computed.map
11854
+ @for Ember
11855
+ @param {String} dependentKey
11856
+ @param {Function} callback
11857
+ @return {Ember.ComputedProperty} an array mapped via the callback
11858
+ */
11472
11859
  Ember.computed.map = function(dependentKey, callback) {
11473
11860
  var options = {
11474
11861
  addedItem: function(array, item, changeMeta, instanceMeta) {
@@ -11485,11 +11872,79 @@ Ember.computed.map = function(dependentKey, callback) {
11485
11872
  return Ember.arrayComputed(dependentKey, options);
11486
11873
  };
11487
11874
 
11488
- Ember.computed.mapProperty = function(dependentKey, propertyKey) {
11875
+ /**
11876
+ Returns an array mapped to the specified key.
11877
+
11878
+ Example
11879
+
11880
+ ```javascript
11881
+ App.Person = Ember.Object.extend({
11882
+ childAges: Ember.computed.mapBy('children', 'age'),
11883
+ minChildAge: Ember.computed.min('childAges')
11884
+ });
11885
+
11886
+ var lordByron = App.Person.create({children: []});
11887
+ lordByron.get('childAge'); // []
11888
+ lordByron.get('children').pushObject({name: 'Augusta Ada Byron', age: 7});
11889
+ lordByron.get('childAge'); // [7]
11890
+ lordByron.get('children').pushObjects([{name: 'Allegra Byron', age: 5}, {name: 'Elizabeth Medora Leigh', age: 8}]);
11891
+ lordByron.get('childAge'); // [7, 5, 8]
11892
+ ```
11893
+
11894
+ @method computed.mapBy
11895
+ @for Ember
11896
+ @param {String} dependentKey
11897
+ @param {String} propertyKey
11898
+ @return {Ember.ComputedProperty} an array mapped to the specified key
11899
+ */
11900
+ Ember.computed.mapBy = function(dependentKey, propertyKey) {
11489
11901
  var callback = function(item) { return get(item, propertyKey); };
11490
11902
  return Ember.computed.map(dependentKey + '.@each.' + propertyKey, callback);
11491
11903
  };
11492
11904
 
11905
+ /**
11906
+ @method computed.mapProperty
11907
+ @for Ember
11908
+ @deprecated Use `Ember.computed.mapBy` instead
11909
+ @param dependentKey
11910
+ @param propertyKey
11911
+ */
11912
+ Ember.computed.mapProperty = Ember.computed.mapBy;
11913
+
11914
+ /**
11915
+ Filters the array by the callback.
11916
+
11917
+ The callback method you provide should have the following signature:
11918
+
11919
+ ```javascript
11920
+ function(item);
11921
+ ```
11922
+
11923
+ - `item` is the current item in the iteration.
11924
+
11925
+ Example
11926
+
11927
+ ```javascript
11928
+ App.Hampster = Ember.Object.extend({
11929
+ remainingChores: Ember.computed.filter('chores', function(chore) {
11930
+ return !chore.done;
11931
+ })
11932
+ });
11933
+
11934
+ var hampster = App.Hampster.create({chores: [
11935
+ {name: 'cook', done: true},
11936
+ {name: 'clean', done: true},
11937
+ {name: 'write more unit tests', done: false}
11938
+ ]});
11939
+ hampster.get('remainingChores'); // [{name: 'write more unit tests', done: false}]
11940
+ ```
11941
+
11942
+ @method computed.filter
11943
+ @for Ember
11944
+ @param {String} dependentKey
11945
+ @param {Function} callback
11946
+ @return {Ember.ComputedProperty} the filtered array
11947
+ */
11493
11948
  Ember.computed.filter = function(dependentKey, callback) {
11494
11949
  var options = {
11495
11950
  initialize: function (array, changeMeta, instanceMeta) {
@@ -11503,6 +11958,8 @@ Ember.computed.filter = function(dependentKey, callback) {
11503
11958
  if (match) {
11504
11959
  array.insertAt(filterIndex, item);
11505
11960
  }
11961
+
11962
+ return array;
11506
11963
  },
11507
11964
 
11508
11965
  removedItem: function(array, item, changeMeta, instanceMeta) {
@@ -11511,13 +11968,40 @@ Ember.computed.filter = function(dependentKey, callback) {
11511
11968
  if (filterIndex > -1) {
11512
11969
  array.removeAt(filterIndex);
11513
11970
  }
11971
+
11972
+ return array;
11514
11973
  }
11515
11974
  };
11516
11975
 
11517
11976
  return Ember.arrayComputed(dependentKey, options);
11518
11977
  };
11519
11978
 
11520
- Ember.computed.filterProperty = function(dependentKey, propertyKey, value) {
11979
+ /**
11980
+ Filters the array by the property and value
11981
+
11982
+ Example
11983
+
11984
+ ```javascript
11985
+ App.Hampster = Ember.Object.extend({
11986
+ remainingChores: Ember.computed.filterBy('chores', 'done', false)
11987
+ });
11988
+
11989
+ var hampster = App.Hampster.create({chores: [
11990
+ {name: 'cook', done: true},
11991
+ {name: 'clean', done: true},
11992
+ {name: 'write more unit tests', done: false}
11993
+ ]});
11994
+ hampster.get('remainingChores'); // [{name: 'write more unit tests', done: false}]
11995
+ ```
11996
+
11997
+ @method computed.filterBy
11998
+ @for Ember
11999
+ @param {String} dependentKey
12000
+ @param {String} propertyKey
12001
+ @param {String} value
12002
+ @return {Ember.ComputedProperty} the filtered array
12003
+ */
12004
+ Ember.computed.filterBy = function(dependentKey, propertyKey, value) {
11521
12005
  var callback;
11522
12006
 
11523
12007
  if (arguments.length === 2) {
@@ -11533,6 +12017,42 @@ Ember.computed.filterProperty = function(dependentKey, propertyKey, value) {
11533
12017
  return Ember.computed.filter(dependentKey + '.@each.' + propertyKey, callback);
11534
12018
  };
11535
12019
 
12020
+ /**
12021
+ @method computed.filterProperty
12022
+ @for Ember
12023
+ @param dependentKey
12024
+ @param propertyKey
12025
+ @param value
12026
+ @deprecated Use `Ember.computed.filterBy` instead
12027
+ */
12028
+ Ember.computed.filterProperty = Ember.computed.filterBy;
12029
+
12030
+ /**
12031
+ A computed property which returns a new array with all the unique
12032
+ elements from one or more dependent arrays.
12033
+
12034
+ Example
12035
+
12036
+ ```javascript
12037
+ App.Hampster = Ember.Object.extend({
12038
+ uniqueFruits: Ember.computed.uniq('fruits')
12039
+ });
12040
+
12041
+ var hampster = App.Hampster.create({fruits: [
12042
+ 'banana',
12043
+ 'grape',
12044
+ 'kale',
12045
+ 'banana'
12046
+ ]});
12047
+ hampster.get('uniqueFruits'); // ['banana', 'grape', 'kale']
12048
+ ```
12049
+
12050
+ @method computed.uniq
12051
+ @for Ember
12052
+ @param {String} propertyKey*
12053
+ @return {Ember.ComputedProperty} computes a new array with all the
12054
+ unique elements from the dependent array
12055
+ */
11536
12056
  Ember.computed.uniq = function() {
11537
12057
  var args = a_slice.call(arguments);
11538
12058
  args.push({
@@ -11541,7 +12061,7 @@ Ember.computed.uniq = function() {
11541
12061
  },
11542
12062
 
11543
12063
  addedItem: function(array, item, changeMeta, instanceMeta) {
11544
- var guid = Ember.guidFor(item);
12064
+ var guid = guidFor(item);
11545
12065
 
11546
12066
  if (!instanceMeta.itemCounts[guid]) {
11547
12067
  instanceMeta.itemCounts[guid] = 1;
@@ -11552,7 +12072,7 @@ Ember.computed.uniq = function() {
11552
12072
  return array;
11553
12073
  },
11554
12074
  removedItem: function(array, item, _, instanceMeta) {
11555
- var guid = Ember.guidFor(item),
12075
+ var guid = guidFor(item),
11556
12076
  itemCounts = instanceMeta.itemCounts;
11557
12077
 
11558
12078
  if (--itemCounts[guid] === 0) {
@@ -11563,12 +12083,44 @@ Ember.computed.uniq = function() {
11563
12083
  });
11564
12084
  return Ember.arrayComputed.apply(null, args);
11565
12085
  };
12086
+
12087
+ /**
12088
+ Alias for [Ember.computed.uniq](/api/#method_computed_uniq).
12089
+
12090
+ @method computed.union
12091
+ @for Ember
12092
+ @param {String} propertyKey*
12093
+ @return {Ember.ComputedProperty} computes a new array with all the
12094
+ unique elements from the dependent array
12095
+ */
11566
12096
  Ember.computed.union = Ember.computed.uniq;
11567
12097
 
12098
+ /**
12099
+ A computed property which returns a new array with all the duplicated
12100
+ elements from two or more dependeny arrays.
12101
+
12102
+ Example
12103
+
12104
+ ```javascript
12105
+ var obj = Ember.Object.createWithMixins({
12106
+ adaFriends: ['Charles Babbage', 'John Hobhouse', 'William King', 'Mary Somerville'],
12107
+ charlesFriends: ['William King', 'Mary Somerville', 'Ada Lovelace', 'George Peacock'],
12108
+ friendsInCommon: Ember.computed.intersect('adaFriends', 'charlesFriends')
12109
+ });
12110
+
12111
+ obj.get('friendsInCommon'); // ['William King', 'Mary Somerville']
12112
+ ```
12113
+
12114
+ @method computed.intersect
12115
+ @for Ember
12116
+ @param {String} propertyKey*
12117
+ @return {Ember.ComputedProperty} computes a new array with all the
12118
+ duplicated elements from the dependent arrays
12119
+ */
11568
12120
  Ember.computed.intersect = function () {
11569
12121
  var getDependentKeyGuids = function (changeMeta) {
11570
12122
  return map(changeMeta.property._dependentKeys, function (dependentKey) {
11571
- return Ember.guidFor(dependentKey);
12123
+ return guidFor(dependentKey);
11572
12124
  });
11573
12125
  };
11574
12126
 
@@ -11579,9 +12131,9 @@ Ember.computed.intersect = function () {
11579
12131
  },
11580
12132
 
11581
12133
  addedItem: function(array, item, changeMeta, instanceMeta) {
11582
- var itemGuid = Ember.guidFor(item),
12134
+ var itemGuid = guidFor(item),
11583
12135
  dependentGuids = getDependentKeyGuids(changeMeta),
11584
- dependentGuid = Ember.guidFor(changeMeta.arrayChanged),
12136
+ dependentGuid = guidFor(changeMeta.arrayChanged),
11585
12137
  numberOfDependentArrays = changeMeta.property._dependentKeys.length,
11586
12138
  itemCounts = instanceMeta.itemCounts;
11587
12139
 
@@ -11590,15 +12142,15 @@ Ember.computed.intersect = function () {
11590
12142
 
11591
12143
  if (++itemCounts[itemGuid][dependentGuid] === 1 &&
11592
12144
  numberOfDependentArrays === Ember.keys(itemCounts[itemGuid]).length) {
11593
-
12145
+
11594
12146
  array.addObject(item);
11595
12147
  }
11596
12148
  return array;
11597
12149
  },
11598
12150
  removedItem: function(array, item, changeMeta, instanceMeta) {
11599
- var itemGuid = Ember.guidFor(item),
12151
+ var itemGuid = guidFor(item),
11600
12152
  dependentGuids = getDependentKeyGuids(changeMeta),
11601
- dependentGuid = Ember.guidFor(changeMeta.arrayChanged),
12153
+ dependentGuid = guidFor(changeMeta.arrayChanged),
11602
12154
  numberOfDependentArrays = changeMeta.property._dependentKeys.length,
11603
12155
  numberOfArraysItemAppearsIn,
11604
12156
  itemCounts = instanceMeta.itemCounts;
@@ -11619,6 +12171,34 @@ Ember.computed.intersect = function () {
11619
12171
  return Ember.arrayComputed.apply(null, args);
11620
12172
  };
11621
12173
 
12174
+ /**
12175
+ A computed property which returns a new array with all the
12176
+ properties from the first dependent array that are not in the second
12177
+ dependent array.
12178
+
12179
+ Example
12180
+
12181
+ ```javascript
12182
+ App.Hampster = Ember.Object.extend({
12183
+ likes: ['banana', 'grape', 'kale'],
12184
+ wants: Ember.computed.setDiff('likes', 'fruits')
12185
+ });
12186
+
12187
+ var hampster = App.Hampster.create({fruits: [
12188
+ 'grape',
12189
+ 'kale',
12190
+ ]});
12191
+ hampster.get('wants'); // ['banana']
12192
+ ```
12193
+
12194
+ @method computed.setDiff
12195
+ @for Ember
12196
+ @param {String} setAProperty
12197
+ @param {String} setBProperty
12198
+ @return {Ember.ComputedProperty} computes a new array with all the
12199
+ items from the first dependent array that are not in the second
12200
+ dependent array
12201
+ */
11622
12202
  Ember.computed.setDiff = function (setAProperty, setBProperty) {
11623
12203
  if (arguments.length !== 2) {
11624
12204
  throw new Error("setDiff requires exactly two dependent arrays.");
@@ -11627,7 +12207,7 @@ Ember.computed.setDiff = function (setAProperty, setBProperty) {
11627
12207
  addedItem: function (array, item, changeMeta, instanceMeta) {
11628
12208
  var setA = get(this, setAProperty),
11629
12209
  setB = get(this, setBProperty);
11630
-
12210
+
11631
12211
  if (changeMeta.arrayChanged === setA) {
11632
12212
  if (!setB.contains(item)) {
11633
12213
  array.addObject(item);
@@ -11641,7 +12221,7 @@ Ember.computed.setDiff = function (setAProperty, setBProperty) {
11641
12221
  removedItem: function (array, item, changeMeta, instanceMeta) {
11642
12222
  var setA = get(this, setAProperty),
11643
12223
  setB = get(this, setBProperty);
11644
-
12224
+
11645
12225
  if (changeMeta.arrayChanged === setB) {
11646
12226
  if (setA.contains(item)) {
11647
12227
  array.addObject(item);
@@ -11655,7 +12235,7 @@ Ember.computed.setDiff = function (setAProperty, setBProperty) {
11655
12235
  };
11656
12236
 
11657
12237
  function binarySearch(array, item, low, high) {
11658
- var mid, midItem, res;
12238
+ var mid, midItem, res, guidMid, guidItem;
11659
12239
 
11660
12240
  if (arguments.length < 4) { high = get(array, 'length'); }
11661
12241
  if (arguments.length < 3) { low = 0; }
@@ -11667,7 +12247,18 @@ function binarySearch(array, item, low, high) {
11667
12247
  mid = low + Math.floor((high - low) / 2);
11668
12248
  midItem = array.objectAt(mid);
11669
12249
 
12250
+ guidMid = _guidFor(midItem);
12251
+ guidItem = _guidFor(item);
12252
+
12253
+ if (guidMid === guidItem) {
12254
+ return mid;
12255
+ }
12256
+
11670
12257
  res = this.order(midItem, item);
12258
+ if (res === 0) {
12259
+ res = guidMid < guidItem ? -1 : 1;
12260
+ }
12261
+
11671
12262
 
11672
12263
  if (res < 0) {
11673
12264
  return this.binarySearch(array, item, mid+1, high);
@@ -11676,8 +12267,66 @@ function binarySearch(array, item, low, high) {
11676
12267
  }
11677
12268
 
11678
12269
  return mid;
12270
+
12271
+ function _guidFor(item) {
12272
+ if (Ember.ObjectProxy.detectInstance(item)) {
12273
+ return guidFor(get(item, 'content'));
12274
+ }
12275
+ return guidFor(item);
12276
+ }
11679
12277
  }
11680
12278
 
12279
+ /**
12280
+ A computed property which returns a new array with all the
12281
+ properties from the first dependent array sorted based on a property
12282
+ or sort function.
12283
+
12284
+ The callback method you provide should have the following signature:
12285
+
12286
+ ```javascript
12287
+ function(itemA, itemB);
12288
+ ```
12289
+
12290
+ - `itemA` the first item to compare.
12291
+ - `itemB` the second item to compare.
12292
+
12293
+ This function should return `-1` when `itemA` should come before
12294
+ `itemB`. It should return `1` when `itemA` should come after
12295
+ `itemB`. If the `itemA` and `itemB` are equal this function should return `0`.
12296
+
12297
+ Example
12298
+
12299
+ ```javascript
12300
+ var ToDoList = Ember.Object.extend({
12301
+ todosSorting: ['name'],
12302
+ sortedTodos: Ember.computed.sort('todos', 'todosSorting'),
12303
+ priorityTodos: Ember.computed.sort('todos', function(a, b){
12304
+ if (a.priority > b.priority) {
12305
+ return 1;
12306
+ } else if (a.priority < b.priority) {
12307
+ return -1;
12308
+ }
12309
+ return 0;
12310
+ }),
12311
+ });
12312
+ var todoList = ToDoList.create({todos: [
12313
+ {name: 'Unit Test', priority: 2},
12314
+ {name: 'Documentation', priority: 3},
12315
+ {name: 'Release', priority: 1}
12316
+ ]});
12317
+
12318
+ todoList.get('sortedTodos'); // [{name:'Documentation', priority:3}, {name:'Release', priority:1}, {name:'Unit Test', priority:2}]
12319
+ todoList.get('priroityTodos'); // [{name:'Release', priority:1}, {name:'Unit Test', priority:2}, {name:'Documentation', priority:3}]
12320
+ ```
12321
+
12322
+ @method computed.sort
12323
+ @for Ember
12324
+ @param {String} dependentKey
12325
+ @param {String or Function} sortDefinition a dependent key to an
12326
+ array of sort properties or a function to use when sorting
12327
+ @return {Ember.ComputedProperty} computes a new sorted array based
12328
+ on the sort property array or callback function
12329
+ */
11681
12330
  Ember.computed.sort = function (itemsKey, sortDefinition) {
11682
12331
  Ember.assert("Ember.computed.sort requires two arguments: an array key to sort and either a sort properties key or sort function", arguments.length === 2);
11683
12332
 
@@ -11765,9 +12414,8 @@ Ember.computed.sort = function (itemsKey, sortDefinition) {
11765
12414
  removedItem: function (array, item, changeMeta, instanceMeta) {
11766
12415
  var proxyProperties, index, searchItem;
11767
12416
 
11768
- if (changeMeta.keyChanged) {
11769
- proxyProperties = { content: item };
11770
- proxyProperties[changeMeta.keyChanged] = changeMeta.previousValue;
12417
+ if (changeMeta.previousValues) {
12418
+ proxyProperties = merge({ content: item }, changeMeta.previousValues);
11771
12419
 
11772
12420
  searchItem = Ember.ObjectProxy.create(proxyProperties);
11773
12421
  } else {
@@ -12191,9 +12839,9 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) {
12191
12839
  Computed properties allow you to treat a function like a property:
12192
12840
 
12193
12841
  ```javascript
12194
- MyApp.president = Ember.Object.create({
12195
- firstName: "Barack",
12196
- lastName: "Obama",
12842
+ MyApp.President = Ember.Object.extend({
12843
+ firstName: '',
12844
+ lastName: '',
12197
12845
 
12198
12846
  fullName: function() {
12199
12847
  return this.get('firstName') + ' ' + this.get('lastName');
@@ -12202,7 +12850,12 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) {
12202
12850
  }.property()
12203
12851
  });
12204
12852
 
12205
- MyApp.president.get('fullName'); // "Barack Obama"
12853
+ var president = MyApp.President.create({
12854
+ firstName: "Barack",
12855
+ lastName: "Obama"
12856
+ });
12857
+
12858
+ president.get('fullName'); // "Barack Obama"
12206
12859
  ```
12207
12860
 
12208
12861
  Treating a function like a property is useful because they can work with
@@ -12214,9 +12867,9 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) {
12214
12867
  about these dependencies like this:
12215
12868
 
12216
12869
  ```javascript
12217
- MyApp.president = Ember.Object.create({
12218
- firstName: "Barack",
12219
- lastName: "Obama",
12870
+ MyApp.President = Ember.Object.extend({
12871
+ firstName: '',
12872
+ lastName: '',
12220
12873
 
12221
12874
  fullName: function() {
12222
12875
  return this.get('firstName') + ' ' + this.get('lastName');
@@ -12253,15 +12906,18 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) {
12253
12906
  For example:
12254
12907
 
12255
12908
  ```javascript
12256
- Ember.Object.create({
12909
+ Ember.Object.extend({
12257
12910
  valueObserver: function() {
12258
12911
  // Executes whenever the "value" property changes
12259
12912
  }.observes('value')
12260
12913
  });
12261
12914
  ```
12262
12915
 
12263
- See `Ember.observes`.
12264
-
12916
+ In the future this method may become asynchronous. If you want to ensure
12917
+ synchronous behavior, use `observesImmediately`.
12918
+
12919
+ See `Ember.observer`.
12920
+
12265
12921
  @method observes
12266
12922
  @for Function
12267
12923
  */
@@ -12270,24 +12926,58 @@ if (Ember.EXTEND_PROTOTYPES === true || Ember.EXTEND_PROTOTYPES.Function) {
12270
12926
  return this;
12271
12927
  };
12272
12928
 
12929
+ /**
12930
+ The `observesImmediately` extension of Javascript's Function prototype is
12931
+ available when `Ember.EXTEND_PROTOTYPES` or
12932
+ `Ember.EXTEND_PROTOTYPES.Function` is true, which is the default.
12933
+
12934
+ You can observe property changes simply by adding the `observesImmediately`
12935
+ call to the end of your method declarations in classes that you write.
12936
+ For example:
12937
+
12938
+ ```javascript
12939
+ Ember.Object.extend({
12940
+ valueObserver: function() {
12941
+ // Executes immediately after the "value" property changes
12942
+ }.observesImmediately('value')
12943
+ });
12944
+ ```
12945
+
12946
+ In the future, `observes` may become asynchronous. In this event,
12947
+ `observesImmediately` will maintain the synchronous behavior.
12948
+
12949
+ See `Ember.immediateObserver`.
12950
+
12951
+ @method observesImmediately
12952
+ @for Function
12953
+ */
12954
+ Function.prototype.observesImmediately = function() {
12955
+ for (var i=0, l=arguments.length; i<l; i++) {
12956
+ var arg = arguments[i];
12957
+ Ember.assert("Immediate observers must observe internal properties only, not properties on other objects.", arg.indexOf('.') === -1);
12958
+ }
12959
+
12960
+ return this.observes.apply(this, arguments);
12961
+ };
12962
+
12273
12963
  /**
12274
12964
  The `observesBefore` extension of Javascript's Function prototype is
12275
12965
  available when `Ember.EXTEND_PROTOTYPES` or
12276
12966
  `Ember.EXTEND_PROTOTYPES.Function` is true, which is the default.
12277
12967
 
12278
- You can get notified when a property changes is about to happen by
12968
+ You can get notified when a property change is about to happen by
12279
12969
  by adding the `observesBefore` call to the end of your method
12280
12970
  declarations in classes that you write. For example:
12281
12971
 
12282
12972
  ```javascript
12283
- Ember.Object.create({
12973
+ Ember.Object.extend({
12284
12974
  valueObserver: function() {
12285
12975
  // Executes whenever the "value" property is about to change
12286
12976
  }.observesBefore('value')
12287
12977
  });
12288
12978
  ```
12289
12979
 
12290
- See `Ember.observesBefore`.
12980
+ See `Ember.beforeObserver`.
12291
12981
 
12292
12982
  @method observesBefore
12293
12983
  @for Function
@@ -13730,11 +14420,6 @@ Ember.Evented = Ember.Mixin.create({
13730
14420
  Ember.sendEvent(this, name, args);
13731
14421
  },
13732
14422
 
13733
- fire: function(name) {
13734
- Ember.deprecate("Ember.Evented#fire() has been deprecated in favor of trigger() for compatibility with jQuery. It will be removed in 1.0. Please update your code to call trigger() instead.");
13735
- this.trigger.apply(this, arguments);
13736
- },
13737
-
13738
14423
  /**
13739
14424
  Cancels subscription for given name, target, and method.
13740
14425
 
@@ -13913,6 +14598,121 @@ Ember.ActionHandler = Ember.Mixin.create({
13913
14598
 
13914
14599
 
13915
14600
 
14601
+ (function() {
14602
+ var set = Ember.set, get = Ember.get,
14603
+ resolve = Ember.RSVP.resolve,
14604
+ rethrow = Ember.RSVP.rethrow,
14605
+ not = Ember.computed.not,
14606
+ or = Ember.computed.or;
14607
+
14608
+ /**
14609
+ @module ember
14610
+ @submodule ember-runtime
14611
+ */
14612
+
14613
+ function installPromise(proxy, promise) {
14614
+ promise.then(function(value) {
14615
+ set(proxy, 'isFulfilled', true);
14616
+ set(proxy, 'content', value);
14617
+
14618
+ return value;
14619
+ }, function(reason) {
14620
+ set(proxy, 'isRejected', true);
14621
+ set(proxy, 'reason', reason);
14622
+ }).fail(rethrow);
14623
+ }
14624
+
14625
+ /**
14626
+ A low level mixin making ObjectProxy, ObjectController or ArrayController's promise aware.
14627
+
14628
+ ```javascript
14629
+ var ObjectPromiseController = Ember.ObjectController.extend(Ember.PromiseProxyMixin);
14630
+
14631
+ var controller = ObjectPromiseController.create({
14632
+ promise: $.getJSON('/some/remote/data.json')
14633
+ });
14634
+
14635
+ controller.then(function(json){
14636
+ // the json
14637
+ }, function(reason) {
14638
+ // the reason why you have no json
14639
+ });
14640
+ ```
14641
+
14642
+ the controller has bindable attributes which
14643
+ track the promises life cycle
14644
+
14645
+ ```javascript
14646
+ controller.get('isPending') //=> true
14647
+ controller.get('isSettled') //=> false
14648
+ controller.get('isRejected') //=> false
14649
+ controller.get('isFulfilled') //=> false
14650
+ ```
14651
+
14652
+ When the the $.getJSON completes, and the promise is fulfilled
14653
+ with json, the life cycle attributes will update accordingly.
14654
+
14655
+ ```javascript
14656
+ controller.get('isPending') //=> false
14657
+ controller.get('isSettled') //=> true
14658
+ controller.get('isRejected') //=> false
14659
+ controller.get('isFulfilled') //=> true
14660
+ ```
14661
+
14662
+ As the controller is an ObjectController, and the json now its content,
14663
+ all the json properties will be available directly from the controller.
14664
+
14665
+ ```javascript
14666
+ // Assuming the following json:
14667
+ {
14668
+ firstName: 'Stefan',
14669
+ lastName: 'Penner'
14670
+ }
14671
+
14672
+ // both properties will accessible on the controller
14673
+ controller.get('firstName') //=> 'Stefan'
14674
+ controller.get('lastName') //=> 'Penner'
14675
+ ```
14676
+
14677
+ If the controller is backing a template, the attributes are
14678
+ bindable from within that template
14679
+ ```handlebars
14680
+ {{#if isPending}}
14681
+ loading...
14682
+ {{else}}
14683
+ firstName: {{firstName}}
14684
+ lastName: {{lastName}}
14685
+ {{/if}}
14686
+ ```
14687
+ @class Ember.PromiseProxyMixin
14688
+ */
14689
+ Ember.PromiseProxyMixin = Ember.Mixin.create({
14690
+ reason: null,
14691
+ isPending: not('isSettled').readOnly(),
14692
+ isSettled: or('isRejected', 'isFulfilled').readOnly(),
14693
+ isRejected: false,
14694
+ isFulfilled: false,
14695
+
14696
+ promise: Ember.computed(function(key, promise) {
14697
+ if (arguments.length === 2) {
14698
+ promise = resolve(promise);
14699
+ installPromise(this, promise);
14700
+ return promise;
14701
+ } else {
14702
+ throw new Error("PromiseProxy's promise must be set");
14703
+ }
14704
+ }),
14705
+
14706
+ then: function(fulfill, reject) {
14707
+ return get(this, 'promise').then(fulfill, reject);
14708
+ }
14709
+ });
14710
+
14711
+
14712
+ })();
14713
+
14714
+
14715
+
13916
14716
  (function() {
13917
14717
 
13918
14718
  })();
@@ -15613,7 +16413,9 @@ var get = Ember.get,
15613
16413
  removeBeforeObserver = Ember.removeBeforeObserver,
15614
16414
  removeObserver = Ember.removeObserver,
15615
16415
  propertyWillChange = Ember.propertyWillChange,
15616
- propertyDidChange = Ember.propertyDidChange;
16416
+ propertyDidChange = Ember.propertyDidChange,
16417
+ meta = Ember.meta,
16418
+ defineProperty = Ember.defineProperty;
15617
16419
 
15618
16420
  function contentPropertyWillChange(content, contentKey) {
15619
16421
  var key = contentKey.slice(8); // remove "content."
@@ -15731,6 +16533,14 @@ Ember.ObjectProxy = Ember.Object.extend(/** @scope Ember.ObjectProxy.prototype *
15731
16533
  },
15732
16534
 
15733
16535
  setUnknownProperty: function (key, value) {
16536
+ var m = meta(this);
16537
+ if (m.proto === this) {
16538
+ // if marked as prototype then just defineProperty
16539
+ // rather than delegate
16540
+ defineProperty(this, key, null, value);
16541
+ return value;
16542
+ }
16543
+
15734
16544
  var content = get(this, 'content');
15735
16545
  Ember.assert(fmt("Cannot delegate set('%@', %@) to the 'content' property of object proxy %@: its 'content' is undefined.", [key, value, this]), content);
15736
16546
  return set(content, key, value);
@@ -15738,25 +16548,6 @@ Ember.ObjectProxy = Ember.Object.extend(/** @scope Ember.ObjectProxy.prototype *
15738
16548
 
15739
16549
  });
15740
16550
 
15741
- Ember.ObjectProxy.reopenClass({
15742
- create: function () {
15743
- var mixin, prototype, i, l, properties, keyName;
15744
- if (arguments.length) {
15745
- prototype = this.proto();
15746
- for (i = 0, l = arguments.length; i < l; i++) {
15747
- properties = arguments[i];
15748
- for (keyName in properties) {
15749
- if (!properties.hasOwnProperty(keyName) || keyName in prototype) { continue; }
15750
- if (!mixin) mixin = {};
15751
- mixin[keyName] = null;
15752
- }
15753
- }
15754
- if (mixin) this._initMixins([mixin]);
15755
- }
15756
- return this._super.apply(this, arguments);
15757
- }
15758
- });
15759
-
15760
16551
  })();
15761
16552
 
15762
16553
 
@@ -15769,7 +16560,8 @@ Ember.ObjectProxy.reopenClass({
15769
16560
 
15770
16561
 
15771
16562
  var set = Ember.set, get = Ember.get, guidFor = Ember.guidFor;
15772
- var forEach = Ember.EnumerableUtils.forEach;
16563
+ var forEach = Ember.EnumerableUtils.forEach,
16564
+ indexOf = Ember.ArrayPolyfills.indexOf;
15773
16565
 
15774
16566
  var EachArray = Ember.Object.extend(Ember.Array, {
15775
16567
 
@@ -15827,7 +16619,7 @@ function removeObserverForContentKey(content, keyName, proxy, idx, loc) {
15827
16619
 
15828
16620
  guid = guidFor(item);
15829
16621
  indicies = objects[guid];
15830
- indicies[indicies.indexOf(loc)] = null;
16622
+ indicies[indexOf.call(indicies, loc)] = null;
15831
16623
  }
15832
16624
  }
15833
16625
  }
@@ -16752,7 +17544,7 @@ Ember.ControllerMixin = Ember.Mixin.create(Ember.ActionHandler, {
16752
17544
  deprecatedSend: function(actionName) {
16753
17545
  var args = [].slice.call(arguments, 1);
16754
17546
  Ember.assert('' + this + " has the action " + actionName + " but it is not a function", typeof this[actionName] === 'function');
16755
- Ember.deprecate('Action handlers implemented directly on controllers are deprecated in favor of action handlers on an `actions` object', false);
17547
+ Ember.deprecate('Action handlers implemented directly on controllers are deprecated in favor of action handlers on an `actions` object (' + actionName + ' on ' + this + ')', false);
16756
17548
  this[actionName].apply(this, args);
16757
17549
  return;
16758
17550
  }
@@ -17232,11 +18024,15 @@ Ember.ArrayController = Ember.ArrayProxy.extend(Ember.ControllerMixin,
17232
18024
  },
17233
18025
 
17234
18026
  init: function() {
17235
- if (!this.get('content')) { Ember.defineProperty(this, 'content', undefined, Ember.A()); }
17236
18027
  this._super();
18028
+
17237
18029
  this.set('_subControllers', Ember.A());
17238
18030
  },
17239
18031
 
18032
+ content: Ember.computed(function () {
18033
+ return Ember.A();
18034
+ }),
18035
+
17240
18036
  controllerAt: function(idx, object, controllerClass) {
17241
18037
  var container = get(this, 'container'),
17242
18038
  subControllers = get(this, '_subControllers'),
@@ -17322,1489 +18118,5 @@ Ember Runtime
17322
18118
 
17323
18119
  })();
17324
18120
 
17325
- (function() {
17326
- var get = Ember.get, set = Ember.set;
17327
-
17328
- /**
17329
- @module ember
17330
- @submodule ember-states
17331
- */
17332
-
17333
- /**
17334
- The State class allows you to define individual states within a finite state machine
17335
- inside your Ember application.
17336
-
17337
- ### How States Work
17338
-
17339
- When you setup a finite state machine this means you are setting up a mechanism to precisely
17340
- manage the change within a system. You can control the various states or modes that your
17341
- application can be in at any given time. Additionally, you can manage what specific states
17342
- are allowed to transition to other states.
17343
-
17344
- The state machine is in only one state at a time. This state is known as the current state.
17345
- It is possible to change from one state to another by a triggering event or condition.
17346
- This is called a transition.
17347
-
17348
- Finite state machines are important because they allow the application developer to be
17349
- deterministic about the the sequence of events that can happen within a system. Some states
17350
- cannot be entered when the application is a given state.
17351
-
17352
- For example:
17353
-
17354
- A door that is in the `locked` state cannot be `opened` (you must transition to the `unlocked`
17355
- state first).
17356
-
17357
- A door that is in the `open` state cannot be `locked` (you must transition to the `closed`
17358
- state first).
17359
-
17360
-
17361
- Each state instance has the following characteristics:
17362
-
17363
- - Zero or more parent states
17364
- - A start state
17365
- - A name
17366
- - A path (a computed value that prefixes parent states and the complete hierarchy to itself )
17367
-
17368
- A state is known as a "leafState" when it is the last item on the path and has no children
17369
- beneath it.
17370
-
17371
- The isLeaf property returns a boolean.
17372
-
17373
- Each state can emit the following transition events
17374
-
17375
- - setup
17376
- - enter
17377
- - exit
17378
-
17379
- A state object is ususally created in the context of a state manager.
17380
-
17381
- ```javascript
17382
- doorStateManager = Ember.StateManager.create({
17383
- locked: Ember.State.create(),
17384
- closed: Ember.State.create(),
17385
- unlocked: Ember.State.create(),
17386
- open: Ember.State.create()
17387
- });
17388
- ```
17389
-
17390
- @class State
17391
- @namespace Ember
17392
- @extends Ember.Object
17393
- @uses Ember.Evented
17394
- */
17395
- Ember.State = Ember.Object.extend(Ember.Evented,
17396
- /** @scope Ember.State.prototype */{
17397
- /**
17398
- A reference to the parent state.
17399
-
17400
- @property parentState
17401
- @type Ember.State
17402
- */
17403
- parentState: null,
17404
- start: null,
17405
-
17406
- /**
17407
- The name of this state.
17408
-
17409
- @property name
17410
- @type String
17411
- */
17412
- name: null,
17413
-
17414
- /**
17415
- The full path to this state.
17416
-
17417
- @property path
17418
- @type String
17419
- */
17420
- path: Ember.computed(function() {
17421
- var parentPath = get(this, 'parentState.path'),
17422
- path = get(this, 'name');
17423
-
17424
- if (parentPath) {
17425
- path = parentPath + '.' + path;
17426
- }
17427
-
17428
- return path;
17429
- }),
17430
-
17431
- /**
17432
- @private
17433
-
17434
- Override the default event firing from `Ember.Evented` to
17435
- also call methods with the given name.
17436
-
17437
- @method trigger
17438
- @param name
17439
- */
17440
- trigger: function(name) {
17441
- if (this[name]) {
17442
- this[name].apply(this, [].slice.call(arguments, 1));
17443
- }
17444
- this._super.apply(this, arguments);
17445
- },
17446
-
17447
- /**
17448
- Initialize Ember.State object
17449
- Sets childStates to Ember.NativeArray
17450
- Sets eventTransitions to empty object unless already defined.
17451
- Loops over properties of this state and ensures that any property that
17452
- is an instance of Ember.State is moved to `states` hash.
17453
-
17454
-
17455
- @method init
17456
- */
17457
- init: function() {
17458
- var states = get(this, 'states');
17459
- set(this, 'childStates', Ember.A());
17460
- set(this, 'eventTransitions', get(this, 'eventTransitions') || {});
17461
-
17462
- var name, value, transitionTarget;
17463
-
17464
- // As a convenience, loop over the properties
17465
- // of this state and look for any that are other
17466
- // Ember.State instances or classes, and move them
17467
- // to the `states` hash. This avoids having to
17468
- // create an explicit separate hash.
17469
-
17470
- if (!states) {
17471
- states = {};
17472
-
17473
- for (name in this) {
17474
- if (name === "constructor") { continue; }
17475
-
17476
- if (value = this[name]) {
17477
- if (transitionTarget = value.transitionTarget) {
17478
- this.eventTransitions[name] = transitionTarget;
17479
- }
17480
-
17481
- this.setupChild(states, name, value);
17482
- }
17483
- }
17484
-
17485
- set(this, 'states', states);
17486
- } else {
17487
- for (name in states) {
17488
- this.setupChild(states, name, states[name]);
17489
- }
17490
- }
17491
-
17492
- // pathsCaches is a nested hash of the form:
17493
- // pathsCaches[stateManagerTypeGuid][path] == transitions_hash
17494
- set(this, 'pathsCaches', {});
17495
- },
17496
-
17497
- /**
17498
- Sets a cached instance of the state. Ember.guidFor is used
17499
- to find the guid of the associated state manager. If a cache can be found
17500
- the state path is added to that cache, otherwise an empty JavaScript object
17501
- is created. And the state path is appended to that instead.
17502
-
17503
- @method setPathsCache
17504
- @param stateManager
17505
- @param path
17506
- @param transitions
17507
- */
17508
- setPathsCache: function(stateManager, path, transitions) {
17509
- var stateManagerTypeGuid = Ember.guidFor(stateManager.constructor),
17510
- pathsCaches = get(this, 'pathsCaches'),
17511
- pathsCacheForManager = pathsCaches[stateManagerTypeGuid] || {};
17512
-
17513
- pathsCacheForManager[path] = transitions;
17514
- pathsCaches[stateManagerTypeGuid] = pathsCacheForManager;
17515
- },
17516
-
17517
- /**
17518
- Returns a cached path for the state instance. Each state manager
17519
- has a GUID and this is used to look up a cached path if it has already
17520
- been created. If a cached path is not found an empty JavaScript object
17521
- is returned instead.
17522
-
17523
- @method getPathsCache
17524
- @param stateManager
17525
- @param path
17526
- */
17527
- getPathsCache: function(stateManager, path) {
17528
- var stateManagerTypeGuid = Ember.guidFor(stateManager.constructor),
17529
- pathsCaches = get(this, 'pathsCaches'),
17530
- pathsCacheForManager = pathsCaches[stateManagerTypeGuid] || {};
17531
-
17532
- return pathsCacheForManager[path];
17533
- },
17534
-
17535
- /**
17536
- @private
17537
-
17538
- Create the child instance and ensure that it is an instance of Ember.State
17539
-
17540
- @method setupChild
17541
- @param states
17542
- @param name
17543
- @param value
17544
- */
17545
- setupChild: function(states, name, value) {
17546
- if (!value) { return false; }
17547
- var instance;
17548
-
17549
- if (value instanceof Ember.State) {
17550
- set(value, 'name', name);
17551
- instance = value;
17552
- instance.container = this.container;
17553
- } else if (Ember.State.detect(value)) {
17554
- instance = value.create({
17555
- name: name,
17556
- container: this.container
17557
- });
17558
- }
17559
-
17560
- if (instance instanceof Ember.State) {
17561
- set(instance, 'parentState', this);
17562
- get(this, 'childStates').pushObject(instance);
17563
- states[name] = instance;
17564
- return instance;
17565
- }
17566
- },
17567
-
17568
- /**
17569
- @private
17570
-
17571
- @method lookupEventTransition
17572
- @param name
17573
- */
17574
- lookupEventTransition: function(name) {
17575
- var path, state = this;
17576
-
17577
- while(state && !path) {
17578
- path = state.eventTransitions[name];
17579
- state = state.get('parentState');
17580
- }
17581
-
17582
- return path;
17583
- },
17584
-
17585
- /**
17586
- A Boolean value indicating whether the state is a leaf state
17587
- in the state hierarchy. This is `false` if the state has child
17588
- states; otherwise it is true.
17589
-
17590
- @property isLeaf
17591
- @type Boolean
17592
- */
17593
- isLeaf: Ember.computed(function() {
17594
- return !get(this, 'childStates').length;
17595
- }),
17596
-
17597
- /**
17598
- A boolean value indicating whether the state takes a context.
17599
- By default we assume all states take contexts.
17600
-
17601
- @property hasContext
17602
- @default true
17603
- */
17604
- hasContext: true,
17605
-
17606
- /**
17607
- This is the default transition event.
17608
-
17609
- @event setup
17610
- @param {Ember.StateManager} manager
17611
- @param context
17612
- @see Ember.StateManager#transitionEvent
17613
- */
17614
- setup: Ember.K,
17615
-
17616
- /**
17617
- This event fires when the state is entered.
17618
-
17619
- @event enter
17620
- @param {Ember.StateManager} manager
17621
- */
17622
- enter: Ember.K,
17623
-
17624
- /**
17625
- This event fires when the state is exited.
17626
-
17627
- @event exit
17628
- @param {Ember.StateManager} manager
17629
- */
17630
- exit: Ember.K
17631
- });
17632
-
17633
- Ember.State.reopenClass({
17634
-
17635
- /**
17636
- Creates an action function for transitioning to the named state while
17637
- preserving context.
17638
-
17639
- The following example StateManagers are equivalent:
17640
-
17641
- ```javascript
17642
- aManager = Ember.StateManager.create({
17643
- stateOne: Ember.State.create({
17644
- changeToStateTwo: Ember.State.transitionTo('stateTwo')
17645
- }),
17646
- stateTwo: Ember.State.create({})
17647
- })
17648
-
17649
- bManager = Ember.StateManager.create({
17650
- stateOne: Ember.State.create({
17651
- changeToStateTwo: function(manager, context) {
17652
- manager.transitionTo('stateTwo', context)
17653
- }
17654
- }),
17655
- stateTwo: Ember.State.create({})
17656
- })
17657
- ```
17658
-
17659
- @method transitionTo
17660
- @static
17661
- @param {String} target
17662
- */
17663
-
17664
- transitionTo: function(target) {
17665
-
17666
- var transitionFunction = function(stateManager, contextOrEvent) {
17667
- var contexts = [],
17668
- Event = Ember.$ && Ember.$.Event;
17669
-
17670
- if (contextOrEvent && (Event && contextOrEvent instanceof Event)) {
17671
- if (contextOrEvent.hasOwnProperty('contexts')) {
17672
- contexts = contextOrEvent.contexts.slice();
17673
- }
17674
- }
17675
- else {
17676
- contexts = [].slice.call(arguments, 1);
17677
- }
17678
-
17679
- contexts.unshift(target);
17680
- stateManager.transitionTo.apply(stateManager, contexts);
17681
- };
17682
-
17683
- transitionFunction.transitionTarget = target;
17684
-
17685
- return transitionFunction;
17686
- }
17687
-
17688
- });
17689
-
17690
- })();
17691
-
17692
-
17693
-
17694
- (function() {
17695
- /**
17696
- @module ember
17697
- @submodule ember-states
17698
- */
17699
-
17700
- var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
17701
- var arrayForEach = Ember.ArrayPolyfills.forEach;
17702
- /**
17703
- A Transition takes the enter, exit and resolve states and normalizes
17704
- them:
17705
-
17706
- * takes any passed in contexts into consideration
17707
- * adds in `initialState`s
17708
-
17709
- @class Transition
17710
- @private
17711
- */
17712
- var Transition = function(raw) {
17713
- this.enterStates = raw.enterStates.slice();
17714
- this.exitStates = raw.exitStates.slice();
17715
- this.resolveState = raw.resolveState;
17716
-
17717
- this.finalState = raw.enterStates[raw.enterStates.length - 1] || raw.resolveState;
17718
- };
17719
-
17720
- Transition.prototype = {
17721
- /**
17722
- Normalize the passed in enter, exit and resolve states.
17723
-
17724
- This process also adds `finalState` and `contexts` to the Transition object.
17725
-
17726
- @method normalize
17727
- @param {Ember.StateManager} manager the state manager running the transition
17728
- @param {Array} contexts a list of contexts passed into `transitionTo`
17729
- */
17730
- normalize: function(manager, contexts) {
17731
- this.matchContextsToStates(contexts);
17732
- this.addInitialStates();
17733
- this.removeUnchangedContexts(manager);
17734
- return this;
17735
- },
17736
-
17737
- /**
17738
- Match each of the contexts passed to `transitionTo` to a state.
17739
- This process may also require adding additional enter and exit
17740
- states if there are more contexts than enter states.
17741
-
17742
- @method matchContextsToStates
17743
- @param {Array} contexts a list of contexts passed into `transitionTo`
17744
- */
17745
- matchContextsToStates: function(contexts) {
17746
- var stateIdx = this.enterStates.length - 1,
17747
- matchedContexts = [],
17748
- state,
17749
- context;
17750
-
17751
- // Next, we will match the passed in contexts to the states they
17752
- // represent.
17753
- //
17754
- // First, assign a context to each enter state in reverse order. If
17755
- // any contexts are left, add a parent state to the list of states
17756
- // to enter and exit, and assign a context to the parent state.
17757
- //
17758
- // If there are still contexts left when the state manager is
17759
- // reached, raise an exception.
17760
- //
17761
- // This allows the following:
17762
- //
17763
- // |- root
17764
- // | |- post
17765
- // | | |- comments
17766
- // | |- about (* current state)
17767
- //
17768
- // For `transitionTo('post.comments', post, post.get('comments')`,
17769
- // the first context (`post`) will be assigned to `root.post`, and
17770
- // the second context (`post.get('comments')`) will be assigned
17771
- // to `root.post.comments`.
17772
- //
17773
- // For the following:
17774
- //
17775
- // |- root
17776
- // | |- post
17777
- // | | |- index (* current state)
17778
- // | | |- comments
17779
- //
17780
- // For `transitionTo('post.comments', otherPost, otherPost.get('comments')`,
17781
- // the `<root.post>` state will be added to the list of enter and exit
17782
- // states because its context has changed.
17783
-
17784
- while (contexts.length > 0) {
17785
- if (stateIdx >= 0) {
17786
- state = this.enterStates[stateIdx--];
17787
- } else {
17788
- if (this.enterStates.length) {
17789
- state = get(this.enterStates[0], 'parentState');
17790
- if (!state) { throw "Cannot match all contexts to states"; }
17791
- } else {
17792
- // If re-entering the current state with a context, the resolve
17793
- // state will be the current state.
17794
- state = this.resolveState;
17795
- }
17796
-
17797
- this.enterStates.unshift(state);
17798
- this.exitStates.unshift(state);
17799
- }
17800
-
17801
- // in routers, only states with dynamic segments have a context
17802
- if (get(state, 'hasContext')) {
17803
- context = contexts.pop();
17804
- } else {
17805
- context = null;
17806
- }
17807
-
17808
- matchedContexts.unshift(context);
17809
- }
17810
-
17811
- this.contexts = matchedContexts;
17812
- },
17813
-
17814
- /**
17815
- Add any `initialState`s to the list of enter states.
17816
-
17817
- @method addInitialStates
17818
- */
17819
- addInitialStates: function() {
17820
- var finalState = this.finalState, initialState;
17821
-
17822
- while(true) {
17823
- initialState = get(finalState, 'initialState') || 'start';
17824
- finalState = get(finalState, 'states.' + initialState);
17825
-
17826
- if (!finalState) { break; }
17827
-
17828
- this.finalState = finalState;
17829
- this.enterStates.push(finalState);
17830
- this.contexts.push(undefined);
17831
- }
17832
- },
17833
-
17834
- /**
17835
- Remove any states that were added because the number of contexts
17836
- exceeded the number of explicit enter states, but the context has
17837
- not changed since the last time the state was entered.
17838
-
17839
- @method removeUnchangedContexts
17840
- @param {Ember.StateManager} manager passed in to look up the last
17841
- context for a state
17842
- */
17843
- removeUnchangedContexts: function(manager) {
17844
- // Start from the beginning of the enter states. If the state was added
17845
- // to the list during the context matching phase, make sure the context
17846
- // has actually changed since the last time the state was entered.
17847
- while (this.enterStates.length > 0) {
17848
- if (this.enterStates[0] !== this.exitStates[0]) { break; }
17849
-
17850
- if (this.enterStates.length === this.contexts.length) {
17851
- if (manager.getStateMeta(this.enterStates[0], 'context') !== this.contexts[0]) { break; }
17852
- this.contexts.shift();
17853
- }
17854
-
17855
- this.resolveState = this.enterStates.shift();
17856
- this.exitStates.shift();
17857
- }
17858
- }
17859
- };
17860
-
17861
-
17862
- /**
17863
- Sends the event to the currentState, if the event is not handled this method
17864
- will proceed to call the parentState recursively until it encounters an
17865
- event handler or reaches the top or root of the state path hierarchy.
17866
-
17867
- @method sendRecursively
17868
- @param event
17869
- @param currentState
17870
- @param isUnhandledPass
17871
- */
17872
- var sendRecursively = function(event, currentState, isUnhandledPass) {
17873
- var log = this.enableLogging,
17874
- eventName = isUnhandledPass ? 'unhandledEvent' : event,
17875
- action = currentState[eventName],
17876
- contexts, sendRecursiveArguments, actionArguments;
17877
-
17878
- contexts = [].slice.call(arguments, 3);
17879
-
17880
- // Test to see if the action is a method that
17881
- // can be invoked. Don't blindly check just for
17882
- // existence, because it is possible the state
17883
- // manager has a child state of the given name,
17884
- // and we should still raise an exception in that
17885
- // case.
17886
- if (typeof action === 'function') {
17887
- if (log) {
17888
- if (isUnhandledPass) {
17889
- Ember.Logger.log(fmt("STATEMANAGER: Unhandled event '%@' being sent to state %@.", [event, get(currentState, 'path')]));
17890
- } else {
17891
- Ember.Logger.log(fmt("STATEMANAGER: Sending event '%@' to state %@.", [event, get(currentState, 'path')]));
17892
- }
17893
- }
17894
-
17895
- actionArguments = contexts;
17896
- if (isUnhandledPass) {
17897
- actionArguments.unshift(event);
17898
- }
17899
- actionArguments.unshift(this);
17900
-
17901
- return action.apply(currentState, actionArguments);
17902
- } else {
17903
- var parentState = get(currentState, 'parentState');
17904
- if (parentState) {
17905
-
17906
- sendRecursiveArguments = contexts;
17907
- sendRecursiveArguments.unshift(event, parentState, isUnhandledPass);
17908
-
17909
- return sendRecursively.apply(this, sendRecursiveArguments);
17910
- } else if (!isUnhandledPass) {
17911
- return sendEvent.call(this, event, contexts, true);
17912
- }
17913
- }
17914
- };
17915
-
17916
- /**
17917
- Send an event to the currentState.
17918
-
17919
- @method sendEvent
17920
- @param eventName
17921
- @param sendRecursiveArguments
17922
- @param isUnhandledPass
17923
- */
17924
- var sendEvent = function(eventName, sendRecursiveArguments, isUnhandledPass) {
17925
- sendRecursiveArguments.unshift(eventName, get(this, 'currentState'), isUnhandledPass);
17926
- return sendRecursively.apply(this, sendRecursiveArguments);
17927
- };
17928
-
17929
- /**
17930
- StateManager is part of Ember's implementation of a finite state machine. A
17931
- StateManager instance manages a number of properties that are instances of
17932
- `Ember.State`,
17933
- tracks the current active state, and triggers callbacks when states have changed.
17934
-
17935
- ## Defining States
17936
-
17937
- The states of StateManager can be declared in one of two ways. First, you can
17938
- define a `states` property that contains all the states:
17939
-
17940
- ```javascript
17941
- var managerA = Ember.StateManager.create({
17942
- states: {
17943
- stateOne: Ember.State.create(),
17944
- stateTwo: Ember.State.create()
17945
- }
17946
- });
17947
-
17948
- managerA.get('states');
17949
- // {
17950
- // stateOne: Ember.State.create(),
17951
- // stateTwo: Ember.State.create()
17952
- // }
17953
- ```
17954
-
17955
- You can also add instances of `Ember.State` (or an `Ember.State` subclass)
17956
- directly as properties of a StateManager. These states will be collected into
17957
- the `states` property for you.
17958
-
17959
- ```javascript
17960
- var managerA = Ember.StateManager.create({
17961
- stateOne: Ember.State.create(),
17962
- stateTwo: Ember.State.create()
17963
- });
17964
-
17965
- managerA.get('states');
17966
- // {
17967
- // stateOne: Ember.State.create(),
17968
- // stateTwo: Ember.State.create()
17969
- // }
17970
- ```
17971
-
17972
- ## The Initial State
17973
-
17974
- When created, a StateManager instance will immediately enter into the state
17975
- defined as its `start` property or the state referenced by name in its
17976
- `initialState` property:
17977
-
17978
- ```javascript
17979
- var managerA = Ember.StateManager.create({
17980
- start: Ember.State.create({})
17981
- });
17982
-
17983
- managerA.get('currentState.name'); // 'start'
17984
-
17985
- var managerB = Ember.StateManager.create({
17986
- initialState: 'beginHere',
17987
- beginHere: Ember.State.create({})
17988
- });
17989
-
17990
- managerB.get('currentState.name'); // 'beginHere'
17991
- ```
17992
-
17993
- Because it is a property you may also provide a computed function if you wish
17994
- to derive an `initialState` programmatically:
17995
-
17996
- ```javascript
17997
- var managerC = Ember.StateManager.create({
17998
- initialState: function() {
17999
- if (someLogic) {
18000
- return 'active';
18001
- } else {
18002
- return 'passive';
18003
- }
18004
- }.property(),
18005
- active: Ember.State.create({}),
18006
- passive: Ember.State.create({})
18007
- });
18008
- ```
18009
-
18010
- ## Moving Between States
18011
-
18012
- A StateManager can have any number of `Ember.State` objects as properties
18013
- and can have a single one of these states as its current state.
18014
-
18015
- Calling `transitionTo` transitions between states:
18016
-
18017
- ```javascript
18018
- var robotManager = Ember.StateManager.create({
18019
- initialState: 'poweredDown',
18020
- poweredDown: Ember.State.create({}),
18021
- poweredUp: Ember.State.create({})
18022
- });
18023
-
18024
- robotManager.get('currentState.name'); // 'poweredDown'
18025
- robotManager.transitionTo('poweredUp');
18026
- robotManager.get('currentState.name'); // 'poweredUp'
18027
- ```
18028
-
18029
- Before transitioning into a new state the existing `currentState` will have
18030
- its `exit` method called with the StateManager instance as its first argument
18031
- and an object representing the transition as its second argument.
18032
-
18033
- After transitioning into a new state the new `currentState` will have its
18034
- `enter` method called with the StateManager instance as its first argument
18035
- and an object representing the transition as its second argument.
18036
-
18037
- ```javascript
18038
- var robotManager = Ember.StateManager.create({
18039
- initialState: 'poweredDown',
18040
- poweredDown: Ember.State.create({
18041
- exit: function(stateManager) {
18042
- console.log("exiting the poweredDown state")
18043
- }
18044
- }),
18045
- poweredUp: Ember.State.create({
18046
- enter: function(stateManager) {
18047
- console.log("entering the poweredUp state. Destroy all humans.")
18048
- }
18049
- })
18050
- });
18051
-
18052
- robotManager.get('currentState.name'); // 'poweredDown'
18053
- robotManager.transitionTo('poweredUp');
18054
-
18055
- // will log
18056
- // 'exiting the poweredDown state'
18057
- // 'entering the poweredUp state. Destroy all humans.'
18058
- ```
18059
-
18060
- Once a StateManager is already in a state, subsequent attempts to enter that
18061
- state will not trigger enter or exit method calls. Attempts to transition
18062
- into a state that the manager does not have will result in no changes in the
18063
- StateManager's current state:
18064
-
18065
- ```javascript
18066
- var robotManager = Ember.StateManager.create({
18067
- initialState: 'poweredDown',
18068
- poweredDown: Ember.State.create({
18069
- exit: function(stateManager) {
18070
- console.log("exiting the poweredDown state")
18071
- }
18072
- }),
18073
- poweredUp: Ember.State.create({
18074
- enter: function(stateManager) {
18075
- console.log("entering the poweredUp state. Destroy all humans.")
18076
- }
18077
- })
18078
- });
18079
-
18080
- robotManager.get('currentState.name'); // 'poweredDown'
18081
- robotManager.transitionTo('poweredUp');
18082
- // will log
18083
- // 'exiting the poweredDown state'
18084
- // 'entering the poweredUp state. Destroy all humans.'
18085
- robotManager.transitionTo('poweredUp'); // no logging, no state change
18086
-
18087
- robotManager.transitionTo('someUnknownState'); // silently fails
18088
- robotManager.get('currentState.name'); // 'poweredUp'
18089
- ```
18090
-
18091
- Each state property may itself contain properties that are instances of
18092
- `Ember.State`. The StateManager can transition to specific sub-states in a
18093
- series of transitionTo method calls or via a single transitionTo with the
18094
- full path to the specific state. The StateManager will also keep track of the
18095
- full path to its currentState
18096
-
18097
- ```javascript
18098
- var robotManager = Ember.StateManager.create({
18099
- initialState: 'poweredDown',
18100
- poweredDown: Ember.State.create({
18101
- charging: Ember.State.create(),
18102
- charged: Ember.State.create()
18103
- }),
18104
- poweredUp: Ember.State.create({
18105
- mobile: Ember.State.create(),
18106
- stationary: Ember.State.create()
18107
- })
18108
- });
18109
-
18110
- robotManager.get('currentState.name'); // 'poweredDown'
18111
-
18112
- robotManager.transitionTo('poweredUp');
18113
- robotManager.get('currentState.name'); // 'poweredUp'
18114
-
18115
- robotManager.transitionTo('mobile');
18116
- robotManager.get('currentState.name'); // 'mobile'
18117
-
18118
- // transition via a state path
18119
- robotManager.transitionTo('poweredDown.charging');
18120
- robotManager.get('currentState.name'); // 'charging'
18121
-
18122
- robotManager.get('currentState.path'); // 'poweredDown.charging'
18123
- ```
18124
-
18125
- Enter transition methods will be called for each state and nested child state
18126
- in their hierarchical order. Exit methods will be called for each state and
18127
- its nested states in reverse hierarchical order.
18128
-
18129
- Exit transitions for a parent state are not called when entering into one of
18130
- its child states, only when transitioning to a new section of possible states
18131
- in the hierarchy.
18132
-
18133
- ```javascript
18134
- var robotManager = Ember.StateManager.create({
18135
- initialState: 'poweredDown',
18136
- poweredDown: Ember.State.create({
18137
- enter: function() {},
18138
- exit: function() {
18139
- console.log("exited poweredDown state")
18140
- },
18141
- charging: Ember.State.create({
18142
- enter: function() {},
18143
- exit: function() {}
18144
- }),
18145
- charged: Ember.State.create({
18146
- enter: function() {
18147
- console.log("entered charged state")
18148
- },
18149
- exit: function() {
18150
- console.log("exited charged state")
18151
- }
18152
- })
18153
- }),
18154
- poweredUp: Ember.State.create({
18155
- enter: function() {
18156
- console.log("entered poweredUp state")
18157
- },
18158
- exit: function() {},
18159
- mobile: Ember.State.create({
18160
- enter: function() {
18161
- console.log("entered mobile state")
18162
- },
18163
- exit: function() {}
18164
- }),
18165
- stationary: Ember.State.create({
18166
- enter: function() {},
18167
- exit: function() {}
18168
- })
18169
- })
18170
- });
18171
-
18172
-
18173
- robotManager.get('currentState.path'); // 'poweredDown'
18174
- robotManager.transitionTo('charged');
18175
- // logs 'entered charged state'
18176
- // but does *not* log 'exited poweredDown state'
18177
- robotManager.get('currentState.name'); // 'charged
18178
-
18179
- robotManager.transitionTo('poweredUp.mobile');
18180
- // logs
18181
- // 'exited charged state'
18182
- // 'exited poweredDown state'
18183
- // 'entered poweredUp state'
18184
- // 'entered mobile state'
18185
- ```
18186
-
18187
- During development you can set a StateManager's `enableLogging` property to
18188
- `true` to receive console messages of state transitions.
18189
-
18190
- ```javascript
18191
- var robotManager = Ember.StateManager.create({
18192
- enableLogging: true
18193
- });
18194
- ```
18195
-
18196
- ## Managing currentState with Actions
18197
-
18198
- To control which transitions are possible for a given state, and
18199
- appropriately handle external events, the StateManager can receive and
18200
- route action messages to its states via the `send` method. Calling to
18201
- `send` with an action name will begin searching for a method with the same
18202
- name starting at the current state and moving up through the parent states
18203
- in a state hierarchy until an appropriate method is found or the StateManager
18204
- instance itself is reached.
18205
-
18206
- If an appropriately named method is found it will be called with the state
18207
- manager as the first argument and an optional `context` object as the second
18208
- argument.
18209
-
18210
- ```javascript
18211
- var managerA = Ember.StateManager.create({
18212
- initialState: 'stateOne.substateOne.subsubstateOne',
18213
- stateOne: Ember.State.create({
18214
- substateOne: Ember.State.create({
18215
- anAction: function(manager, context) {
18216
- console.log("an action was called")
18217
- },
18218
- subsubstateOne: Ember.State.create({})
18219
- })
18220
- })
18221
- });
18222
-
18223
- managerA.get('currentState.name'); // 'subsubstateOne'
18224
- managerA.send('anAction');
18225
- // 'stateOne.substateOne.subsubstateOne' has no anAction method
18226
- // so the 'anAction' method of 'stateOne.substateOne' is called
18227
- // and logs "an action was called"
18228
- // with managerA as the first argument
18229
- // and no second argument
18230
-
18231
- var someObject = {};
18232
- managerA.send('anAction', someObject);
18233
- // the 'anAction' method of 'stateOne.substateOne' is called again
18234
- // with managerA as the first argument and
18235
- // someObject as the second argument.
18236
- ```
18237
-
18238
- If the StateManager attempts to send an action but does not find an appropriately named
18239
- method in the current state or while moving upwards through the state hierarchy, it will
18240
- repeat the process looking for a `unhandledEvent` method. If an `unhandledEvent` method is
18241
- found, it will be called with the original event name as the second argument. If an
18242
- `unhandledEvent` method is not found, the StateManager will throw a new Ember.Error.
18243
-
18244
- ```javascript
18245
- var managerB = Ember.StateManager.create({
18246
- initialState: 'stateOne.substateOne.subsubstateOne',
18247
- stateOne: Ember.State.create({
18248
- substateOne: Ember.State.create({
18249
- subsubstateOne: Ember.State.create({}),
18250
- unhandledEvent: function(manager, eventName, context) {
18251
- console.log("got an unhandledEvent with name " + eventName);
18252
- }
18253
- })
18254
- })
18255
- });
18256
-
18257
- managerB.get('currentState.name'); // 'subsubstateOne'
18258
- managerB.send('anAction');
18259
- // neither `stateOne.substateOne.subsubstateOne` nor any of it's
18260
- // parent states have a handler for `anAction`. `subsubstateOne`
18261
- // also does not have a `unhandledEvent` method, but its parent
18262
- // state, `substateOne`, does, and it gets fired. It will log
18263
- // "got an unhandledEvent with name anAction"
18264
- ```
18265
-
18266
- Action detection only moves upwards through the state hierarchy from the current state.
18267
- It does not search in other portions of the hierarchy.
18268
-
18269
- ```javascript
18270
- var managerC = Ember.StateManager.create({
18271
- initialState: 'stateOne.substateOne.subsubstateOne',
18272
- stateOne: Ember.State.create({
18273
- substateOne: Ember.State.create({
18274
- subsubstateOne: Ember.State.create({})
18275
- })
18276
- }),
18277
- stateTwo: Ember.State.create({
18278
- anAction: function(manager, context) {
18279
- // will not be called below because it is
18280
- // not a parent of the current state
18281
- }
18282
- })
18283
- });
18284
-
18285
- managerC.get('currentState.name'); // 'subsubstateOne'
18286
- managerC.send('anAction');
18287
- // Error: <Ember.StateManager:ember132> could not
18288
- // respond to event anAction in state stateOne.substateOne.subsubstateOne.
18289
- ```
18290
-
18291
- Inside of an action method the given state should delegate `transitionTo` calls on its
18292
- StateManager.
18293
-
18294
- ```javascript
18295
- var robotManager = Ember.StateManager.create({
18296
- initialState: 'poweredDown.charging',
18297
- poweredDown: Ember.State.create({
18298
- charging: Ember.State.create({
18299
- chargeComplete: function(manager, context) {
18300
- manager.transitionTo('charged')
18301
- }
18302
- }),
18303
- charged: Ember.State.create({
18304
- boot: function(manager, context) {
18305
- manager.transitionTo('poweredUp')
18306
- }
18307
- })
18308
- }),
18309
- poweredUp: Ember.State.create({
18310
- beginExtermination: function(manager, context) {
18311
- manager.transitionTo('rampaging')
18312
- },
18313
- rampaging: Ember.State.create()
18314
- })
18315
- });
18316
-
18317
- robotManager.get('currentState.name'); // 'charging'
18318
- robotManager.send('boot'); // throws error, no boot action
18319
- // in current hierarchy
18320
- robotManager.get('currentState.name'); // remains 'charging'
18321
-
18322
- robotManager.send('beginExtermination'); // throws error, no beginExtermination
18323
- // action in current hierarchy
18324
- robotManager.get('currentState.name'); // remains 'charging'
18325
-
18326
- robotManager.send('chargeComplete');
18327
- robotManager.get('currentState.name'); // 'charged'
18328
-
18329
- robotManager.send('boot');
18330
- robotManager.get('currentState.name'); // 'poweredUp'
18331
-
18332
- robotManager.send('beginExtermination', allHumans);
18333
- robotManager.get('currentState.name'); // 'rampaging'
18334
- ```
18335
-
18336
- Transition actions can also be created using the `transitionTo` method of the `Ember.State` class. The
18337
- following example StateManagers are equivalent:
18338
-
18339
- ```javascript
18340
- var aManager = Ember.StateManager.create({
18341
- stateOne: Ember.State.create({
18342
- changeToStateTwo: Ember.State.transitionTo('stateTwo')
18343
- }),
18344
- stateTwo: Ember.State.create({})
18345
- });
18346
-
18347
- var bManager = Ember.StateManager.create({
18348
- stateOne: Ember.State.create({
18349
- changeToStateTwo: function(manager, context) {
18350
- manager.transitionTo('stateTwo', context)
18351
- }
18352
- }),
18353
- stateTwo: Ember.State.create({})
18354
- });
18355
- ```
18356
-
18357
- @class StateManager
18358
- @namespace Ember
18359
- @extends Ember.State
18360
- **/
18361
- Ember.StateManager = Ember.State.extend({
18362
- /**
18363
- @private
18364
-
18365
- When creating a new statemanager, look for a default state to transition
18366
- into. This state can either be named `start`, or can be specified using the
18367
- `initialState` property.
18368
-
18369
- @method init
18370
- */
18371
- init: function() {
18372
- this._super();
18373
-
18374
- set(this, 'stateMeta', Ember.Map.create());
18375
-
18376
- var initialState = get(this, 'initialState');
18377
-
18378
- if (!initialState && get(this, 'states.start')) {
18379
- initialState = 'start';
18380
- }
18381
-
18382
- if (initialState) {
18383
- this.transitionTo(initialState);
18384
- Ember.assert('Failed to transition to initial state "' + initialState + '"', !!get(this, 'currentState'));
18385
- }
18386
- },
18387
-
18388
- /**
18389
- Return the stateMeta, a hash of possible states. If no items exist in the stateMeta hash
18390
- this method sets the stateMeta to an empty JavaScript object and returns that instead.
18391
-
18392
- @method stateMetaFor
18393
- @param state
18394
- */
18395
- stateMetaFor: function(state) {
18396
- var meta = get(this, 'stateMeta'),
18397
- stateMeta = meta.get(state);
18398
-
18399
- if (!stateMeta) {
18400
- stateMeta = {};
18401
- meta.set(state, stateMeta);
18402
- }
18403
-
18404
- return stateMeta;
18405
- },
18406
-
18407
- /**
18408
- Sets a key value pair on the stateMeta hash.
18409
-
18410
- @method setStateMeta
18411
- @param state
18412
- @param key
18413
- @param value
18414
- */
18415
- setStateMeta: function(state, key, value) {
18416
- return set(this.stateMetaFor(state), key, value);
18417
- },
18418
-
18419
- /**
18420
- Returns the value of an item in the stateMeta hash at the given key.
18421
-
18422
- @method getStateMeta
18423
- @param state
18424
- @param key
18425
- */
18426
- getStateMeta: function(state, key) {
18427
- return get(this.stateMetaFor(state), key);
18428
- },
18429
-
18430
- /**
18431
- The current state from among the manager's possible states. This property should
18432
- not be set directly. Use `transitionTo` to move between states by name.
18433
-
18434
- @property currentState
18435
- @type Ember.State
18436
- */
18437
- currentState: null,
18438
-
18439
- /**
18440
- The path of the current state. Returns a string representation of the current
18441
- state.
18442
-
18443
- @property currentPath
18444
- @type String
18445
- */
18446
- currentPath: Ember.computed.alias('currentState.path'),
18447
-
18448
- /**
18449
- The name of transitionEvent that this stateManager will dispatch
18450
-
18451
- @property transitionEvent
18452
- @type String
18453
- @default 'setup'
18454
- */
18455
- transitionEvent: 'setup',
18456
-
18457
- /**
18458
- If set to true, `errorOnUnhandledEvents` will cause an exception to be
18459
- raised if you attempt to send an event to a state manager that is not
18460
- handled by the current state or any of its parent states.
18461
-
18462
- @property errorOnUnhandledEvents
18463
- @type Boolean
18464
- @default true
18465
- */
18466
- errorOnUnhandledEvent: true,
18467
-
18468
- /**
18469
- An alias to sendEvent method
18470
-
18471
- @method send
18472
- @param event
18473
- */
18474
- send: function(event) {
18475
- var contexts = [].slice.call(arguments, 1);
18476
- Ember.assert('Cannot send event "' + event + '" while currentState is ' + get(this, 'currentState'), get(this, 'currentState'));
18477
- return sendEvent.call(this, event, contexts, false);
18478
- },
18479
-
18480
- /**
18481
- If errorOnUnhandledEvent is true this event with throw an Ember.Error
18482
- indicating that the no state could respond to the event passed through the
18483
- state machine.
18484
-
18485
- @method unhandledEvent
18486
- @param manager
18487
- @param event
18488
- */
18489
- unhandledEvent: function(manager, event) {
18490
- if (get(this, 'errorOnUnhandledEvent')) {
18491
- throw new Ember.Error(this.toString() + " could not respond to event " + event + " in state " + get(this, 'currentState.path') + ".");
18492
- }
18493
- },
18494
-
18495
- /**
18496
- Finds a state by its state path.
18497
-
18498
- Example:
18499
-
18500
- ```javascript
18501
- var manager = Ember.StateManager.create({
18502
- root: Ember.State.create({
18503
- dashboard: Ember.State.create()
18504
- })
18505
- });
18506
-
18507
- manager.getStateByPath(manager, "root.dashboard");
18508
- // returns the dashboard state
18509
-
18510
- var aState = manager.getStateByPath(manager, "root.dashboard");
18511
-
18512
- var path = aState.get('path');
18513
- // path is 'root.dashboard'
18514
-
18515
- var name = aState.get('name');
18516
- // name is 'dashboard'
18517
- ```
18518
-
18519
- @method getStateByPath
18520
- @param {Ember.State} root the state to start searching from
18521
- @param {String} path the state path to follow
18522
- @return {Ember.State} the state at the end of the path
18523
- */
18524
- getStateByPath: function(root, path) {
18525
- var parts = path.split('.'),
18526
- state = root;
18527
-
18528
- for (var i=0, len=parts.length; i<len; i++) {
18529
- state = get(get(state, 'states'), parts[i]);
18530
- if (!state) { break; }
18531
- }
18532
-
18533
- return state;
18534
- },
18535
-
18536
- findStateByPath: function(state, path) {
18537
- var possible;
18538
-
18539
- while (!possible && state) {
18540
- possible = this.getStateByPath(state, path);
18541
- state = get(state, 'parentState');
18542
- }
18543
-
18544
- return possible;
18545
- },
18546
-
18547
- /**
18548
- A state stores its child states in its `states` hash.
18549
- This code takes a path like `posts.show` and looks
18550
- up `root.states.posts.states.show`.
18551
-
18552
- It returns a list of all of the states from the
18553
- root, which is the list of states to call `enter`
18554
- on.
18555
-
18556
- @method getStatesInPath
18557
- @param root
18558
- @param path
18559
- */
18560
- getStatesInPath: function(root, path) {
18561
- if (!path || path === "") { return undefined; }
18562
- var parts = path.split('.'),
18563
- result = [],
18564
- states,
18565
- state;
18566
-
18567
- for (var i=0, len=parts.length; i<len; i++) {
18568
- states = get(root, 'states');
18569
- if (!states) { return undefined; }
18570
- state = get(states, parts[i]);
18571
- if (state) { root = state; result.push(state); }
18572
- else { return undefined; }
18573
- }
18574
-
18575
- return result;
18576
- },
18577
-
18578
- /**
18579
- Alias for transitionTo.
18580
- This method applies a transitionTo to the arguments passed into this method.
18581
-
18582
- @method goToState
18583
- */
18584
- goToState: function() {
18585
- // not deprecating this yet so people don't constantly need to
18586
- // make trivial changes for little reason.
18587
- return this.transitionTo.apply(this, arguments);
18588
- },
18589
-
18590
- /**
18591
- Transition to another state within the state machine. If the path is empty returns
18592
- immediately. This method attempts to get a hash of the enter, exit and resolve states
18593
- from the existing state cache. Processes the raw state information based on the
18594
- passed in context. Creates a new transition object and triggers a new setupContext.
18595
-
18596
- @method transitionTo
18597
- @param path
18598
- @param context
18599
- */
18600
- transitionTo: function(path, context) {
18601
- // XXX When is transitionTo called with no path
18602
- if (Ember.isEmpty(path)) { return; }
18603
-
18604
- // The ES6 signature of this function is `path, ...contexts`
18605
- var contexts = context ? Array.prototype.slice.call(arguments, 1) : [],
18606
- currentState = get(this, 'currentState') || this;
18607
-
18608
- // First, get the enter, exit and resolve states for the current state
18609
- // and specified path. If possible, use an existing cache.
18610
- var hash = this.contextFreeTransition(currentState, path);
18611
-
18612
- // Next, process the raw state information for the contexts passed in.
18613
- var transition = new Transition(hash).normalize(this, contexts);
18614
-
18615
- this.enterState(transition);
18616
- this.triggerSetupContext(transition);
18617
- },
18618
-
18619
- /**
18620
- Allows you to transition to any other state in the state manager without
18621
- being constrained by the state hierarchy of the current state path.
18622
- This method will traverse the state path upwards through its parents until
18623
- it finds the specified state path. All the transitions are captured during the
18624
- traversal.
18625
-
18626
- Caches and returns hash of transitions, which contain the exitSates, enterStates and
18627
- resolvedState
18628
-
18629
- @method contextFreeTransition
18630
- @param currentState
18631
- @param path
18632
- */
18633
- contextFreeTransition: function(currentState, path) {
18634
- var cache = currentState.getPathsCache(this, path);
18635
- if (cache) { return cache; }
18636
-
18637
- var enterStates = this.getStatesInPath(currentState, path),
18638
- exitStates = [],
18639
- resolveState = currentState;
18640
-
18641
- // Walk up the states. For each state, check whether a state matching
18642
- // the `path` is nested underneath. This will find the closest
18643
- // parent state containing `path`.
18644
- //
18645
- // This allows the user to pass in a relative path. For example, for
18646
- // the following state hierarchy:
18647
- //
18648
- // | |root
18649
- // | |- posts
18650
- // | | |- show (* current)
18651
- // | |- comments
18652
- // | | |- show
18653
- //
18654
- // If the current state is `<root.posts.show>`, an attempt to
18655
- // transition to `comments.show` will match `<root.comments.show>`.
18656
- //
18657
- // First, this code will look for root.posts.show.comments.show.
18658
- // Next, it will look for root.posts.comments.show. Finally,
18659
- // it will look for `root.comments.show`, and find the state.
18660
- //
18661
- // After this process, the following variables will exist:
18662
- //
18663
- // * resolveState: a common parent state between the current
18664
- // and target state. In the above example, `<root>` is the
18665
- // `resolveState`.
18666
- // * enterStates: a list of all of the states represented
18667
- // by the path from the `resolveState`. For example, for
18668
- // the path `root.comments.show`, `enterStates` would have
18669
- // `[<root.comments>, <root.comments.show>]`
18670
- // * exitStates: a list of all of the states from the
18671
- // `resolveState` to the `currentState`. In the above
18672
- // example, `exitStates` would have
18673
- // `[<root.posts>`, `<root.posts.show>]`.
18674
- while (resolveState && !enterStates) {
18675
- exitStates.unshift(resolveState);
18676
-
18677
- resolveState = get(resolveState, 'parentState');
18678
- if (!resolveState) {
18679
- enterStates = this.getStatesInPath(this, path);
18680
- if (!enterStates) {
18681
- Ember.assert('Could not find state for path: "'+path+'"');
18682
- return;
18683
- }
18684
- }
18685
- enterStates = this.getStatesInPath(resolveState, path);
18686
- }
18687
-
18688
- // If the path contains some states that are parents of both the
18689
- // current state and the target state, remove them.
18690
- //
18691
- // For example, in the following hierarchy:
18692
- //
18693
- // |- root
18694
- // | |- post
18695
- // | | |- index (* current)
18696
- // | | |- show
18697
- //
18698
- // If the `path` is `root.post.show`, the three variables will
18699
- // be:
18700
- //
18701
- // * resolveState: `<state manager>`
18702
- // * enterStates: `[<root>, <root.post>, <root.post.show>]`
18703
- // * exitStates: `[<root>, <root.post>, <root.post.index>]`
18704
- //
18705
- // The goal of this code is to remove the common states, so we
18706
- // have:
18707
- //
18708
- // * resolveState: `<root.post>`
18709
- // * enterStates: `[<root.post.show>]`
18710
- // * exitStates: `[<root.post.index>]`
18711
- //
18712
- // This avoid unnecessary calls to the enter and exit transitions.
18713
- while (enterStates.length > 0 && enterStates[0] === exitStates[0]) {
18714
- resolveState = enterStates.shift();
18715
- exitStates.shift();
18716
- }
18717
-
18718
- // Cache the enterStates, exitStates, and resolveState for the
18719
- // current state and the `path`.
18720
- var transitions = {
18721
- exitStates: exitStates,
18722
- enterStates: enterStates,
18723
- resolveState: resolveState
18724
- };
18725
-
18726
- currentState.setPathsCache(this, path, transitions);
18727
-
18728
- return transitions;
18729
- },
18730
-
18731
- /**
18732
- A trigger to setup the state contexts. Each state is setup with
18733
- an enterState.
18734
-
18735
- @method triggerSetupContext
18736
- @param transitions
18737
- */
18738
- triggerSetupContext: function(transitions) {
18739
- var contexts = transitions.contexts,
18740
- offset = transitions.enterStates.length - contexts.length,
18741
- enterStates = transitions.enterStates,
18742
- transitionEvent = get(this, 'transitionEvent');
18743
-
18744
- Ember.assert("More contexts provided than states", offset >= 0);
18745
-
18746
- arrayForEach.call(enterStates, function(state, idx) {
18747
- state.trigger(transitionEvent, this, contexts[idx-offset]);
18748
- }, this);
18749
- },
18750
-
18751
- /**
18752
- Returns the state instance by name. If state is not found the parentState
18753
- is returned instead.
18754
-
18755
- @method getState
18756
- @param name
18757
- */
18758
- getState: function(name) {
18759
- var state = get(this, name),
18760
- parentState = get(this, 'parentState');
18761
-
18762
- if (state) {
18763
- return state;
18764
- } else if (parentState) {
18765
- return parentState.getState(name);
18766
- }
18767
- },
18768
-
18769
- /**
18770
- Causes a transition from the exitState of one state to the enterState of another
18771
- state in the state machine. At the end of the transition the currentState is set
18772
- to the finalState of the transition passed into this method.
18773
-
18774
- @method enterState
18775
- @param transition
18776
- */
18777
- enterState: function(transition) {
18778
- var log = this.enableLogging;
18779
-
18780
- var exitStates = transition.exitStates.slice(0).reverse();
18781
- arrayForEach.call(exitStates, function(state) {
18782
- state.trigger('exit', this);
18783
- }, this);
18784
-
18785
- arrayForEach.call(transition.enterStates, function(state) {
18786
- if (log) { Ember.Logger.log("STATEMANAGER: Entering " + get(state, 'path')); }
18787
- state.trigger('enter', this);
18788
- }, this);
18789
-
18790
- set(this, 'currentState', transition.finalState);
18791
- }
18792
- });
18793
-
18794
- })();
18795
-
18796
-
18797
-
18798
- (function() {
18799
- /**
18800
- Ember States
18801
-
18802
- @module ember
18803
- @submodule ember-states
18804
- @requires ember-runtime
18805
- */
18806
-
18807
- })();
18808
-
18809
18121
 
18810
18122
  })();