pyro 0.9.0 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,284 @@
1
+ (function() {
2
+ var define, requireModule;
3
+
4
+ (function() {
5
+ var registry = {}, seen = {};
6
+
7
+ define = function(name, deps, callback) {
8
+ registry[name] = { deps: deps, callback: callback };
9
+ };
10
+
11
+ requireModule = function(name) {
12
+ if (seen[name]) { return seen[name]; }
13
+ seen[name] = {};
14
+
15
+ var mod, deps, callback, reified , exports;
16
+
17
+ mod = registry[name];
18
+
19
+ if (!mod) {
20
+ throw new Error("Module '" + name + "' not found.");
21
+ }
22
+
23
+ deps = mod.deps;
24
+ callback = mod.callback;
25
+ reified = [];
26
+ exports;
27
+
28
+ for (var i=0, l=deps.length; i<l; i++) {
29
+ if (deps[i] === 'exports') {
30
+ reified.push(exports = {});
31
+ } else {
32
+ reified.push(requireModule(deps[i]));
33
+ }
34
+ }
35
+
36
+ var value = callback.apply(this, reified);
37
+ return seen[name] = exports || value;
38
+ };
39
+ })();
40
+ (function() {
41
+ Ember.String.pluralize = function(word) {
42
+ return Ember.Inflector.inflector.pluralize(word);
43
+ };
44
+
45
+ Ember.String.singularize = function(word) {
46
+ return Ember.Inflector.inflector.singularize(word);
47
+ };
48
+
49
+ })();
50
+
51
+
52
+
53
+ (function() {
54
+ var BLANK_REGEX = /^\s*$/;
55
+
56
+ function loadUncountable(rules, uncountable) {
57
+ for (var i = 0, length = uncountable.length; i < length; i++) {
58
+ rules.uncountable[uncountable[i]] = true;
59
+ }
60
+ }
61
+
62
+ function loadIrregular(rules, irregularPairs) {
63
+ var pair;
64
+
65
+ for (var i = 0, length = irregularPairs.length; i < length; i++) {
66
+ pair = irregularPairs[i];
67
+
68
+ rules.irregular[pair[0]] = pair[1];
69
+ rules.irregularInverse[pair[1]] = pair[0];
70
+ }
71
+ }
72
+
73
+ function Inflector(ruleSet) {
74
+ ruleSet = ruleSet || {};
75
+ ruleSet.uncountable = ruleSet.uncountable || {};
76
+ ruleSet.irregularPairs= ruleSet.irregularPairs|| {};
77
+
78
+ var rules = this.rules = {
79
+ plurals: ruleSet.plurals || [],
80
+ singular: ruleSet.singular || [],
81
+ irregular: {},
82
+ irregularInverse: {},
83
+ uncountable: {}
84
+ };
85
+
86
+ loadUncountable(rules, ruleSet.uncountable);
87
+ loadIrregular(rules, ruleSet.irregularPairs);
88
+ }
89
+
90
+ Inflector.prototype = {
91
+ pluralize: function(word) {
92
+ return this.inflect(word, this.rules.plurals);
93
+ },
94
+
95
+ singularize: function(word) {
96
+ return this.inflect(word, this.rules.singular);
97
+ },
98
+
99
+ inflect: function(word, typeRules) {
100
+ var inflection, substitution, result, lowercase, isBlank,
101
+ isUncountable, isIrregular, isIrregularInverse, rule;
102
+
103
+ isBlank = BLANK_REGEX.test(word);
104
+
105
+ if (isBlank) {
106
+ return word;
107
+ }
108
+
109
+ lowercase = word.toLowerCase();
110
+
111
+ isUncountable = this.rules.uncountable[lowercase];
112
+
113
+ if (isUncountable) {
114
+ return word;
115
+ }
116
+
117
+ isIrregular = this.rules.irregular[lowercase];
118
+
119
+ if (isIrregular) {
120
+ return isIrregular;
121
+ }
122
+
123
+ isIrregularInverse = this.rules.irregularInverse[lowercase];
124
+
125
+ if (isIrregularInverse) {
126
+ return isIrregularInverse;
127
+ }
128
+
129
+ for (var i = typeRules.length, min = 0; i > min; i--) {
130
+ inflection = typeRules[i-1];
131
+ rule = inflection[0];
132
+
133
+ if (rule.test(word)) {
134
+ break;
135
+ }
136
+ }
137
+
138
+ inflection = inflection || [];
139
+
140
+ rule = inflection[0];
141
+ substitution = inflection[1];
142
+
143
+ result = word.replace(rule, substitution);
144
+
145
+ return result;
146
+ }
147
+ };
148
+
149
+ Ember.Inflector = Inflector;
150
+
151
+ })();
152
+
153
+
154
+
155
+ (function() {
156
+ Ember.Inflector.defaultRules = {
157
+ plurals: [
158
+ [/$/, 's'],
159
+ [/s$/i, 's'],
160
+ [/^(ax|test)is$/i, '$1es'],
161
+ [/(octop|vir)us$/i, '$1i'],
162
+ [/(octop|vir)i$/i, '$1i'],
163
+ [/(alias|status)$/i, '$1es'],
164
+ [/(bu)s$/i, '$1ses'],
165
+ [/(buffal|tomat)o$/i, '$1oes'],
166
+ [/([ti])um$/i, '$1a'],
167
+ [/([ti])a$/i, '$1a'],
168
+ [/sis$/i, 'ses'],
169
+ [/(?:([^f])fe|([lr])f)$/i, '$1$2ves'],
170
+ [/(hive)$/i, '$1s'],
171
+ [/([^aeiouy]|qu)y$/i, '$1ies'],
172
+ [/(x|ch|ss|sh)$/i, '$1es'],
173
+ [/(matr|vert|ind)(?:ix|ex)$/i, '$1ices'],
174
+ [/^(m|l)ouse$/i, '$1ice'],
175
+ [/^(m|l)ice$/i, '$1ice'],
176
+ [/^(ox)$/i, '$1en'],
177
+ [/^(oxen)$/i, '$1'],
178
+ [/(quiz)$/i, '$1zes']
179
+ ],
180
+
181
+ singular: [
182
+ [/s$/i, ''],
183
+ [/(ss)$/i, '$1'],
184
+ [/(n)ews$/i, '$1ews'],
185
+ [/([ti])a$/i, '$1um'],
186
+ [/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)(sis|ses)$/i, '$1sis'],
187
+ [/(^analy)(sis|ses)$/i, '$1sis'],
188
+ [/([^f])ves$/i, '$1fe'],
189
+ [/(hive)s$/i, '$1'],
190
+ [/(tive)s$/i, '$1'],
191
+ [/([lr])ves$/i, '$1f'],
192
+ [/([^aeiouy]|qu)ies$/i, '$1y'],
193
+ [/(s)eries$/i, '$1eries'],
194
+ [/(m)ovies$/i, '$1ovie'],
195
+ [/(x|ch|ss|sh)es$/i, '$1'],
196
+ [/^(m|l)ice$/i, '$1ouse'],
197
+ [/(bus)(es)?$/i, '$1'],
198
+ [/(o)es$/i, '$1'],
199
+ [/(shoe)s$/i, '$1'],
200
+ [/(cris|test)(is|es)$/i, '$1is'],
201
+ [/^(a)x[ie]s$/i, '$1xis'],
202
+ [/(octop|vir)(us|i)$/i, '$1us'],
203
+ [/(alias|status)(es)?$/i, '$1'],
204
+ [/^(ox)en/i, '$1'],
205
+ [/(vert|ind)ices$/i, '$1ex'],
206
+ [/(matr)ices$/i, '$1ix'],
207
+ [/(quiz)zes$/i, '$1'],
208
+ [/(database)s$/i, '$1']
209
+ ],
210
+
211
+ irregularPairs: [
212
+ ['person', 'people'],
213
+ ['man', 'men'],
214
+ ['child', 'children'],
215
+ ['sex', 'sexes'],
216
+ ['move', 'moves'],
217
+ ['cow', 'kine'],
218
+ ['zombie', 'zombies']
219
+ ],
220
+
221
+ uncountable: [
222
+ 'equipment',
223
+ 'information',
224
+ 'rice',
225
+ 'money',
226
+ 'species',
227
+ 'series',
228
+ 'fish',
229
+ 'sheep',
230
+ 'jeans',
231
+ 'police'
232
+ ]
233
+ };
234
+
235
+ })();
236
+
237
+
238
+
239
+ (function() {
240
+ if (Ember.EXTEND_PROTOTYPES) {
241
+ /**
242
+ See {{#crossLink "Ember.String/pluralize"}}{{/crossLink}}
243
+
244
+ @method pluralize
245
+ @for String
246
+ */
247
+ String.prototype.pluralize = function() {
248
+ return Ember.String.pluralize(this);
249
+ };
250
+
251
+ /**
252
+ See {{#crossLink "Ember.String/singularize"}}{{/crossLink}}
253
+
254
+ @method singularize
255
+ @for String
256
+ */
257
+ String.prototype.singularize = function() {
258
+ return Ember.String.singularize(this);
259
+ };
260
+ }
261
+
262
+ })();
263
+
264
+
265
+
266
+ (function() {
267
+ Ember.Inflector.inflector = new Ember.Inflector(Ember.Inflector.defaultRules);
268
+
269
+ })();
270
+
271
+
272
+
273
+ (function() {
274
+
275
+ })();
276
+
277
+
278
+ })();
279
+
280
+
281
+ if (typeof location !== 'undefined' && (location.hostname === 'localhost' || location.hostname === '127.0.0.1')) {
282
+ Ember.Logger.warn("You are running a production build of Ember on localhost and won't receive detailed error messages. "+
283
+ "If you want full error messages please use the non-minified build provided on the Ember website.");
284
+ }
@@ -1,5 +1,5 @@
1
- // Version: v1.0.0-rc.7-205-ga347ce2
2
- // Last commit: a347ce2 (2013-08-28 22:01:01 -0700)
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.7-205-ga347ce2
162
- // Last commit: a347ce2 (2013-08-28 22:01:01 -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.
11336
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).
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.
11407
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
+ };
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'),
@@ -18507,7 +19303,7 @@ Ember.CoreView = Ember.Object.extend(Ember.Evented, Ember.ActionHandler, {
18507
19303
  deprecatedSend: function(actionName) {
18508
19304
  var args = [].slice.call(arguments, 1);
18509
19305
  Ember.assert('' + this + " has the action " + actionName + " but it is not a function", typeof this[actionName] === 'function');
18510
- Ember.deprecate('Action handlers implemented directly on views are deprecated in favor of action handlers on an `actions` object', false);
19306
+ Ember.deprecate('Action handlers implemented directly on views are deprecated in favor of action handlers on an `actions` object (' + actionName + ' on ' + this + ')', false);
18511
19307
  this[actionName].apply(this, args);
18512
19308
  return;
18513
19309
  },
@@ -18891,9 +19687,6 @@ var EMPTY_ARRAY = [];
18891
19687
  Using a value for `templateName` that does not have a Handlebars template
18892
19688
  with a matching `data-template-name` attribute will throw an error.
18893
19689
 
18894
- Assigning a value to both `template` and `templateName` properties will throw
18895
- an error.
18896
-
18897
19690
  For views classes that may have a template later defined (e.g. as the block
18898
19691
  portion of a `{{view}}` Handlebars helper call in another template or in
18899
19692
  a subclass), you can provide a `defaultTemplate` property set to compiled
@@ -21436,7 +22229,7 @@ Ember.ContainerView = Ember.View.extend(Ember.MutableArray, {
21436
22229
 
21437
22230
  length: Ember.computed(function () {
21438
22231
  return this._childViews.length;
21439
- }),
22232
+ }).volatile(),
21440
22233
 
21441
22234
  /**
21442
22235
  @private
@@ -21766,11 +22559,6 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
21766
22559
  manipulated. Instead, add, remove, replace items from its `content` property.
21767
22560
  This will trigger appropriate changes to its rendered HTML.
21768
22561
 
21769
- ## Use in templates via the `{{collection}}` `Ember.Handlebars` helper
21770
-
21771
- `Ember.Handlebars` provides a helper specifically for adding
21772
- `CollectionView`s to templates. See [Ember.Handlebars.helpers.collection](/api/classes/Ember.Handlebars.helpers.html#method_collection)
21773
- for more details
21774
22562
 
21775
22563
  @class CollectionView
21776
22564
  @namespace Ember
@@ -22079,7 +22867,7 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
22079
22867
  ```html
22080
22868
  <!-- app-profile template -->
22081
22869
  <h1>{{person.title}}</h1>
22082
- <img {{bindAttr src=person.avatar}}>
22870
+ <img {{bind-attr src=person.avatar}}>
22083
22871
  <p class='signature'>{{person.signature}}</p>
22084
22872
  ```
22085
22873
 
@@ -22101,7 +22889,7 @@ var get = Ember.get, set = Ember.set, isNone = Ember.isNone;
22101
22889
  If you want to customize the component, in order to
22102
22890
  handle events or actions, you implement a subclass
22103
22891
  of `Ember.Component` named after the name of the
22104
- component. Note that `Component` needs to be appended to the name of
22892
+ component. Note that `Component` needs to be appended to the name of
22105
22893
  your subclass like `AppProfileComponent`.
22106
22894
 
22107
22895
  For example, you could implement the action
@@ -22163,6 +22951,7 @@ Ember.Component = Ember.View.extend(Ember.TargetActionSupport, {
22163
22951
  view.appendChild(Ember.View, {
22164
22952
  isVirtual: true,
22165
22953
  tagName: '',
22954
+ _contextView: parentView,
22166
22955
  template: get(this, 'template'),
22167
22956
  context: get(parentView, 'context'),
22168
22957
  controller: get(parentView, 'controller'),
@@ -22912,16 +23701,7 @@ function makeBindings(options) {
22912
23701
  @param {String} dependentKeys*
22913
23702
  */
22914
23703
  Ember.Handlebars.helper = function(name, value) {
22915
- if (Ember.Component.detect(value)) {
22916
- Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", name.match(/-/));
22917
-
22918
- var proto = value.proto();
22919
- if (!proto.layoutName && !proto.templateName) {
22920
- value.reopen({
22921
- layoutName: 'components/' + name
22922
- });
22923
- }
22924
- }
23704
+ Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", !Ember.Component.detect(value) || name.match(/-/));
22925
23705
 
22926
23706
  if (Ember.View.detect(value)) {
22927
23707
  Ember.Handlebars.registerHelper(name, function(options) {
@@ -23712,6 +24492,8 @@ function SimpleHandlebarsView(path, pathRoot, isEscaped, templateData) {
23712
24492
  this.morph = Metamorph();
23713
24493
  this.state = 'preRender';
23714
24494
  this.updateId = null;
24495
+ this._parentView = null;
24496
+ this.buffer = null;
23715
24497
  }
23716
24498
 
23717
24499
  Ember._SimpleHandlebarsView = SimpleHandlebarsView;
@@ -23725,7 +24507,11 @@ SimpleHandlebarsView.prototype = {
23725
24507
  Ember.run.cancel(this.updateId);
23726
24508
  this.updateId = null;
23727
24509
  }
24510
+ if (this._parentView) {
24511
+ this._parentView.removeChild(this);
24512
+ }
23728
24513
  this.morph = null;
24514
+ this.state = 'destroyed';
23729
24515
  },
23730
24516
 
23731
24517
  propertyWillChange: Ember.K,
@@ -23780,7 +24566,7 @@ SimpleHandlebarsView.prototype = {
23780
24566
  rerender: function() {
23781
24567
  switch(this.state) {
23782
24568
  case 'preRender':
23783
- case 'destroying':
24569
+ case 'destroyed':
23784
24570
  break;
23785
24571
  case 'inBuffer':
23786
24572
  throw new Ember.Error("Something you did tried to replace an {{expression}} before it was inserted into the DOM.");
@@ -25483,6 +26269,8 @@ GroupedEach.prototype = {
25483
26269
  },
25484
26270
 
25485
26271
  addArrayObservers: function() {
26272
+ if (!this.content) { return; }
26273
+
25486
26274
  this.content.addArrayObserver(this, {
25487
26275
  willChange: 'contentArrayWillChange',
25488
26276
  didChange: 'contentArrayDidChange'
@@ -25490,6 +26278,8 @@ GroupedEach.prototype = {
25490
26278
  },
25491
26279
 
25492
26280
  removeArrayObservers: function() {
26281
+ if (!this.content) { return; }
26282
+
25493
26283
  this.content.removeArrayObserver(this, {
25494
26284
  willChange: 'contentArrayWillChange',
25495
26285
  didChange: 'contentArrayDidChange'
@@ -25507,6 +26297,8 @@ GroupedEach.prototype = {
25507
26297
  },
25508
26298
 
25509
26299
  render: function() {
26300
+ if (!this.content) { return; }
26301
+
25510
26302
  var content = this.content,
25511
26303
  contentLength = get(content, 'length'),
25512
26304
  data = this.options.data,
@@ -25519,12 +26311,21 @@ GroupedEach.prototype = {
25519
26311
  },
25520
26312
 
25521
26313
  rerenderContainingView: function() {
25522
- Ember.run.scheduleOnce('render', this.containingView, 'rerender');
26314
+ var self = this;
26315
+ Ember.run.scheduleOnce('render', this, function() {
26316
+ // It's possible it's been destroyed after we enqueued a re-render call.
26317
+ if (!self.destroyed) {
26318
+ self.containingView.rerender();
26319
+ }
26320
+ });
25523
26321
  },
25524
26322
 
25525
26323
  destroy: function() {
25526
26324
  this.removeContentObservers();
25527
- this.removeArrayObservers();
26325
+ if (this.content) {
26326
+ this.removeArrayObservers();
26327
+ }
26328
+ this.destroyed = true;
25528
26329
  }
25529
26330
  };
25530
26331
 
@@ -25865,20 +26666,20 @@ var get = Ember.get, set = Ember.set;
25865
26666
  inserting the view's own rendered output at the `{{yield}}` location.
25866
26667
 
25867
26668
  An empty `<body>` and the following application code:
25868
-
26669
+
25869
26670
  ```javascript
25870
26671
  AView = Ember.View.extend({
25871
26672
  classNames: ['a-view-with-layout'],
25872
26673
  layout: Ember.Handlebars.compile('<div class="wrapper">{{yield}}</div>'),
25873
26674
  template: Ember.Handlebars.compile('<span>I am wrapped</span>')
25874
26675
  });
25875
-
26676
+
25876
26677
  aView = AView.create();
25877
26678
  aView.appendTo('body');
25878
26679
  ```
25879
-
26680
+
25880
26681
  Will result in the following HTML output:
25881
-
26682
+
25882
26683
  ```html
25883
26684
  <body>
25884
26685
  <div class='ember-view a-view-with-layout'>
@@ -25888,50 +26689,50 @@ var get = Ember.get, set = Ember.set;
25888
26689
  </div>
25889
26690
  </body>
25890
26691
  ```
25891
-
26692
+
25892
26693
  The `yield` helper cannot be used outside of a template assigned to an
25893
26694
  `Ember.View`'s `layout` property and will throw an error if attempted.
25894
-
26695
+
25895
26696
  ```javascript
25896
26697
  BView = Ember.View.extend({
25897
26698
  classNames: ['a-view-with-layout'],
25898
26699
  template: Ember.Handlebars.compile('{{yield}}')
25899
26700
  });
25900
-
26701
+
25901
26702
  bView = BView.create();
25902
26703
  bView.appendTo('body');
25903
-
26704
+
25904
26705
  // throws
25905
- // Uncaught Error: assertion failed:
26706
+ // Uncaught Error: assertion failed:
25906
26707
  // You called yield in a template that was not a layout
25907
26708
  ```
25908
26709
 
25909
26710
  ### Use with Ember.Component
25910
26711
  When designing components `{{yield}}` is used to denote where, inside the component's
25911
26712
  template, an optional block passed to the component should render:
25912
-
26713
+
25913
26714
  ```handlebars
25914
26715
  <!-- application.hbs -->
25915
26716
  {{#labeled-textfield value=someProperty}}
25916
26717
  First name:
25917
26718
  {{/my-component}}
25918
26719
  ```
25919
-
26720
+
25920
26721
  ```handlebars
25921
26722
  <!-- components/my-component.hbs -->
25922
26723
  <label>
25923
26724
  {{yield}} {{input value=value}}
25924
26725
  </label>
25925
26726
  ```
25926
-
26727
+
25927
26728
  Result:
25928
-
26729
+
25929
26730
  ```html
25930
26731
  <label>
25931
26732
  First name: <input type="text" />
25932
26733
  <label>
25933
26734
  ```
25934
-
26735
+
25935
26736
  @method yield
25936
26737
  @for Ember.Handlebars.helpers
25937
26738
  @param {Hash} options
@@ -25941,7 +26742,11 @@ Ember.Handlebars.registerHelper('yield', function(options) {
25941
26742
  var view = options.data.view;
25942
26743
 
25943
26744
  while (view && !get(view, 'layout')) {
25944
- view = get(view, 'parentView');
26745
+ if (view._contextView) {
26746
+ view = view._contextView;
26747
+ } else {
26748
+ view = get(view, 'parentView');
26749
+ }
25945
26750
  }
25946
26751
 
25947
26752
  Ember.assert("You called yield in a template that was not a layout", !!view);
@@ -26083,9 +26888,6 @@ Ember.TextSupport = Ember.Mixin.create({
26083
26888
  disabled: false,
26084
26889
  maxlength: null,
26085
26890
 
26086
- insertNewline: Ember.K,
26087
- cancel: Ember.K,
26088
-
26089
26891
  init: function() {
26090
26892
  this._super();
26091
26893
  this.on("focusOut", this, this._elementValueDidChange);
@@ -26096,99 +26898,6 @@ Ember.TextSupport = Ember.Mixin.create({
26096
26898
  this.on("keyUp", this, this.interpretKeyEvents);
26097
26899
  },
26098
26900
 
26099
- interpretKeyEvents: function(event) {
26100
- var map = Ember.TextSupport.KEY_EVENTS;
26101
- var method = map[event.keyCode];
26102
-
26103
- this._elementValueDidChange();
26104
- if (method) { return this[method](event); }
26105
- },
26106
-
26107
- _elementValueDidChange: function() {
26108
- set(this, 'value', this.$().val());
26109
- }
26110
-
26111
- });
26112
-
26113
- Ember.TextSupport.KEY_EVENTS = {
26114
- 13: 'insertNewline',
26115
- 27: 'cancel'
26116
- };
26117
-
26118
- })();
26119
-
26120
-
26121
-
26122
- (function() {
26123
- /**
26124
- @module ember
26125
- @submodule ember-handlebars
26126
- */
26127
-
26128
- var get = Ember.get, set = Ember.set;
26129
-
26130
- /**
26131
-
26132
- The internal class used to create text inputs when the `{{input}}`
26133
- helper is used with `type` of `text`.
26134
-
26135
- See Handlebars.helpers.input for usage details.
26136
-
26137
- ## Layout and LayoutName properties
26138
-
26139
- Because HTML `input` elements are self closing `layout` and `layoutName`
26140
- properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s
26141
- layout section for more information.
26142
-
26143
- @class TextField
26144
- @namespace Ember
26145
- @extends Ember.View
26146
- @uses Ember.TextSupport
26147
- */
26148
- Ember.TextField = Ember.View.extend(Ember.TextSupport,
26149
- /** @scope Ember.TextField.prototype */ {
26150
-
26151
- classNames: ['ember-text-field'],
26152
- tagName: "input",
26153
- attributeBindings: ['type', 'value', 'size', 'pattern', 'name'],
26154
-
26155
- /**
26156
- The `value` attribute of the input element. As the user inputs text, this
26157
- property is updated live.
26158
-
26159
- @property value
26160
- @type String
26161
- @default ""
26162
- */
26163
- value: "",
26164
-
26165
- /**
26166
- The `type` attribute of the input element.
26167
-
26168
- @property type
26169
- @type String
26170
- @default "text"
26171
- */
26172
- type: "text",
26173
-
26174
- /**
26175
- The `size` of the text field in characters.
26176
-
26177
- @property size
26178
- @type String
26179
- @default null
26180
- */
26181
- size: null,
26182
-
26183
- /**
26184
- The `pattern` the pattern attribute of input element.
26185
-
26186
- @property pattern
26187
- @type String
26188
- @default null
26189
- */
26190
- pattern: null,
26191
-
26192
26901
  /**
26193
26902
  The action to be sent when the user presses the return key.
26194
26903
 
@@ -26233,6 +26942,18 @@ Ember.TextField = Ember.View.extend(Ember.TextSupport,
26233
26942
  */
26234
26943
  bubbles: false,
26235
26944
 
26945
+ interpretKeyEvents: function(event) {
26946
+ var map = Ember.TextSupport.KEY_EVENTS;
26947
+ var method = map[event.keyCode];
26948
+
26949
+ this._elementValueDidChange();
26950
+ if (method) { return this[method](event); }
26951
+ },
26952
+
26953
+ _elementValueDidChange: function() {
26954
+ set(this, 'value', this.$().val());
26955
+ },
26956
+
26236
26957
  /**
26237
26958
  The action to be sent when the user inserts a new line.
26238
26959
 
@@ -26244,6 +26965,40 @@ Ember.TextField = Ember.View.extend(Ember.TextSupport,
26244
26965
  */
26245
26966
  insertNewline: function(event) {
26246
26967
  sendAction('enter', this, event);
26968
+ sendAction('insert-newline', this, event);
26969
+ },
26970
+
26971
+ /**
26972
+ Called when the user hits escape.
26973
+
26974
+ Called by the `Ember.TextSupport` mixin on keyUp if keycode matches 13.
26975
+ Uses sendAction to send the `enter` action to the controller.
26976
+
26977
+ @method cancel
26978
+ @param {Event} event
26979
+ */
26980
+ cancel: function(event) {
26981
+ sendAction('escape-press', this, event);
26982
+ },
26983
+
26984
+ /**
26985
+ Called when the text area is focused.
26986
+
26987
+ @method focusIn
26988
+ @param {Event} event
26989
+ */
26990
+ focusIn: function(event) {
26991
+ sendAction('focus-in', this, event);
26992
+ },
26993
+
26994
+ /**
26995
+ Called when the text area is blurred.
26996
+
26997
+ @method focusOut
26998
+ @param {Event} event
26999
+ */
27000
+ focusOut: function(event) {
27001
+ sendAction('focus-out', this, event);
26247
27002
  },
26248
27003
 
26249
27004
  /**
@@ -26256,22 +27011,35 @@ Ember.TextField = Ember.View.extend(Ember.TextSupport,
26256
27011
  @param {Event} event
26257
27012
  */
26258
27013
  keyPress: function(event) {
26259
- sendAction('keyPress', this, event);
27014
+ sendAction('key-press', this, event);
26260
27015
  }
27016
+
26261
27017
  });
26262
27018
 
27019
+ Ember.TextSupport.KEY_EVENTS = {
27020
+ 13: 'insertNewline',
27021
+ 27: 'cancel'
27022
+ };
27023
+
27024
+ // In principle, this shouldn't be necessary, but the legacy
27025
+ // sectionAction semantics for TextField are different from
27026
+ // the component semantics so this method normalizes them.
26263
27027
  function sendAction(eventName, view, event) {
26264
- var action = get(view, 'action'),
26265
- on = get(view, 'onEvent');
27028
+ var action = get(view, eventName),
27029
+ on = get(view, 'onEvent'),
27030
+ value = get(view, 'value');
26266
27031
 
26267
- if (action && on === eventName) {
26268
- var controller = get(view, 'controller'),
26269
- value = get(view, 'value'),
26270
- bubbles = get(view, 'bubbles');
27032
+ // back-compat support for keyPress as an event name even though
27033
+ // it's also a method name that consumes the event (and therefore
27034
+ // incompatible with sendAction semantics).
27035
+ if (on === eventName || (on === 'keyPress' && eventName === 'key-press')) {
27036
+ view.sendAction('action', value);
27037
+ }
26271
27038
 
26272
- controller.send(action, value, view);
27039
+ view.sendAction(eventName, value);
26273
27040
 
26274
- if (!bubbles) {
27041
+ if (action || on === eventName) {
27042
+ if(!get(view, 'bubbles')) {
26275
27043
  event.stopPropagation();
26276
27044
  }
26277
27045
  }
@@ -26281,6 +27049,81 @@ function sendAction(eventName, view, event) {
26281
27049
 
26282
27050
 
26283
27051
 
27052
+ (function() {
27053
+ /**
27054
+ @module ember
27055
+ @submodule ember-handlebars
27056
+ */
27057
+
27058
+ var get = Ember.get, set = Ember.set;
27059
+
27060
+ /**
27061
+
27062
+ The internal class used to create text inputs when the `{{input}}`
27063
+ helper is used with `type` of `text`.
27064
+
27065
+ See Handlebars.helpers.input for usage details.
27066
+
27067
+ ## Layout and LayoutName properties
27068
+
27069
+ Because HTML `input` elements are self closing `layout` and `layoutName`
27070
+ properties will not be applied. See [Ember.View](/api/classes/Ember.View.html)'s
27071
+ layout section for more information.
27072
+
27073
+ @class TextField
27074
+ @namespace Ember
27075
+ @extends Ember.View
27076
+ @uses Ember.TextSupport
27077
+ */
27078
+ Ember.TextField = Ember.Component.extend(Ember.TextSupport,
27079
+ /** @scope Ember.TextField.prototype */ {
27080
+
27081
+ classNames: ['ember-text-field'],
27082
+ tagName: "input",
27083
+ attributeBindings: ['type', 'value', 'size', 'pattern', 'name'],
27084
+
27085
+ /**
27086
+ The `value` attribute of the input element. As the user inputs text, this
27087
+ property is updated live.
27088
+
27089
+ @property value
27090
+ @type String
27091
+ @default ""
27092
+ */
27093
+ value: "",
27094
+
27095
+ /**
27096
+ The `type` attribute of the input element.
27097
+
27098
+ @property type
27099
+ @type String
27100
+ @default "text"
27101
+ */
27102
+ type: "text",
27103
+
27104
+ /**
27105
+ The `size` of the text field in characters.
27106
+
27107
+ @property size
27108
+ @type String
27109
+ @default null
27110
+ */
27111
+ size: null,
27112
+
27113
+ /**
27114
+ The `pattern` the pattern attribute of input element.
27115
+
27116
+ @property pattern
27117
+ @type String
27118
+ @default null
27119
+ */
27120
+ pattern: null
27121
+ });
27122
+
27123
+ })();
27124
+
27125
+
27126
+
26284
27127
  (function() {
26285
27128
  /*
26286
27129
  @module ember
@@ -26434,7 +27277,7 @@ var get = Ember.get, set = Ember.set;
26434
27277
  @extends Ember.View
26435
27278
  @uses Ember.TextSupport
26436
27279
  */
26437
- Ember.TextArea = Ember.View.extend(Ember.TextSupport, {
27280
+ Ember.TextArea = Ember.Component.extend(Ember.TextSupport, {
26438
27281
  classNames: ['ember-text-area'],
26439
27282
 
26440
27283
  tagName: "textarea",
@@ -28975,8 +29818,6 @@ define("router",
28975
29818
 
28976
29819
  var params = paramsForHandler(router, handlerName, objects);
28977
29820
 
28978
- transition.providedModelsArray = [];
28979
- transition.providedContexts = {};
28980
29821
  router.currentParams = params;
28981
29822
 
28982
29823
  var urlMethod = transition.urlMethod;
@@ -29593,6 +30434,7 @@ Ember.Router = Ember.Object.extend({
29593
30434
  if (passedName.charAt(0) === '/') {
29594
30435
  name = passedName;
29595
30436
  } else {
30437
+
29596
30438
  if (!this.router.hasRoute(passedName)) {
29597
30439
  name = args[0] = passedName + '.index';
29598
30440
  } else {
@@ -29611,6 +30453,8 @@ Ember.Router = Ember.Object.extend({
29611
30453
 
29612
30454
  transitionPromise.then(function(route) {
29613
30455
  self._transitionCompleted(route);
30456
+ }, function(error){
30457
+ Ember.assert("The URL '" + error.message + "' did match any routes in your application", error.name !== "UnrecognizedURLError");
29614
30458
  });
29615
30459
 
29616
30460
  // We want to return the configurable promise object
@@ -29672,7 +30516,7 @@ function triggerEvent(handlerInfos, ignoreFailure, args) {
29672
30516
  return;
29673
30517
  }
29674
30518
  } else if (handler.events && handler.events[name]) {
29675
- Ember.deprecate('Action handlers contained in an `events` object are deprecated in favor of putting them in an `actions` object', false);
30519
+ Ember.deprecate('Action handlers contained in an `events` object are deprecated in favor of putting them in an `actions` object (' + name + ' on ' + handler + ')', false);
29676
30520
  if (handler.events[name].apply(handler, args) === true) {
29677
30521
  eventWasHandled = true;
29678
30522
  } else {
@@ -29820,18 +30664,81 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
29820
30664
  this.send('playMusic');
29821
30665
  ```
29822
30666
 
30667
+ Within a route's action handler, the value of the `this` context
30668
+ is the Route object:
30669
+
30670
+ ```js
30671
+ App.SongRoute = Ember.Route.extend({
30672
+ actions: {
30673
+ myAction: function() {
30674
+ this.controllerFor("song");
30675
+ this.transitionTo("other.route");
30676
+ ...
30677
+ }
30678
+ }
30679
+ });
30680
+ ```
30681
+
29823
30682
  It is also possible to call `this._super()` from within an
29824
- action handler if it overrides a handle defined on a parent
29825
- class or mixin.
30683
+ action handler if it overrides a handler defined on a parent
30684
+ class or mixin:
29826
30685
 
29827
- Within a route's action handler, the value of the `this` context
29828
- is the Route object.
30686
+ Take for example the following routes:
30687
+
30688
+ ```js
30689
+ App.DebugRoute = Ember.Mixin.create({
30690
+ actions: {
30691
+ debugRouteInformation: function() {
30692
+ console.debug("trololo");
30693
+ }
30694
+ }
30695
+ });
30696
+
30697
+ App.AnnoyingDebugRoute = Ember.Route.extend(App.DebugRoute, {
30698
+ actions: {
30699
+ debugRouteInformation: function() {
30700
+ // also call the debugRouteInformation of mixed in App.DebugRoute
30701
+ this._super();
30702
+
30703
+ // show additional annoyance
30704
+ window.alert(...);
30705
+ }
30706
+ }
30707
+ });
30708
+ ```
29829
30709
 
29830
30710
  ## Bubbling
29831
30711
 
29832
30712
  By default, an action will stop bubbling once a handler defined
29833
30713
  on the `actions` hash handles it. To continue bubbling the action,
29834
- you must return `true` from the handler.
30714
+ you must return `true` from the handler:
30715
+
30716
+ ```js
30717
+ App.Router.map(function() {
30718
+ this.resource("album", function() {
30719
+ this.route("song");
30720
+ });
30721
+ });
30722
+
30723
+ App.AlbumRoute = Ember.Route.extend({
30724
+ actions: {
30725
+ startPlaying: function() {
30726
+ }
30727
+ }
30728
+ });
30729
+
30730
+ App.AlbumSongRoute = Ember.Route.extend({
30731
+ actions: {
30732
+ startPlaying: function() {
30733
+ // ...
30734
+
30735
+ if (actionShouldAlsoBeTriggeredOnParentRoute) {
30736
+ return true;
30737
+ }
30738
+ }
30739
+ }
30740
+ });
30741
+ ```
29835
30742
 
29836
30743
  ## Built-in actions
29837
30744
 
@@ -30299,13 +31206,49 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
30299
31206
  if (!name && sawParams) { return params; }
30300
31207
  else if (!name) { return; }
30301
31208
 
30302
- var modelClass = this.container.lookupFactory('model:' + name).superclass;
30303
- var namespace = get(this, 'router.namespace');
31209
+ return this.findModel(name, value);
31210
+ },
31211
+
31212
+ /**
30304
31213
 
30305
- Ember.assert("You used the dynamic segment " + name + "_id in your router, but " + namespace + "." + classify(name) + " did not exist and you did not override your route's `model` hook.", modelClass);
30306
- return modelClass.find(value);
31214
+ @method findModel
31215
+ @param {String} type the model type
31216
+ @param {Object} value the value passed to find
31217
+ */
31218
+ findModel: function(){
31219
+ var store = get(this, 'store');
31220
+ return store.find.apply(store, arguments);
30307
31221
  },
30308
31222
 
31223
+ /**
31224
+ Store property provides a hook for data persistence libraries to inject themselves.
31225
+
31226
+ By default, this store property provides the exact same functionality previously
31227
+ in the model hook.
31228
+
31229
+ Currently, the required interface is:
31230
+
31231
+ `store.find(modelName, findArguments)`
31232
+
31233
+ @method store
31234
+ @param {Object} store
31235
+ */
31236
+ store: Ember.computed(function(){
31237
+ var container = this.container;
31238
+ var routeName = this.routeName;
31239
+ var namespace = get(this, 'router.namespace');
31240
+
31241
+ return {
31242
+ find: function(name, value) {
31243
+ var modelClass = container.lookupFactory('model:' + name);
31244
+
31245
+ Ember.assert("You used the dynamic segment " + name + "_id in your route "+ routeName + ", but " + namespace + "." + classify(name) + " did not exist and you did not override your route's `model` hook.", modelClass);
31246
+
31247
+ return modelClass.find(value);
31248
+ }
31249
+ };
31250
+ }),
31251
+
30309
31252
  /**
30310
31253
  A hook you can implement to convert the route's model into parameters
30311
31254
  for the URL.
@@ -30328,8 +31271,10 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
30328
31271
  });
30329
31272
  ```
30330
31273
 
30331
- The default `serialize` method inserts the model's `id` into the
30332
- route's dynamic segment (in this case, `:post_id`).
31274
+ The default `serialize` method will insert the model's `id` into the
31275
+ route's dynamic segment (in this case, `:post_id`) if the segment contains '_id'.
31276
+ If the route has multiple dynamic segments or does not contain '_id', `serialize`
31277
+ will return `Ember.getProperties(model, params)`
30333
31278
 
30334
31279
  This method is called when `transitionTo` is called with a context
30335
31280
  in order to populate the URL.
@@ -30612,13 +31557,18 @@ Ember.Route = Ember.Object.extend(Ember.ActionHandler, {
30612
31557
  name = this.routeName;
30613
31558
  }
30614
31559
 
31560
+ options = options || {};
30615
31561
  name = name ? name.replace(/\//g, '.') : this.routeName;
30616
- var viewName = this.viewName || name;
31562
+ var viewName = options.view || this.viewName || name;
30617
31563
  var templateName = this.templateName || name;
30618
31564
 
30619
31565
  var container = this.container,
30620
31566
  view = container.lookup('view:' + viewName),
30621
- template = container.lookup('template:' + templateName);
31567
+ template = view ? view.get('template') : null;
31568
+
31569
+ if (!template) {
31570
+ template = container.lookup('template:' + templateName);
31571
+ }
30622
31572
 
30623
31573
  if (!view && !template) {
30624
31574
  Ember.assert("Could not find \"" + name + "\" template or view.", !namePassed);
@@ -30746,7 +31696,7 @@ function normalizeOptions(route, name, template, options) {
30746
31696
  } else if (namedController = route.container.lookup('controller:' + name)) {
30747
31697
  controller = namedController;
30748
31698
  } else {
30749
- controller = route.routeName;
31699
+ controller = route.controllerName || route.routeName;
30750
31700
  }
30751
31701
 
30752
31702
  if (typeof controller === 'string') {
@@ -30828,7 +31778,7 @@ Ember.onLoad('Ember.Handlebars', function() {
30828
31778
  handlebarsGet = Ember.Handlebars.get;
30829
31779
 
30830
31780
  function resolveParams(context, params, options) {
30831
- return resolvePaths(context, params, options).map(function(path, i) {
31781
+ return map.call(resolvePaths(context, params, options), function(path, i) {
30832
31782
  if (null === path) {
30833
31783
  // Param was string/number, not a path, so just return raw string/number.
30834
31784
  return params[i];
@@ -31075,7 +32025,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
31075
32025
  whenever the helpers
31076
32026
  */
31077
32027
  _paramsChanged: function() {
31078
- this.notifyPropertyChange('routeArgs');
32028
+ this.notifyPropertyChange('resolvedParams');
31079
32029
  },
31080
32030
 
31081
32031
  /**
@@ -31120,13 +32070,14 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
31120
32070
  var router = get(this, 'router'),
31121
32071
  routeArgs = get(this, 'routeArgs'),
31122
32072
  contexts = routeArgs.slice(1),
31123
- currentWhen = this.currentWhen || routeArgs[0],
32073
+ resolvedParams = get(this, 'resolvedParams'),
32074
+ currentWhen = this.currentWhen || resolvedParams[0],
31124
32075
  currentWithIndex = currentWhen + '.index',
31125
32076
  isActive = router.isActive.apply(router, [currentWhen].concat(contexts)) ||
31126
32077
  router.isActive.apply(router, [currentWithIndex].concat(contexts));
31127
32078
 
31128
32079
  if (isActive) { return get(this, 'activeClass'); }
31129
- }).property('routeArgs', 'router.url'),
32080
+ }).property('resolvedParams', 'routeArgs', 'router.url'),
31130
32081
 
31131
32082
  /**
31132
32083
  Accessed as a classname binding to apply the `LinkView`'s `loadingClass`
@@ -31185,6 +32136,23 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
31185
32136
  }
31186
32137
  },
31187
32138
 
32139
+ /**
32140
+ @private
32141
+
32142
+ Computed property that returns the resolved parameters.
32143
+
32144
+ @property
32145
+ @return {Array}
32146
+ */
32147
+ resolvedParams: Ember.computed(function() {
32148
+ var parameters = this.parameters,
32149
+ options = parameters.options,
32150
+ types = options.types,
32151
+ data = options.data;
32152
+
32153
+ return resolveParams(parameters.context, parameters.params, { types: types, data: data });
32154
+ }).property(),
32155
+
31188
32156
  /**
31189
32157
  @private
31190
32158
 
@@ -31196,11 +32164,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
31196
32164
  */
31197
32165
  routeArgs: Ember.computed(function() {
31198
32166
 
31199
- var parameters = this.parameters,
31200
- options = parameters.options,
31201
- types = options.types,
31202
- data = options.data,
31203
- resolvedParams = resolveParams(parameters.context, parameters.params, { types: types, data: data }),
32167
+ var resolvedParams = get(this, 'resolvedParams').slice(0),
31204
32168
  router = get(this, 'router'),
31205
32169
  namedRoute = resolvedParams[0];
31206
32170
 
@@ -31220,7 +32184,7 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
31220
32184
  }
31221
32185
 
31222
32186
  return resolvedParams;
31223
- }).property(),
32187
+ }).property('resolvedParams'),
31224
32188
 
31225
32189
  /**
31226
32190
  Sets the element's `href` attribute to the url for
@@ -32948,7 +33912,6 @@ Ember Routing
32948
33912
 
32949
33913
  @module ember
32950
33914
  @submodule ember-routing
32951
- @requires ember-states
32952
33915
  @requires ember-views
32953
33916
  */
32954
33917
 
@@ -34178,11 +35141,8 @@ Ember.runLoadHooks('Ember.Application', Ember.Application);
34178
35141
 
34179
35142
  var get = Ember.get, set = Ember.set;
34180
35143
 
34181
- function verifyDependencies(controller) {
34182
- var needs = get(controller, 'needs'),
34183
- container = get(controller, 'container'),
34184
- satisfied = true,
34185
- dependency, i, l;
35144
+ function verifyNeedsDependencies(controller, container, needs) {
35145
+ var dependency, i, l;
34186
35146
 
34187
35147
  for (i=0, l=needs.length; i<l; i++) {
34188
35148
  dependency = needs[i];
@@ -34190,26 +35150,11 @@ function verifyDependencies(controller) {
34190
35150
  dependency = "controller:" + dependency;
34191
35151
  }
34192
35152
 
35153
+ // Structure assert to still do verification but not string concat in production
34193
35154
  if (!container.has(dependency)) {
34194
- satisfied = false;
34195
- Ember.assert(controller + " needs " + dependency + " but it does not exist", false);
35155
+ Ember.assert(Ember.inspect(controller) + " needs " + dependency + " but it does not exist", false);
34196
35156
  }
34197
35157
  }
34198
-
34199
- if (l > 0) {
34200
- set(controller, 'controllers', {
34201
- unknownProperty: function(controllerName) {
34202
- var dependency, i, l;
34203
- for (i=0, l=needs.length; i<l; i++) {
34204
- dependency = needs[i];
34205
- if (dependency === controllerName) {
34206
- return container.lookup('controller:' + controllerName);
34207
- }
34208
- }
34209
- }
34210
- });
34211
- }
34212
- return satisfied;
34213
35158
  }
34214
35159
 
34215
35160
  /**
@@ -34248,12 +35193,17 @@ Ember.ControllerMixin.reopen({
34248
35193
  needs: [],
34249
35194
 
34250
35195
  init: function() {
34251
- this._super.apply(this, arguments);
35196
+ var needs = get(this, 'needs'),
35197
+ length = get(needs, 'length');
35198
+
35199
+ if (length > 0) {
35200
+ verifyNeedsDependencies(this, this.container, needs);
34252
35201
 
34253
- // Structure asserts to still do verification but not string concat in production
34254
- if (!verifyDependencies(this)) {
34255
- Ember.assert("Missing dependencies", false);
35202
+ // if needs then initialize controllers proxy
35203
+ get(this, 'controllers');
34256
35204
  }
35205
+
35206
+ this._super.apply(this, arguments);
34257
35207
  },
34258
35208
 
34259
35209
  controllerFor: function(controllerName) {
@@ -34263,7 +35213,7 @@ Ember.ControllerMixin.reopen({
34263
35213
 
34264
35214
  /**
34265
35215
  Stores the instances of other controllers available from within
34266
- this controller. Any controller listed by name in the `needs`
35216
+ this controller. Any controller listed by name in the `needs`
34267
35217
  property will be accessible by name through this property.
34268
35218
 
34269
35219
  ```javascript
@@ -34275,398 +35225,32 @@ Ember.ControllerMixin.reopen({
34275
35225
  }.property('controllers.post.title')
34276
35226
  });
34277
35227
  ```
34278
-
35228
+
34279
35229
  @see {Ember.ControllerMixin#needs}
34280
35230
  @property {Object} controllers
34281
35231
  @default null
34282
35232
  */
34283
- controllers: null
34284
- });
34285
-
34286
- })();
34287
-
34288
-
34289
-
34290
- (function() {
34291
-
34292
- })();
34293
-
34294
-
34295
-
34296
- (function() {
34297
- /**
34298
- Ember Application
34299
-
34300
- @module ember
34301
- @submodule ember-application
34302
- @requires ember-views, ember-states, ember-routing
34303
- */
34304
-
34305
- })();
34306
-
34307
- (function() {
34308
- var get = Ember.get, set = Ember.set;
34309
-
34310
- /**
34311
- @module ember
34312
- @submodule ember-states
34313
- */
34314
-
34315
- /**
34316
- The State class allows you to define individual states within a finite state machine
34317
- inside your Ember application.
34318
-
34319
- ### How States Work
34320
-
34321
- When you setup a finite state machine this means you are setting up a mechanism to precisely
34322
- manage the change within a system. You can control the various states or modes that your
34323
- application can be in at any given time. Additionally, you can manage what specific states
34324
- are allowed to transition to other states.
34325
-
34326
- The state machine is in only one state at a time. This state is known as the current state.
34327
- It is possible to change from one state to another by a triggering event or condition.
34328
- This is called a transition.
34329
-
34330
- Finite state machines are important because they allow the application developer to be
34331
- deterministic about the the sequence of events that can happen within a system. Some states
34332
- cannot be entered when the application is a given state.
34333
-
34334
- For example:
34335
-
34336
- A door that is in the `locked` state cannot be `opened` (you must transition to the `unlocked`
34337
- state first).
34338
-
34339
- A door that is in the `open` state cannot be `locked` (you must transition to the `closed`
34340
- state first).
34341
-
34342
-
34343
- Each state instance has the following characteristics:
34344
-
34345
- - Zero or more parent states
34346
- - A start state
34347
- - A name
34348
- - A path (a computed value that prefixes parent states and the complete hierarchy to itself )
34349
-
34350
- A state is known as a "leafState" when it is the last item on the path and has no children
34351
- beneath it.
34352
-
34353
- The isLeaf property returns a boolean.
34354
-
34355
- Each state can emit the following transition events
34356
-
34357
- - setup
34358
- - enter
34359
- - exit
34360
-
34361
- A state object is ususally created in the context of a state manager.
34362
-
34363
- ```javascript
34364
- doorStateManager = Ember.StateManager.create({
34365
- locked: Ember.State.create(),
34366
- closed: Ember.State.create(),
34367
- unlocked: Ember.State.create(),
34368
- open: Ember.State.create()
34369
- });
34370
- ```
34371
-
34372
- @class State
34373
- @namespace Ember
34374
- @extends Ember.Object
34375
- @uses Ember.Evented
34376
- */
34377
- Ember.State = Ember.Object.extend(Ember.Evented,
34378
- /** @scope Ember.State.prototype */{
34379
- /**
34380
- A reference to the parent state.
34381
-
34382
- @property parentState
34383
- @type Ember.State
34384
- */
34385
- parentState: null,
34386
- start: null,
34387
-
34388
- /**
34389
- The name of this state.
34390
-
34391
- @property name
34392
- @type String
34393
- */
34394
- name: null,
34395
-
34396
- /**
34397
- The full path to this state.
34398
-
34399
- @property path
34400
- @type String
34401
- */
34402
- path: Ember.computed(function() {
34403
- var parentPath = get(this, 'parentState.path'),
34404
- path = get(this, 'name');
34405
-
34406
- if (parentPath) {
34407
- path = parentPath + '.' + path;
34408
- }
34409
-
34410
- return path;
34411
- }),
34412
-
34413
- /**
34414
- @private
34415
-
34416
- Override the default event firing from `Ember.Evented` to
34417
- also call methods with the given name.
34418
-
34419
- @method trigger
34420
- @param name
34421
- */
34422
- trigger: function(name) {
34423
- if (this[name]) {
34424
- this[name].apply(this, [].slice.call(arguments, 1));
34425
- }
34426
- this._super.apply(this, arguments);
34427
- },
34428
-
34429
- /**
34430
- Initialize Ember.State object
34431
- Sets childStates to Ember.NativeArray
34432
- Sets eventTransitions to empty object unless already defined.
34433
- Loops over properties of this state and ensures that any property that
34434
- is an instance of Ember.State is moved to `states` hash.
34435
-
34436
-
34437
- @method init
34438
- */
34439
- init: function() {
34440
- var states = get(this, 'states');
34441
- set(this, 'childStates', Ember.A());
34442
- set(this, 'eventTransitions', get(this, 'eventTransitions') || {});
34443
-
34444
- var name, value, transitionTarget;
34445
-
34446
- // As a convenience, loop over the properties
34447
- // of this state and look for any that are other
34448
- // Ember.State instances or classes, and move them
34449
- // to the `states` hash. This avoids having to
34450
- // create an explicit separate hash.
34451
-
34452
- if (!states) {
34453
- states = {};
34454
-
34455
- for (name in this) {
34456
- if (name === "constructor") { continue; }
35233
+ controllers: Ember.computed(function() {
35234
+ var controller = this;
34457
35235
 
34458
- if (value = this[name]) {
34459
- if (transitionTarget = value.transitionTarget) {
34460
- this.eventTransitions[name] = transitionTarget;
35236
+ return {
35237
+ needs: get(controller, 'needs'),
35238
+ container: get(controller, 'container'),
35239
+ unknownProperty: function(controllerName) {
35240
+ var needs = this.needs,
35241
+ dependency, i, l;
35242
+ for (i=0, l=needs.length; i<l; i++) {
35243
+ dependency = needs[i];
35244
+ if (dependency === controllerName) {
35245
+ return this.container.lookup('controller:' + controllerName);
34461
35246
  }
34462
-
34463
- this.setupChild(states, name, value);
34464
35247
  }
34465
- }
34466
35248
 
34467
- set(this, 'states', states);
34468
- } else {
34469
- for (name in states) {
34470
- this.setupChild(states, name, states[name]);
35249
+ var errorMessage = Ember.inspect(controller) + '#needs does not include `' + controllerName + '`. To access the ' + controllerName + ' controller from ' + Ember.inspect(controller) + ', ' + Ember.inspect(controller) + ' should have a `needs` property that is an array of the controllers it has access to.';
35250
+ throw new ReferenceError(errorMessage);
34471
35251
  }
34472
- }
34473
-
34474
- // pathsCaches is a nested hash of the form:
34475
- // pathsCaches[stateManagerTypeGuid][path] == transitions_hash
34476
- set(this, 'pathsCaches', {});
34477
- },
34478
-
34479
- /**
34480
- Sets a cached instance of the state. Ember.guidFor is used
34481
- to find the guid of the associated state manager. If a cache can be found
34482
- the state path is added to that cache, otherwise an empty JavaScript object
34483
- is created. And the state path is appended to that instead.
34484
-
34485
- @method setPathsCache
34486
- @param stateManager
34487
- @param path
34488
- @param transitions
34489
- */
34490
- setPathsCache: function(stateManager, path, transitions) {
34491
- var stateManagerTypeGuid = Ember.guidFor(stateManager.constructor),
34492
- pathsCaches = get(this, 'pathsCaches'),
34493
- pathsCacheForManager = pathsCaches[stateManagerTypeGuid] || {};
34494
-
34495
- pathsCacheForManager[path] = transitions;
34496
- pathsCaches[stateManagerTypeGuid] = pathsCacheForManager;
34497
- },
34498
-
34499
- /**
34500
- Returns a cached path for the state instance. Each state manager
34501
- has a GUID and this is used to look up a cached path if it has already
34502
- been created. If a cached path is not found an empty JavaScript object
34503
- is returned instead.
34504
-
34505
- @method getPathsCache
34506
- @param stateManager
34507
- @param path
34508
- */
34509
- getPathsCache: function(stateManager, path) {
34510
- var stateManagerTypeGuid = Ember.guidFor(stateManager.constructor),
34511
- pathsCaches = get(this, 'pathsCaches'),
34512
- pathsCacheForManager = pathsCaches[stateManagerTypeGuid] || {};
34513
-
34514
- return pathsCacheForManager[path];
34515
- },
34516
-
34517
- /**
34518
- @private
34519
-
34520
- Create the child instance and ensure that it is an instance of Ember.State
34521
-
34522
- @method setupChild
34523
- @param states
34524
- @param name
34525
- @param value
34526
- */
34527
- setupChild: function(states, name, value) {
34528
- if (!value) { return false; }
34529
- var instance;
34530
-
34531
- if (value instanceof Ember.State) {
34532
- set(value, 'name', name);
34533
- instance = value;
34534
- instance.container = this.container;
34535
- } else if (Ember.State.detect(value)) {
34536
- instance = value.create({
34537
- name: name,
34538
- container: this.container
34539
- });
34540
- }
34541
-
34542
- if (instance instanceof Ember.State) {
34543
- set(instance, 'parentState', this);
34544
- get(this, 'childStates').pushObject(instance);
34545
- states[name] = instance;
34546
- return instance;
34547
- }
34548
- },
34549
-
34550
- /**
34551
- @private
34552
-
34553
- @method lookupEventTransition
34554
- @param name
34555
- */
34556
- lookupEventTransition: function(name) {
34557
- var path, state = this;
34558
-
34559
- while(state && !path) {
34560
- path = state.eventTransitions[name];
34561
- state = state.get('parentState');
34562
- }
34563
-
34564
- return path;
34565
- },
34566
-
34567
- /**
34568
- A Boolean value indicating whether the state is a leaf state
34569
- in the state hierarchy. This is `false` if the state has child
34570
- states; otherwise it is true.
34571
-
34572
- @property isLeaf
34573
- @type Boolean
34574
- */
34575
- isLeaf: Ember.computed(function() {
34576
- return !get(this, 'childStates').length;
34577
- }),
34578
-
34579
- /**
34580
- A boolean value indicating whether the state takes a context.
34581
- By default we assume all states take contexts.
34582
-
34583
- @property hasContext
34584
- @default true
34585
- */
34586
- hasContext: true,
34587
-
34588
- /**
34589
- This is the default transition event.
34590
-
34591
- @event setup
34592
- @param {Ember.StateManager} manager
34593
- @param context
34594
- @see Ember.StateManager#transitionEvent
34595
- */
34596
- setup: Ember.K,
34597
-
34598
- /**
34599
- This event fires when the state is entered.
34600
-
34601
- @event enter
34602
- @param {Ember.StateManager} manager
34603
- */
34604
- enter: Ember.K,
34605
-
34606
- /**
34607
- This event fires when the state is exited.
34608
-
34609
- @event exit
34610
- @param {Ember.StateManager} manager
34611
- */
34612
- exit: Ember.K
34613
- });
34614
-
34615
- Ember.State.reopenClass({
34616
-
34617
- /**
34618
- Creates an action function for transitioning to the named state while
34619
- preserving context.
34620
-
34621
- The following example StateManagers are equivalent:
34622
-
34623
- ```javascript
34624
- aManager = Ember.StateManager.create({
34625
- stateOne: Ember.State.create({
34626
- changeToStateTwo: Ember.State.transitionTo('stateTwo')
34627
- }),
34628
- stateTwo: Ember.State.create({})
34629
- })
34630
-
34631
- bManager = Ember.StateManager.create({
34632
- stateOne: Ember.State.create({
34633
- changeToStateTwo: function(manager, context) {
34634
- manager.transitionTo('stateTwo', context)
34635
- }
34636
- }),
34637
- stateTwo: Ember.State.create({})
34638
- })
34639
- ```
34640
-
34641
- @method transitionTo
34642
- @static
34643
- @param {String} target
34644
- */
34645
-
34646
- transitionTo: function(target) {
34647
-
34648
- var transitionFunction = function(stateManager, contextOrEvent) {
34649
- var contexts = [],
34650
- Event = Ember.$ && Ember.$.Event;
34651
-
34652
- if (contextOrEvent && (Event && contextOrEvent instanceof Event)) {
34653
- if (contextOrEvent.hasOwnProperty('contexts')) {
34654
- contexts = contextOrEvent.contexts.slice();
34655
- }
34656
- }
34657
- else {
34658
- contexts = [].slice.call(arguments, 1);
34659
- }
34660
-
34661
- contexts.unshift(target);
34662
- stateManager.transitionTo.apply(stateManager, contexts);
34663
35252
  };
34664
-
34665
- transitionFunction.transitionTarget = target;
34666
-
34667
- return transitionFunction;
34668
- }
34669
-
35253
+ }).readOnly()
34670
35254
  });
34671
35255
 
34672
35256
  })();
@@ -34674,1104 +35258,6 @@ Ember.State.reopenClass({
34674
35258
 
34675
35259
 
34676
35260
  (function() {
34677
- /**
34678
- @module ember
34679
- @submodule ember-states
34680
- */
34681
-
34682
- var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
34683
- var arrayForEach = Ember.ArrayPolyfills.forEach;
34684
- /**
34685
- A Transition takes the enter, exit and resolve states and normalizes
34686
- them:
34687
-
34688
- * takes any passed in contexts into consideration
34689
- * adds in `initialState`s
34690
-
34691
- @class Transition
34692
- @private
34693
- */
34694
- var Transition = function(raw) {
34695
- this.enterStates = raw.enterStates.slice();
34696
- this.exitStates = raw.exitStates.slice();
34697
- this.resolveState = raw.resolveState;
34698
-
34699
- this.finalState = raw.enterStates[raw.enterStates.length - 1] || raw.resolveState;
34700
- };
34701
-
34702
- Transition.prototype = {
34703
- /**
34704
- Normalize the passed in enter, exit and resolve states.
34705
-
34706
- This process also adds `finalState` and `contexts` to the Transition object.
34707
-
34708
- @method normalize
34709
- @param {Ember.StateManager} manager the state manager running the transition
34710
- @param {Array} contexts a list of contexts passed into `transitionTo`
34711
- */
34712
- normalize: function(manager, contexts) {
34713
- this.matchContextsToStates(contexts);
34714
- this.addInitialStates();
34715
- this.removeUnchangedContexts(manager);
34716
- return this;
34717
- },
34718
-
34719
- /**
34720
- Match each of the contexts passed to `transitionTo` to a state.
34721
- This process may also require adding additional enter and exit
34722
- states if there are more contexts than enter states.
34723
-
34724
- @method matchContextsToStates
34725
- @param {Array} contexts a list of contexts passed into `transitionTo`
34726
- */
34727
- matchContextsToStates: function(contexts) {
34728
- var stateIdx = this.enterStates.length - 1,
34729
- matchedContexts = [],
34730
- state,
34731
- context;
34732
-
34733
- // Next, we will match the passed in contexts to the states they
34734
- // represent.
34735
- //
34736
- // First, assign a context to each enter state in reverse order. If
34737
- // any contexts are left, add a parent state to the list of states
34738
- // to enter and exit, and assign a context to the parent state.
34739
- //
34740
- // If there are still contexts left when the state manager is
34741
- // reached, raise an exception.
34742
- //
34743
- // This allows the following:
34744
- //
34745
- // |- root
34746
- // | |- post
34747
- // | | |- comments
34748
- // | |- about (* current state)
34749
- //
34750
- // For `transitionTo('post.comments', post, post.get('comments')`,
34751
- // the first context (`post`) will be assigned to `root.post`, and
34752
- // the second context (`post.get('comments')`) will be assigned
34753
- // to `root.post.comments`.
34754
- //
34755
- // For the following:
34756
- //
34757
- // |- root
34758
- // | |- post
34759
- // | | |- index (* current state)
34760
- // | | |- comments
34761
- //
34762
- // For `transitionTo('post.comments', otherPost, otherPost.get('comments')`,
34763
- // the `<root.post>` state will be added to the list of enter and exit
34764
- // states because its context has changed.
34765
-
34766
- while (contexts.length > 0) {
34767
- if (stateIdx >= 0) {
34768
- state = this.enterStates[stateIdx--];
34769
- } else {
34770
- if (this.enterStates.length) {
34771
- state = get(this.enterStates[0], 'parentState');
34772
- if (!state) { throw "Cannot match all contexts to states"; }
34773
- } else {
34774
- // If re-entering the current state with a context, the resolve
34775
- // state will be the current state.
34776
- state = this.resolveState;
34777
- }
34778
-
34779
- this.enterStates.unshift(state);
34780
- this.exitStates.unshift(state);
34781
- }
34782
-
34783
- // in routers, only states with dynamic segments have a context
34784
- if (get(state, 'hasContext')) {
34785
- context = contexts.pop();
34786
- } else {
34787
- context = null;
34788
- }
34789
-
34790
- matchedContexts.unshift(context);
34791
- }
34792
-
34793
- this.contexts = matchedContexts;
34794
- },
34795
-
34796
- /**
34797
- Add any `initialState`s to the list of enter states.
34798
-
34799
- @method addInitialStates
34800
- */
34801
- addInitialStates: function() {
34802
- var finalState = this.finalState, initialState;
34803
-
34804
- while(true) {
34805
- initialState = get(finalState, 'initialState') || 'start';
34806
- finalState = get(finalState, 'states.' + initialState);
34807
-
34808
- if (!finalState) { break; }
34809
-
34810
- this.finalState = finalState;
34811
- this.enterStates.push(finalState);
34812
- this.contexts.push(undefined);
34813
- }
34814
- },
34815
-
34816
- /**
34817
- Remove any states that were added because the number of contexts
34818
- exceeded the number of explicit enter states, but the context has
34819
- not changed since the last time the state was entered.
34820
-
34821
- @method removeUnchangedContexts
34822
- @param {Ember.StateManager} manager passed in to look up the last
34823
- context for a state
34824
- */
34825
- removeUnchangedContexts: function(manager) {
34826
- // Start from the beginning of the enter states. If the state was added
34827
- // to the list during the context matching phase, make sure the context
34828
- // has actually changed since the last time the state was entered.
34829
- while (this.enterStates.length > 0) {
34830
- if (this.enterStates[0] !== this.exitStates[0]) { break; }
34831
-
34832
- if (this.enterStates.length === this.contexts.length) {
34833
- if (manager.getStateMeta(this.enterStates[0], 'context') !== this.contexts[0]) { break; }
34834
- this.contexts.shift();
34835
- }
34836
-
34837
- this.resolveState = this.enterStates.shift();
34838
- this.exitStates.shift();
34839
- }
34840
- }
34841
- };
34842
-
34843
-
34844
- /**
34845
- Sends the event to the currentState, if the event is not handled this method
34846
- will proceed to call the parentState recursively until it encounters an
34847
- event handler or reaches the top or root of the state path hierarchy.
34848
-
34849
- @method sendRecursively
34850
- @param event
34851
- @param currentState
34852
- @param isUnhandledPass
34853
- */
34854
- var sendRecursively = function(event, currentState, isUnhandledPass) {
34855
- var log = this.enableLogging,
34856
- eventName = isUnhandledPass ? 'unhandledEvent' : event,
34857
- action = currentState[eventName],
34858
- contexts, sendRecursiveArguments, actionArguments;
34859
-
34860
- contexts = [].slice.call(arguments, 3);
34861
-
34862
- // Test to see if the action is a method that
34863
- // can be invoked. Don't blindly check just for
34864
- // existence, because it is possible the state
34865
- // manager has a child state of the given name,
34866
- // and we should still raise an exception in that
34867
- // case.
34868
- if (typeof action === 'function') {
34869
- if (log) {
34870
- if (isUnhandledPass) {
34871
- Ember.Logger.log(fmt("STATEMANAGER: Unhandled event '%@' being sent to state %@.", [event, get(currentState, 'path')]));
34872
- } else {
34873
- Ember.Logger.log(fmt("STATEMANAGER: Sending event '%@' to state %@.", [event, get(currentState, 'path')]));
34874
- }
34875
- }
34876
-
34877
- actionArguments = contexts;
34878
- if (isUnhandledPass) {
34879
- actionArguments.unshift(event);
34880
- }
34881
- actionArguments.unshift(this);
34882
-
34883
- return action.apply(currentState, actionArguments);
34884
- } else {
34885
- var parentState = get(currentState, 'parentState');
34886
- if (parentState) {
34887
-
34888
- sendRecursiveArguments = contexts;
34889
- sendRecursiveArguments.unshift(event, parentState, isUnhandledPass);
34890
-
34891
- return sendRecursively.apply(this, sendRecursiveArguments);
34892
- } else if (!isUnhandledPass) {
34893
- return sendEvent.call(this, event, contexts, true);
34894
- }
34895
- }
34896
- };
34897
-
34898
- /**
34899
- Send an event to the currentState.
34900
-
34901
- @method sendEvent
34902
- @param eventName
34903
- @param sendRecursiveArguments
34904
- @param isUnhandledPass
34905
- */
34906
- var sendEvent = function(eventName, sendRecursiveArguments, isUnhandledPass) {
34907
- sendRecursiveArguments.unshift(eventName, get(this, 'currentState'), isUnhandledPass);
34908
- return sendRecursively.apply(this, sendRecursiveArguments);
34909
- };
34910
-
34911
- /**
34912
- StateManager is part of Ember's implementation of a finite state machine. A
34913
- StateManager instance manages a number of properties that are instances of
34914
- `Ember.State`,
34915
- tracks the current active state, and triggers callbacks when states have changed.
34916
-
34917
- ## Defining States
34918
-
34919
- The states of StateManager can be declared in one of two ways. First, you can
34920
- define a `states` property that contains all the states:
34921
-
34922
- ```javascript
34923
- var managerA = Ember.StateManager.create({
34924
- states: {
34925
- stateOne: Ember.State.create(),
34926
- stateTwo: Ember.State.create()
34927
- }
34928
- });
34929
-
34930
- managerA.get('states');
34931
- // {
34932
- // stateOne: Ember.State.create(),
34933
- // stateTwo: Ember.State.create()
34934
- // }
34935
- ```
34936
-
34937
- You can also add instances of `Ember.State` (or an `Ember.State` subclass)
34938
- directly as properties of a StateManager. These states will be collected into
34939
- the `states` property for you.
34940
-
34941
- ```javascript
34942
- var managerA = Ember.StateManager.create({
34943
- stateOne: Ember.State.create(),
34944
- stateTwo: Ember.State.create()
34945
- });
34946
-
34947
- managerA.get('states');
34948
- // {
34949
- // stateOne: Ember.State.create(),
34950
- // stateTwo: Ember.State.create()
34951
- // }
34952
- ```
34953
-
34954
- ## The Initial State
34955
-
34956
- When created, a StateManager instance will immediately enter into the state
34957
- defined as its `start` property or the state referenced by name in its
34958
- `initialState` property:
34959
-
34960
- ```javascript
34961
- var managerA = Ember.StateManager.create({
34962
- start: Ember.State.create({})
34963
- });
34964
-
34965
- managerA.get('currentState.name'); // 'start'
34966
-
34967
- var managerB = Ember.StateManager.create({
34968
- initialState: 'beginHere',
34969
- beginHere: Ember.State.create({})
34970
- });
34971
-
34972
- managerB.get('currentState.name'); // 'beginHere'
34973
- ```
34974
-
34975
- Because it is a property you may also provide a computed function if you wish
34976
- to derive an `initialState` programmatically:
34977
-
34978
- ```javascript
34979
- var managerC = Ember.StateManager.create({
34980
- initialState: function() {
34981
- if (someLogic) {
34982
- return 'active';
34983
- } else {
34984
- return 'passive';
34985
- }
34986
- }.property(),
34987
- active: Ember.State.create({}),
34988
- passive: Ember.State.create({})
34989
- });
34990
- ```
34991
-
34992
- ## Moving Between States
34993
-
34994
- A StateManager can have any number of `Ember.State` objects as properties
34995
- and can have a single one of these states as its current state.
34996
-
34997
- Calling `transitionTo` transitions between states:
34998
-
34999
- ```javascript
35000
- var robotManager = Ember.StateManager.create({
35001
- initialState: 'poweredDown',
35002
- poweredDown: Ember.State.create({}),
35003
- poweredUp: Ember.State.create({})
35004
- });
35005
-
35006
- robotManager.get('currentState.name'); // 'poweredDown'
35007
- robotManager.transitionTo('poweredUp');
35008
- robotManager.get('currentState.name'); // 'poweredUp'
35009
- ```
35010
-
35011
- Before transitioning into a new state the existing `currentState` will have
35012
- its `exit` method called with the StateManager instance as its first argument
35013
- and an object representing the transition as its second argument.
35014
-
35015
- After transitioning into a new state the new `currentState` will have its
35016
- `enter` method called with the StateManager instance as its first argument
35017
- and an object representing the transition as its second argument.
35018
-
35019
- ```javascript
35020
- var robotManager = Ember.StateManager.create({
35021
- initialState: 'poweredDown',
35022
- poweredDown: Ember.State.create({
35023
- exit: function(stateManager) {
35024
- console.log("exiting the poweredDown state")
35025
- }
35026
- }),
35027
- poweredUp: Ember.State.create({
35028
- enter: function(stateManager) {
35029
- console.log("entering the poweredUp state. Destroy all humans.")
35030
- }
35031
- })
35032
- });
35033
-
35034
- robotManager.get('currentState.name'); // 'poweredDown'
35035
- robotManager.transitionTo('poweredUp');
35036
-
35037
- // will log
35038
- // 'exiting the poweredDown state'
35039
- // 'entering the poweredUp state. Destroy all humans.'
35040
- ```
35041
-
35042
- Once a StateManager is already in a state, subsequent attempts to enter that
35043
- state will not trigger enter or exit method calls. Attempts to transition
35044
- into a state that the manager does not have will result in no changes in the
35045
- StateManager's current state:
35046
-
35047
- ```javascript
35048
- var robotManager = Ember.StateManager.create({
35049
- initialState: 'poweredDown',
35050
- poweredDown: Ember.State.create({
35051
- exit: function(stateManager) {
35052
- console.log("exiting the poweredDown state")
35053
- }
35054
- }),
35055
- poweredUp: Ember.State.create({
35056
- enter: function(stateManager) {
35057
- console.log("entering the poweredUp state. Destroy all humans.")
35058
- }
35059
- })
35060
- });
35061
-
35062
- robotManager.get('currentState.name'); // 'poweredDown'
35063
- robotManager.transitionTo('poweredUp');
35064
- // will log
35065
- // 'exiting the poweredDown state'
35066
- // 'entering the poweredUp state. Destroy all humans.'
35067
- robotManager.transitionTo('poweredUp'); // no logging, no state change
35068
-
35069
- robotManager.transitionTo('someUnknownState'); // silently fails
35070
- robotManager.get('currentState.name'); // 'poweredUp'
35071
- ```
35072
-
35073
- Each state property may itself contain properties that are instances of
35074
- `Ember.State`. The StateManager can transition to specific sub-states in a
35075
- series of transitionTo method calls or via a single transitionTo with the
35076
- full path to the specific state. The StateManager will also keep track of the
35077
- full path to its currentState
35078
-
35079
- ```javascript
35080
- var robotManager = Ember.StateManager.create({
35081
- initialState: 'poweredDown',
35082
- poweredDown: Ember.State.create({
35083
- charging: Ember.State.create(),
35084
- charged: Ember.State.create()
35085
- }),
35086
- poweredUp: Ember.State.create({
35087
- mobile: Ember.State.create(),
35088
- stationary: Ember.State.create()
35089
- })
35090
- });
35091
-
35092
- robotManager.get('currentState.name'); // 'poweredDown'
35093
-
35094
- robotManager.transitionTo('poweredUp');
35095
- robotManager.get('currentState.name'); // 'poweredUp'
35096
-
35097
- robotManager.transitionTo('mobile');
35098
- robotManager.get('currentState.name'); // 'mobile'
35099
-
35100
- // transition via a state path
35101
- robotManager.transitionTo('poweredDown.charging');
35102
- robotManager.get('currentState.name'); // 'charging'
35103
-
35104
- robotManager.get('currentState.path'); // 'poweredDown.charging'
35105
- ```
35106
-
35107
- Enter transition methods will be called for each state and nested child state
35108
- in their hierarchical order. Exit methods will be called for each state and
35109
- its nested states in reverse hierarchical order.
35110
-
35111
- Exit transitions for a parent state are not called when entering into one of
35112
- its child states, only when transitioning to a new section of possible states
35113
- in the hierarchy.
35114
-
35115
- ```javascript
35116
- var robotManager = Ember.StateManager.create({
35117
- initialState: 'poweredDown',
35118
- poweredDown: Ember.State.create({
35119
- enter: function() {},
35120
- exit: function() {
35121
- console.log("exited poweredDown state")
35122
- },
35123
- charging: Ember.State.create({
35124
- enter: function() {},
35125
- exit: function() {}
35126
- }),
35127
- charged: Ember.State.create({
35128
- enter: function() {
35129
- console.log("entered charged state")
35130
- },
35131
- exit: function() {
35132
- console.log("exited charged state")
35133
- }
35134
- })
35135
- }),
35136
- poweredUp: Ember.State.create({
35137
- enter: function() {
35138
- console.log("entered poweredUp state")
35139
- },
35140
- exit: function() {},
35141
- mobile: Ember.State.create({
35142
- enter: function() {
35143
- console.log("entered mobile state")
35144
- },
35145
- exit: function() {}
35146
- }),
35147
- stationary: Ember.State.create({
35148
- enter: function() {},
35149
- exit: function() {}
35150
- })
35151
- })
35152
- });
35153
-
35154
-
35155
- robotManager.get('currentState.path'); // 'poweredDown'
35156
- robotManager.transitionTo('charged');
35157
- // logs 'entered charged state'
35158
- // but does *not* log 'exited poweredDown state'
35159
- robotManager.get('currentState.name'); // 'charged
35160
-
35161
- robotManager.transitionTo('poweredUp.mobile');
35162
- // logs
35163
- // 'exited charged state'
35164
- // 'exited poweredDown state'
35165
- // 'entered poweredUp state'
35166
- // 'entered mobile state'
35167
- ```
35168
-
35169
- During development you can set a StateManager's `enableLogging` property to
35170
- `true` to receive console messages of state transitions.
35171
-
35172
- ```javascript
35173
- var robotManager = Ember.StateManager.create({
35174
- enableLogging: true
35175
- });
35176
- ```
35177
-
35178
- ## Managing currentState with Actions
35179
-
35180
- To control which transitions are possible for a given state, and
35181
- appropriately handle external events, the StateManager can receive and
35182
- route action messages to its states via the `send` method. Calling to
35183
- `send` with an action name will begin searching for a method with the same
35184
- name starting at the current state and moving up through the parent states
35185
- in a state hierarchy until an appropriate method is found or the StateManager
35186
- instance itself is reached.
35187
-
35188
- If an appropriately named method is found it will be called with the state
35189
- manager as the first argument and an optional `context` object as the second
35190
- argument.
35191
-
35192
- ```javascript
35193
- var managerA = Ember.StateManager.create({
35194
- initialState: 'stateOne.substateOne.subsubstateOne',
35195
- stateOne: Ember.State.create({
35196
- substateOne: Ember.State.create({
35197
- anAction: function(manager, context) {
35198
- console.log("an action was called")
35199
- },
35200
- subsubstateOne: Ember.State.create({})
35201
- })
35202
- })
35203
- });
35204
-
35205
- managerA.get('currentState.name'); // 'subsubstateOne'
35206
- managerA.send('anAction');
35207
- // 'stateOne.substateOne.subsubstateOne' has no anAction method
35208
- // so the 'anAction' method of 'stateOne.substateOne' is called
35209
- // and logs "an action was called"
35210
- // with managerA as the first argument
35211
- // and no second argument
35212
-
35213
- var someObject = {};
35214
- managerA.send('anAction', someObject);
35215
- // the 'anAction' method of 'stateOne.substateOne' is called again
35216
- // with managerA as the first argument and
35217
- // someObject as the second argument.
35218
- ```
35219
-
35220
- If the StateManager attempts to send an action but does not find an appropriately named
35221
- method in the current state or while moving upwards through the state hierarchy, it will
35222
- repeat the process looking for a `unhandledEvent` method. If an `unhandledEvent` method is
35223
- found, it will be called with the original event name as the second argument. If an
35224
- `unhandledEvent` method is not found, the StateManager will throw a new Ember.Error.
35225
-
35226
- ```javascript
35227
- var managerB = Ember.StateManager.create({
35228
- initialState: 'stateOne.substateOne.subsubstateOne',
35229
- stateOne: Ember.State.create({
35230
- substateOne: Ember.State.create({
35231
- subsubstateOne: Ember.State.create({}),
35232
- unhandledEvent: function(manager, eventName, context) {
35233
- console.log("got an unhandledEvent with name " + eventName);
35234
- }
35235
- })
35236
- })
35237
- });
35238
-
35239
- managerB.get('currentState.name'); // 'subsubstateOne'
35240
- managerB.send('anAction');
35241
- // neither `stateOne.substateOne.subsubstateOne` nor any of it's
35242
- // parent states have a handler for `anAction`. `subsubstateOne`
35243
- // also does not have a `unhandledEvent` method, but its parent
35244
- // state, `substateOne`, does, and it gets fired. It will log
35245
- // "got an unhandledEvent with name anAction"
35246
- ```
35247
-
35248
- Action detection only moves upwards through the state hierarchy from the current state.
35249
- It does not search in other portions of the hierarchy.
35250
-
35251
- ```javascript
35252
- var managerC = Ember.StateManager.create({
35253
- initialState: 'stateOne.substateOne.subsubstateOne',
35254
- stateOne: Ember.State.create({
35255
- substateOne: Ember.State.create({
35256
- subsubstateOne: Ember.State.create({})
35257
- })
35258
- }),
35259
- stateTwo: Ember.State.create({
35260
- anAction: function(manager, context) {
35261
- // will not be called below because it is
35262
- // not a parent of the current state
35263
- }
35264
- })
35265
- });
35266
-
35267
- managerC.get('currentState.name'); // 'subsubstateOne'
35268
- managerC.send('anAction');
35269
- // Error: <Ember.StateManager:ember132> could not
35270
- // respond to event anAction in state stateOne.substateOne.subsubstateOne.
35271
- ```
35272
-
35273
- Inside of an action method the given state should delegate `transitionTo` calls on its
35274
- StateManager.
35275
-
35276
- ```javascript
35277
- var robotManager = Ember.StateManager.create({
35278
- initialState: 'poweredDown.charging',
35279
- poweredDown: Ember.State.create({
35280
- charging: Ember.State.create({
35281
- chargeComplete: function(manager, context) {
35282
- manager.transitionTo('charged')
35283
- }
35284
- }),
35285
- charged: Ember.State.create({
35286
- boot: function(manager, context) {
35287
- manager.transitionTo('poweredUp')
35288
- }
35289
- })
35290
- }),
35291
- poweredUp: Ember.State.create({
35292
- beginExtermination: function(manager, context) {
35293
- manager.transitionTo('rampaging')
35294
- },
35295
- rampaging: Ember.State.create()
35296
- })
35297
- });
35298
-
35299
- robotManager.get('currentState.name'); // 'charging'
35300
- robotManager.send('boot'); // throws error, no boot action
35301
- // in current hierarchy
35302
- robotManager.get('currentState.name'); // remains 'charging'
35303
-
35304
- robotManager.send('beginExtermination'); // throws error, no beginExtermination
35305
- // action in current hierarchy
35306
- robotManager.get('currentState.name'); // remains 'charging'
35307
-
35308
- robotManager.send('chargeComplete');
35309
- robotManager.get('currentState.name'); // 'charged'
35310
-
35311
- robotManager.send('boot');
35312
- robotManager.get('currentState.name'); // 'poweredUp'
35313
-
35314
- robotManager.send('beginExtermination', allHumans);
35315
- robotManager.get('currentState.name'); // 'rampaging'
35316
- ```
35317
-
35318
- Transition actions can also be created using the `transitionTo` method of the `Ember.State` class. The
35319
- following example StateManagers are equivalent:
35320
-
35321
- ```javascript
35322
- var aManager = Ember.StateManager.create({
35323
- stateOne: Ember.State.create({
35324
- changeToStateTwo: Ember.State.transitionTo('stateTwo')
35325
- }),
35326
- stateTwo: Ember.State.create({})
35327
- });
35328
-
35329
- var bManager = Ember.StateManager.create({
35330
- stateOne: Ember.State.create({
35331
- changeToStateTwo: function(manager, context) {
35332
- manager.transitionTo('stateTwo', context)
35333
- }
35334
- }),
35335
- stateTwo: Ember.State.create({})
35336
- });
35337
- ```
35338
-
35339
- @class StateManager
35340
- @namespace Ember
35341
- @extends Ember.State
35342
- **/
35343
- Ember.StateManager = Ember.State.extend({
35344
- /**
35345
- @private
35346
-
35347
- When creating a new statemanager, look for a default state to transition
35348
- into. This state can either be named `start`, or can be specified using the
35349
- `initialState` property.
35350
-
35351
- @method init
35352
- */
35353
- init: function() {
35354
- this._super();
35355
-
35356
- set(this, 'stateMeta', Ember.Map.create());
35357
-
35358
- var initialState = get(this, 'initialState');
35359
-
35360
- if (!initialState && get(this, 'states.start')) {
35361
- initialState = 'start';
35362
- }
35363
-
35364
- if (initialState) {
35365
- this.transitionTo(initialState);
35366
- Ember.assert('Failed to transition to initial state "' + initialState + '"', !!get(this, 'currentState'));
35367
- }
35368
- },
35369
-
35370
- /**
35371
- Return the stateMeta, a hash of possible states. If no items exist in the stateMeta hash
35372
- this method sets the stateMeta to an empty JavaScript object and returns that instead.
35373
-
35374
- @method stateMetaFor
35375
- @param state
35376
- */
35377
- stateMetaFor: function(state) {
35378
- var meta = get(this, 'stateMeta'),
35379
- stateMeta = meta.get(state);
35380
-
35381
- if (!stateMeta) {
35382
- stateMeta = {};
35383
- meta.set(state, stateMeta);
35384
- }
35385
-
35386
- return stateMeta;
35387
- },
35388
-
35389
- /**
35390
- Sets a key value pair on the stateMeta hash.
35391
-
35392
- @method setStateMeta
35393
- @param state
35394
- @param key
35395
- @param value
35396
- */
35397
- setStateMeta: function(state, key, value) {
35398
- return set(this.stateMetaFor(state), key, value);
35399
- },
35400
-
35401
- /**
35402
- Returns the value of an item in the stateMeta hash at the given key.
35403
-
35404
- @method getStateMeta
35405
- @param state
35406
- @param key
35407
- */
35408
- getStateMeta: function(state, key) {
35409
- return get(this.stateMetaFor(state), key);
35410
- },
35411
-
35412
- /**
35413
- The current state from among the manager's possible states. This property should
35414
- not be set directly. Use `transitionTo` to move between states by name.
35415
-
35416
- @property currentState
35417
- @type Ember.State
35418
- */
35419
- currentState: null,
35420
-
35421
- /**
35422
- The path of the current state. Returns a string representation of the current
35423
- state.
35424
-
35425
- @property currentPath
35426
- @type String
35427
- */
35428
- currentPath: Ember.computed.alias('currentState.path'),
35429
-
35430
- /**
35431
- The name of transitionEvent that this stateManager will dispatch
35432
-
35433
- @property transitionEvent
35434
- @type String
35435
- @default 'setup'
35436
- */
35437
- transitionEvent: 'setup',
35438
-
35439
- /**
35440
- If set to true, `errorOnUnhandledEvents` will cause an exception to be
35441
- raised if you attempt to send an event to a state manager that is not
35442
- handled by the current state or any of its parent states.
35443
-
35444
- @property errorOnUnhandledEvents
35445
- @type Boolean
35446
- @default true
35447
- */
35448
- errorOnUnhandledEvent: true,
35449
-
35450
- /**
35451
- An alias to sendEvent method
35452
-
35453
- @method send
35454
- @param event
35455
- */
35456
- send: function(event) {
35457
- var contexts = [].slice.call(arguments, 1);
35458
- Ember.assert('Cannot send event "' + event + '" while currentState is ' + get(this, 'currentState'), get(this, 'currentState'));
35459
- return sendEvent.call(this, event, contexts, false);
35460
- },
35461
-
35462
- /**
35463
- If errorOnUnhandledEvent is true this event with throw an Ember.Error
35464
- indicating that the no state could respond to the event passed through the
35465
- state machine.
35466
-
35467
- @method unhandledEvent
35468
- @param manager
35469
- @param event
35470
- */
35471
- unhandledEvent: function(manager, event) {
35472
- if (get(this, 'errorOnUnhandledEvent')) {
35473
- throw new Ember.Error(this.toString() + " could not respond to event " + event + " in state " + get(this, 'currentState.path') + ".");
35474
- }
35475
- },
35476
-
35477
- /**
35478
- Finds a state by its state path.
35479
-
35480
- Example:
35481
-
35482
- ```javascript
35483
- var manager = Ember.StateManager.create({
35484
- root: Ember.State.create({
35485
- dashboard: Ember.State.create()
35486
- })
35487
- });
35488
-
35489
- manager.getStateByPath(manager, "root.dashboard");
35490
- // returns the dashboard state
35491
-
35492
- var aState = manager.getStateByPath(manager, "root.dashboard");
35493
-
35494
- var path = aState.get('path');
35495
- // path is 'root.dashboard'
35496
-
35497
- var name = aState.get('name');
35498
- // name is 'dashboard'
35499
- ```
35500
-
35501
- @method getStateByPath
35502
- @param {Ember.State} root the state to start searching from
35503
- @param {String} path the state path to follow
35504
- @return {Ember.State} the state at the end of the path
35505
- */
35506
- getStateByPath: function(root, path) {
35507
- var parts = path.split('.'),
35508
- state = root;
35509
-
35510
- for (var i=0, len=parts.length; i<len; i++) {
35511
- state = get(get(state, 'states'), parts[i]);
35512
- if (!state) { break; }
35513
- }
35514
-
35515
- return state;
35516
- },
35517
-
35518
- findStateByPath: function(state, path) {
35519
- var possible;
35520
-
35521
- while (!possible && state) {
35522
- possible = this.getStateByPath(state, path);
35523
- state = get(state, 'parentState');
35524
- }
35525
-
35526
- return possible;
35527
- },
35528
-
35529
- /**
35530
- A state stores its child states in its `states` hash.
35531
- This code takes a path like `posts.show` and looks
35532
- up `root.states.posts.states.show`.
35533
-
35534
- It returns a list of all of the states from the
35535
- root, which is the list of states to call `enter`
35536
- on.
35537
-
35538
- @method getStatesInPath
35539
- @param root
35540
- @param path
35541
- */
35542
- getStatesInPath: function(root, path) {
35543
- if (!path || path === "") { return undefined; }
35544
- var parts = path.split('.'),
35545
- result = [],
35546
- states,
35547
- state;
35548
-
35549
- for (var i=0, len=parts.length; i<len; i++) {
35550
- states = get(root, 'states');
35551
- if (!states) { return undefined; }
35552
- state = get(states, parts[i]);
35553
- if (state) { root = state; result.push(state); }
35554
- else { return undefined; }
35555
- }
35556
-
35557
- return result;
35558
- },
35559
-
35560
- /**
35561
- Alias for transitionTo.
35562
- This method applies a transitionTo to the arguments passed into this method.
35563
-
35564
- @method goToState
35565
- */
35566
- goToState: function() {
35567
- // not deprecating this yet so people don't constantly need to
35568
- // make trivial changes for little reason.
35569
- return this.transitionTo.apply(this, arguments);
35570
- },
35571
-
35572
- /**
35573
- Transition to another state within the state machine. If the path is empty returns
35574
- immediately. This method attempts to get a hash of the enter, exit and resolve states
35575
- from the existing state cache. Processes the raw state information based on the
35576
- passed in context. Creates a new transition object and triggers a new setupContext.
35577
-
35578
- @method transitionTo
35579
- @param path
35580
- @param context
35581
- */
35582
- transitionTo: function(path, context) {
35583
- // XXX When is transitionTo called with no path
35584
- if (Ember.isEmpty(path)) { return; }
35585
-
35586
- // The ES6 signature of this function is `path, ...contexts`
35587
- var contexts = context ? Array.prototype.slice.call(arguments, 1) : [],
35588
- currentState = get(this, 'currentState') || this;
35589
-
35590
- // First, get the enter, exit and resolve states for the current state
35591
- // and specified path. If possible, use an existing cache.
35592
- var hash = this.contextFreeTransition(currentState, path);
35593
-
35594
- // Next, process the raw state information for the contexts passed in.
35595
- var transition = new Transition(hash).normalize(this, contexts);
35596
-
35597
- this.enterState(transition);
35598
- this.triggerSetupContext(transition);
35599
- },
35600
-
35601
- /**
35602
- Allows you to transition to any other state in the state manager without
35603
- being constrained by the state hierarchy of the current state path.
35604
- This method will traverse the state path upwards through its parents until
35605
- it finds the specified state path. All the transitions are captured during the
35606
- traversal.
35607
-
35608
- Caches and returns hash of transitions, which contain the exitSates, enterStates and
35609
- resolvedState
35610
-
35611
- @method contextFreeTransition
35612
- @param currentState
35613
- @param path
35614
- */
35615
- contextFreeTransition: function(currentState, path) {
35616
- var cache = currentState.getPathsCache(this, path);
35617
- if (cache) { return cache; }
35618
-
35619
- var enterStates = this.getStatesInPath(currentState, path),
35620
- exitStates = [],
35621
- resolveState = currentState;
35622
-
35623
- // Walk up the states. For each state, check whether a state matching
35624
- // the `path` is nested underneath. This will find the closest
35625
- // parent state containing `path`.
35626
- //
35627
- // This allows the user to pass in a relative path. For example, for
35628
- // the following state hierarchy:
35629
- //
35630
- // | |root
35631
- // | |- posts
35632
- // | | |- show (* current)
35633
- // | |- comments
35634
- // | | |- show
35635
- //
35636
- // If the current state is `<root.posts.show>`, an attempt to
35637
- // transition to `comments.show` will match `<root.comments.show>`.
35638
- //
35639
- // First, this code will look for root.posts.show.comments.show.
35640
- // Next, it will look for root.posts.comments.show. Finally,
35641
- // it will look for `root.comments.show`, and find the state.
35642
- //
35643
- // After this process, the following variables will exist:
35644
- //
35645
- // * resolveState: a common parent state between the current
35646
- // and target state. In the above example, `<root>` is the
35647
- // `resolveState`.
35648
- // * enterStates: a list of all of the states represented
35649
- // by the path from the `resolveState`. For example, for
35650
- // the path `root.comments.show`, `enterStates` would have
35651
- // `[<root.comments>, <root.comments.show>]`
35652
- // * exitStates: a list of all of the states from the
35653
- // `resolveState` to the `currentState`. In the above
35654
- // example, `exitStates` would have
35655
- // `[<root.posts>`, `<root.posts.show>]`.
35656
- while (resolveState && !enterStates) {
35657
- exitStates.unshift(resolveState);
35658
-
35659
- resolveState = get(resolveState, 'parentState');
35660
- if (!resolveState) {
35661
- enterStates = this.getStatesInPath(this, path);
35662
- if (!enterStates) {
35663
- Ember.assert('Could not find state for path: "'+path+'"');
35664
- return;
35665
- }
35666
- }
35667
- enterStates = this.getStatesInPath(resolveState, path);
35668
- }
35669
-
35670
- // If the path contains some states that are parents of both the
35671
- // current state and the target state, remove them.
35672
- //
35673
- // For example, in the following hierarchy:
35674
- //
35675
- // |- root
35676
- // | |- post
35677
- // | | |- index (* current)
35678
- // | | |- show
35679
- //
35680
- // If the `path` is `root.post.show`, the three variables will
35681
- // be:
35682
- //
35683
- // * resolveState: `<state manager>`
35684
- // * enterStates: `[<root>, <root.post>, <root.post.show>]`
35685
- // * exitStates: `[<root>, <root.post>, <root.post.index>]`
35686
- //
35687
- // The goal of this code is to remove the common states, so we
35688
- // have:
35689
- //
35690
- // * resolveState: `<root.post>`
35691
- // * enterStates: `[<root.post.show>]`
35692
- // * exitStates: `[<root.post.index>]`
35693
- //
35694
- // This avoid unnecessary calls to the enter and exit transitions.
35695
- while (enterStates.length > 0 && enterStates[0] === exitStates[0]) {
35696
- resolveState = enterStates.shift();
35697
- exitStates.shift();
35698
- }
35699
-
35700
- // Cache the enterStates, exitStates, and resolveState for the
35701
- // current state and the `path`.
35702
- var transitions = {
35703
- exitStates: exitStates,
35704
- enterStates: enterStates,
35705
- resolveState: resolveState
35706
- };
35707
-
35708
- currentState.setPathsCache(this, path, transitions);
35709
-
35710
- return transitions;
35711
- },
35712
-
35713
- /**
35714
- A trigger to setup the state contexts. Each state is setup with
35715
- an enterState.
35716
-
35717
- @method triggerSetupContext
35718
- @param transitions
35719
- */
35720
- triggerSetupContext: function(transitions) {
35721
- var contexts = transitions.contexts,
35722
- offset = transitions.enterStates.length - contexts.length,
35723
- enterStates = transitions.enterStates,
35724
- transitionEvent = get(this, 'transitionEvent');
35725
-
35726
- Ember.assert("More contexts provided than states", offset >= 0);
35727
-
35728
- arrayForEach.call(enterStates, function(state, idx) {
35729
- state.trigger(transitionEvent, this, contexts[idx-offset]);
35730
- }, this);
35731
- },
35732
-
35733
- /**
35734
- Returns the state instance by name. If state is not found the parentState
35735
- is returned instead.
35736
-
35737
- @method getState
35738
- @param name
35739
- */
35740
- getState: function(name) {
35741
- var state = get(this, name),
35742
- parentState = get(this, 'parentState');
35743
-
35744
- if (state) {
35745
- return state;
35746
- } else if (parentState) {
35747
- return parentState.getState(name);
35748
- }
35749
- },
35750
-
35751
- /**
35752
- Causes a transition from the exitState of one state to the enterState of another
35753
- state in the state machine. At the end of the transition the currentState is set
35754
- to the finalState of the transition passed into this method.
35755
-
35756
- @method enterState
35757
- @param transition
35758
- */
35759
- enterState: function(transition) {
35760
- var log = this.enableLogging;
35761
-
35762
- var exitStates = transition.exitStates.slice(0).reverse();
35763
- arrayForEach.call(exitStates, function(state) {
35764
- state.trigger('exit', this);
35765
- }, this);
35766
-
35767
- arrayForEach.call(transition.enterStates, function(state) {
35768
- if (log) { Ember.Logger.log("STATEMANAGER: Entering " + get(state, 'path')); }
35769
- state.trigger('enter', this);
35770
- }, this);
35771
-
35772
- set(this, 'currentState', transition.finalState);
35773
- }
35774
- });
35775
35261
 
35776
35262
  })();
35777
35263
 
@@ -35779,11 +35265,11 @@ Ember.StateManager = Ember.State.extend({
35779
35265
 
35780
35266
  (function() {
35781
35267
  /**
35782
- Ember States
35268
+ Ember Application
35783
35269
 
35784
35270
  @module ember
35785
- @submodule ember-states
35786
- @requires ember-runtime
35271
+ @submodule ember-application
35272
+ @requires ember-views, ember-routing
35787
35273
  */
35788
35274
 
35789
35275
  })();
@@ -36810,7 +36296,7 @@ function chain(app, promise, fn) {
36810
36296
  * using your app.
36811
36297
  *
36812
36298
  * Example:
36813
- *
36299
+ *
36814
36300
  * ```
36815
36301
  * visit('posts/index').then(function() {
36816
36302
  * // assert something
@@ -36818,7 +36304,7 @@ function chain(app, promise, fn) {
36818
36304
  * ```
36819
36305
  *
36820
36306
  * @method visit
36821
- * @param {String} url the name of the route
36307
+ * @param {String} url the name of the route
36822
36308
  * @returns {RSVP.Promise}
36823
36309
  */
36824
36310
  helper('visit', visit);
@@ -36961,6 +36447,23 @@ Ember
36961
36447
  @module ember
36962
36448
  */
36963
36449
 
36450
+ function throwWithMessage(msg) {
36451
+ return function() {
36452
+ throw new Error(msg);
36453
+ };
36454
+ }
36455
+
36456
+ function generateRemovedClass(className) {
36457
+ var msg = " has been moved into a plugin: https://github.com/emberjs/ember-states";
36458
+
36459
+ return {
36460
+ extend: throwWithMessage(className + msg),
36461
+ create: throwWithMessage(className + msg)
36462
+ };
36463
+ }
36464
+
36465
+ Ember.StateManager = generateRemovedClass("Ember.StateManager");
36466
+ Ember.State = generateRemovedClass("Ember.State");
36964
36467
  })();
36965
36468
 
36966
36469