ember-source 1.0.0.rc8 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.

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
  })();