cadenero 0.0.2.a3 → 0.0.2.b1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +15 -0
  2. data/README.md +4 -3
  3. data/app/controllers/cadenero/v1/account/sessions_controller.rb +3 -1
  4. data/app/controllers/cadenero/v1/account/users_controller.rb +8 -2
  5. data/app/controllers/cadenero/v1/accounts_controller.rb +13 -1
  6. data/app/extenders/middleware/robustness.rb +19 -0
  7. data/app/models/cadenero/member.rb +1 -0
  8. data/app/models/cadenero/user.rb +2 -0
  9. data/app/models/cadenero/v1/account.rb +16 -1
  10. data/app/serializers/cadenero/account_serializer.rb +1 -0
  11. data/app/serializers/cadenero/user_serializer.rb +1 -0
  12. data/config/initializers/apartment.rb +4 -1
  13. data/config/initializers/warden/strategies/password.rb +1 -1
  14. data/config/routes.rb +1 -0
  15. data/lib/cadenero/constraints/subdomain_required.rb +2 -0
  16. data/lib/cadenero/engine.rb +1 -1
  17. data/lib/cadenero/testing_support/subdomain_helpers.rb +15 -0
  18. data/lib/cadenero/version.rb +1 -1
  19. data/lib/cadenero.rb +2 -0
  20. data/lib/generators/cadenero/install_generator.rb +17 -0
  21. data/spec/dummy/log/development.log +209 -0
  22. data/spec/dummy/log/test.log +26783 -5135
  23. data/spec/dummy/tmp/ember-rails/ember.js +1693 -676
  24. data/spec/features/users/sign_in_spec.rb +13 -3
  25. data/spec/models/cadenero/account_spec.rb +31 -1
  26. data/spec/spec_helper.rb +1 -0
  27. metadata +11 -44
  28. data/app/helpers/cadenero/application_helper.rb +0 -4
  29. data/app/helpers/cadenero/v1/accounts_helper.rb +0 -4
  30. data/app/helpers/cadenero/v1/users_helper.rb +0 -4
  31. data/spec/features/cadenero/account_spec.rb +0 -23
  32. data/spec/support/subdomain_helpers.rb +0 -8
@@ -1,5 +1,5 @@
1
- // Version: v1.0.0-rc.5-3-g835b8e5
2
- // Last commit: 835b8e5 (2013-06-01 14:23:38 -0400)
1
+ // Version: v1.0.0-rc.6
2
+ // Last commit: 893bbc4 (2013-06-23 15:14:46 -0400)
3
3
 
4
4
 
5
5
  (function() {
@@ -49,7 +49,12 @@ if (!('MANDATORY_SETTER' in Ember.ENV)) {
49
49
  falsy, an exception will be thrown.
50
50
  */
51
51
  Ember.assert = function(desc, test) {
52
- if (!test) throw new Error("assertion failed: "+desc);
52
+ Ember.Logger.assert(test, desc);
53
+
54
+ if (Ember.testing && !test) {
55
+ // when testing, ensure test failures when assertions fail
56
+ throw new Error("Assertion Failed: " + desc);
57
+ }
53
58
  };
54
59
 
55
60
 
@@ -95,12 +100,12 @@ Ember.debug = function(message) {
95
100
  will be displayed.
96
101
  */
97
102
  Ember.deprecate = function(message, test) {
98
- if (Ember && Ember.TESTING_DEPRECATION) { return; }
103
+ if (Ember.TESTING_DEPRECATION) { return; }
99
104
 
100
105
  if (arguments.length === 1) { test = false; }
101
106
  if (test) { return; }
102
107
 
103
- if (Ember && Ember.ENV.RAISE_ON_DEPRECATION) { throw new Error(message); }
108
+ if (Ember.ENV.RAISE_ON_DEPRECATION) { throw new Error(message); }
104
109
 
105
110
  var error;
106
111
 
@@ -151,8 +156,8 @@ Ember.deprecateFunc = function(message, func) {
151
156
 
152
157
  })();
153
158
 
154
- // Version: v1.0.0-rc.5-3-g835b8e5
155
- // Last commit: 835b8e5 (2013-06-01 14:23:38 -0400)
159
+ // Version: v1.0.0-rc.6
160
+ // Last commit: 893bbc4 (2013-06-23 15:14:46 -0400)
156
161
 
157
162
 
158
163
  (function() {
@@ -219,7 +224,7 @@ var define, requireModule;
219
224
 
220
225
  @class Ember
221
226
  @static
222
- @version 1.0.0-rc.5
227
+ @version 1.0.0-rc.6
223
228
  */
224
229
 
225
230
  if ('undefined' === typeof Ember) {
@@ -246,10 +251,10 @@ Ember.toString = function() { return "Ember"; };
246
251
  /**
247
252
  @property VERSION
248
253
  @type String
249
- @default '1.0.0-rc.5'
254
+ @default '1.0.0-rc.6'
250
255
  @final
251
256
  */
252
- Ember.VERSION = '1.0.0-rc.5';
257
+ Ember.VERSION = '1.0.0-rc.6';
253
258
 
254
259
  /**
255
260
  Standard environmental variables. You can define these in a global `ENV`
@@ -364,6 +369,19 @@ function consoleMethod(name) {
364
369
  }
365
370
  }
366
371
 
372
+ function assertPolyfill(test, message) {
373
+ if (!test) {
374
+ try {
375
+ // attempt to preserve the stack
376
+ throw new Error("assertion failed: " + message);
377
+ } catch(error) {
378
+ setTimeout(function(){
379
+ throw error;
380
+ }, 0);
381
+ }
382
+ }
383
+ }
384
+
367
385
  /**
368
386
  Inside Ember-Metal, simply uses the methods from `imports.console`.
369
387
  Override this to provide more robust logging functionality.
@@ -376,7 +394,8 @@ Ember.Logger = {
376
394
  warn: consoleMethod('warn') || Ember.K,
377
395
  error: consoleMethod('error') || Ember.K,
378
396
  info: consoleMethod('info') || Ember.K,
379
- debug: consoleMethod('debug') || consoleMethod('info') || Ember.K
397
+ debug: consoleMethod('debug') || consoleMethod('info') || Ember.K,
398
+ assert: consoleMethod('assert') || assertPolyfill
380
399
  };
381
400
 
382
401
 
@@ -1644,6 +1663,7 @@ get = function get(obj, keyName) {
1644
1663
  obj = null;
1645
1664
  }
1646
1665
 
1666
+ Ember.assert("Cannot call get with "+ keyName +" key.", !!keyName);
1647
1667
  Ember.assert("Cannot call get with '"+ keyName +"' on an undefined object.", obj !== undefined);
1648
1668
 
1649
1669
  if (obj === null || keyName.indexOf('.') !== -1) {
@@ -1676,12 +1696,21 @@ if (Ember.config.overrideAccessors) {
1676
1696
  get = Ember.get;
1677
1697
  }
1678
1698
 
1679
- function firstKey(path) {
1680
- return path.match(FIRST_KEY)[0];
1681
- }
1699
+ /**
1700
+ @private
1701
+
1702
+ Normalizes a target/path pair to reflect that actual target/path that should
1703
+ be observed, etc. This takes into account passing in global property
1704
+ paths (i.e. a path beginning with a captial letter not defined on the
1705
+ target) and * separators.
1682
1706
 
1683
- // assumes path is already normalized
1684
- function normalizeTuple(target, path) {
1707
+ @method normalizeTuple
1708
+ @for Ember
1709
+ @param {Object} target The current target. May be `null`.
1710
+ @param {String} path A path on the target or a global property path.
1711
+ @return {Array} a temporary array with the normalized target/path pair.
1712
+ */
1713
+ var normalizeTuple = Ember.normalizeTuple = function(target, path) {
1685
1714
  var hasThis = HAS_THIS.test(path),
1686
1715
  isGlobal = !hasThis && IS_GLOBAL_PATH.test(path),
1687
1716
  key;
@@ -1690,7 +1719,7 @@ function normalizeTuple(target, path) {
1690
1719
  if (hasThis) path = path.slice(5);
1691
1720
 
1692
1721
  if (target === Ember.lookup) {
1693
- key = firstKey(path);
1722
+ key = path.match(FIRST_KEY)[0];
1694
1723
  target = get(target, key);
1695
1724
  path = path.slice(key.length+1);
1696
1725
  }
@@ -1699,7 +1728,7 @@ function normalizeTuple(target, path) {
1699
1728
  if (!path || path.length===0) throw new Error('Invalid Path');
1700
1729
 
1701
1730
  return [ target, path ];
1702
- }
1731
+ };
1703
1732
 
1704
1733
  var getPath = Ember._getPath = function(root, path) {
1705
1734
  var hasThis, parts, tuple, idx, len;
@@ -1728,24 +1757,6 @@ var getPath = Ember._getPath = function(root, path) {
1728
1757
  return root;
1729
1758
  };
1730
1759
 
1731
- /**
1732
- @private
1733
-
1734
- Normalizes a target/path pair to reflect that actual target/path that should
1735
- be observed, etc. This takes into account passing in global property
1736
- paths (i.e. a path beginning with a captial letter not defined on the
1737
- target) and * separators.
1738
-
1739
- @method normalizeTuple
1740
- @for Ember
1741
- @param {Object} target The current target. May be `null`.
1742
- @param {String} path A path on the target or a global property path.
1743
- @return {Array} a temporary array with the normalized target/path pair.
1744
- */
1745
- Ember.normalizeTuple = function(target, path) {
1746
- return normalizeTuple(target, path);
1747
- };
1748
-
1749
1760
  Ember.getWithDefault = function(root, key, defaultValue) {
1750
1761
  var value = get(root, key);
1751
1762
 
@@ -2465,6 +2476,8 @@ var set = function set(obj, keyName, value, tolerant) {
2465
2476
  obj = null;
2466
2477
  }
2467
2478
 
2479
+ Ember.assert("Cannot call set with "+ keyName +" key.", !!keyName);
2480
+
2468
2481
  if (!obj || keyName.indexOf('.') !== -1) {
2469
2482
  return setPath(obj, keyName, value, tolerant);
2470
2483
  }
@@ -3116,9 +3129,9 @@ var changeProperties = Ember.changeProperties,
3116
3129
  observers will be buffered.
3117
3130
 
3118
3131
  @method setProperties
3119
- @param target
3120
- @param {Hash} properties
3121
- @return target
3132
+ @param self
3133
+ @param {Object} hash
3134
+ @return self
3122
3135
  */
3123
3136
  Ember.setProperties = function(self, hash) {
3124
3137
  changeProperties(function(){
@@ -4758,7 +4771,7 @@ define("backburner",
4758
4771
  },
4759
4772
 
4760
4773
  cancel: function(timer) {
4761
- if (typeof timer === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce
4774
+ if (timer && typeof timer === 'object' && timer.queue && timer.method) { // we're cancelling a deferOnce
4762
4775
  return timer.queue.cancel(timer);
4763
4776
  } else if (typeof timer === 'function') { // we're cancelling a setTimeout
4764
4777
  for (var i = 0, l = timers.length; i < l; i += 2) {
@@ -4767,6 +4780,8 @@ define("backburner",
4767
4780
  return true;
4768
4781
  }
4769
4782
  }
4783
+ } else {
4784
+ return; // timer was null or not a timer
4770
4785
  }
4771
4786
  }
4772
4787
  };
@@ -4859,7 +4874,7 @@ define("backburner/deferred_action_queues",
4859
4874
  while (queueNameIndex < numberOfQueues) {
4860
4875
  queueName = queueNames[queueNameIndex];
4861
4876
  queue = queues[queueName];
4862
- queueItems = queue._queue.slice();
4877
+ queueItems = queue._queueBeingFlushed = queue._queue.slice();
4863
4878
  queue._queue = [];
4864
4879
 
4865
4880
  var options = queue.options,
@@ -4877,15 +4892,19 @@ define("backburner/deferred_action_queues",
4877
4892
 
4878
4893
  if (typeof method === 'string') { method = target[method]; }
4879
4894
 
4880
- // TODO: error handling
4881
- if (args && args.length > 0) {
4882
- method.apply(target, args);
4883
- } else {
4884
- method.call(target);
4895
+ // method could have been nullified / canceled during flush
4896
+ if (method) {
4897
+ // TODO: error handling
4898
+ if (args && args.length > 0) {
4899
+ method.apply(target, args);
4900
+ } else {
4901
+ method.call(target);
4902
+ }
4885
4903
  }
4886
4904
 
4887
4905
  queueIndex += 4;
4888
4906
  }
4907
+ queue._queueBeingFlushed = null;
4889
4908
  if (numberOfQueueItems && after) { after(); }
4890
4909
 
4891
4910
  if ((priorQueueNameIndex = indexOfPriorQueueWithActions(this, queueNameIndex)) !== -1) {
@@ -4910,6 +4929,7 @@ define("backburner/deferred_action_queues",
4910
4929
  return -1;
4911
4930
  }
4912
4931
 
4932
+
4913
4933
  __exports__.DeferredActionQueues = DeferredActionQueues;
4914
4934
  });
4915
4935
 
@@ -4999,12 +5019,30 @@ define("backburner/queue",
4999
5019
  return true;
5000
5020
  }
5001
5021
  }
5022
+
5023
+ // if not found in current queue
5024
+ // could be in the queue that is being flushed
5025
+ queue = this._queueBeingFlushed;
5026
+ if (!queue) {
5027
+ return;
5028
+ }
5029
+ for (i = 0, l = queue.length; i < l; i += 4) {
5030
+ currentTarget = queue[i];
5031
+ currentMethod = queue[i+1];
5032
+
5033
+ if (currentTarget === actionToCancel.target && currentMethod === actionToCancel.method) {
5034
+ // don't mess with array during flush
5035
+ // just nullify the method
5036
+ queue[i+1] = null;
5037
+ return true;
5038
+ }
5039
+ }
5002
5040
  }
5003
5041
  };
5004
5042
 
5043
+
5005
5044
  __exports__.Queue = Queue;
5006
5045
  });
5007
-
5008
5046
  })();
5009
5047
 
5010
5048
 
@@ -5112,7 +5150,7 @@ Ember.run = function(target, method) {
5112
5150
  May be a function or a string. If you pass a string
5113
5151
  then it will be looked up on the passed target.
5114
5152
  @param {Object} [args*] Any additional arguments you wish to pass to the method.
5115
- @return {Object} return value from invoking the passed function. Please note,
5153
+ @return {Object} return value from invoking the passed function. Please note,
5116
5154
  when called within an existing loop, no return value is possible.
5117
5155
  */
5118
5156
  Ember.run.join = function(target, method) {
@@ -5248,7 +5286,9 @@ Ember.run.cancelTimers = function () {
5248
5286
  @return {void}
5249
5287
  */
5250
5288
  Ember.run.sync = function() {
5251
- backburner.currentInstance.queues.sync.flush();
5289
+ if (backburner.currentInstance) {
5290
+ backburner.currentInstance.queues.sync.flush();
5291
+ }
5252
5292
  };
5253
5293
 
5254
5294
  /**
@@ -5441,6 +5481,38 @@ Ember.run.cancel = function(timer) {
5441
5481
  return backburner.cancel(timer);
5442
5482
  };
5443
5483
 
5484
+ /**
5485
+ Execute the passed method in a specified amount of time, reset timer
5486
+ upon additional calls.
5487
+
5488
+ ```javascript
5489
+ var myFunc = function() { console.log(this.name + ' ran.'); };
5490
+ var myContext = {name: 'debounce'};
5491
+
5492
+ Ember.run.debounce(myContext, myFunc, 150);
5493
+
5494
+ // less than 150ms passes
5495
+
5496
+ Ember.run.debounce(myContext, myFunc, 150);
5497
+
5498
+ // 150ms passes
5499
+ // myFunc is invoked with context myContext
5500
+ // console logs 'debounce ran.' one time.
5501
+ ```
5502
+
5503
+ @method debounce
5504
+ @param {Object} [target] target of method to invoke
5505
+ @param {Function|String} method The method to invoke.
5506
+ May be a function or a string. If you pass a string
5507
+ then it will be looked up on the passed target.
5508
+ @param {Object} [args*] Optional arguments to pass to the timeout.
5509
+ @param {Number} wait Number of milliseconds to wait.
5510
+ @return {void}
5511
+ */
5512
+ Ember.run.debounce = function() {
5513
+ return backburner.debounce.apply(backburner, arguments);
5514
+ };
5515
+
5444
5516
  // Make sure it's not an autorun during testing
5445
5517
  function checkAutoRun() {
5446
5518
  if (!Ember.run.currentRunLoop) {
@@ -7230,6 +7302,8 @@ define("rsvp",
7230
7302
  __exports__.reject = reject;
7231
7303
  });
7232
7304
 
7305
+
7306
+
7233
7307
  })();
7234
7308
 
7235
7309
  (function() {
@@ -7354,6 +7428,10 @@ define("container",
7354
7428
  return value;
7355
7429
  },
7356
7430
 
7431
+ lookupFactory: function(fullName) {
7432
+ return factoryFor(this, fullName);
7433
+ },
7434
+
7357
7435
  has: function(fullName) {
7358
7436
  if (this.cache.has(fullName)) {
7359
7437
  return true;
@@ -8074,8 +8152,8 @@ Ember.String = {
8074
8152
  ```
8075
8153
 
8076
8154
  @method capitalize
8077
- @param {String} str
8078
- @return {String}
8155
+ @param {String} str The string to capitalize.
8156
+ @return {String} The capitalized string.
8079
8157
  */
8080
8158
  capitalize: function(str) {
8081
8159
  return str.charAt(0).toUpperCase() + str.substr(1);
@@ -9448,15 +9526,15 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
9448
9526
  Adds an array observer to the receiving array. The array observer object
9449
9527
  normally must implement two methods:
9450
9528
 
9451
- * `arrayWillChange(start, removeCount, addCount)` - This method will be
9529
+ * `arrayWillChange(observedObj, start, removeCount, addCount)` - This method will be
9452
9530
  called just before the array is modified.
9453
- * `arrayDidChange(start, removeCount, addCount)` - This method will be
9531
+ * `arrayDidChange(observedObj, start, removeCount, addCount)` - This method will be
9454
9532
  called just after the array is modified.
9455
9533
 
9456
- Both callbacks will be passed the starting index of the change as well a
9457
- a count of the items to be removed and added. You can use these callbacks
9458
- to optionally inspect the array during the change, clear caches, or do
9459
- any other bookkeeping necessary.
9534
+ Both callbacks will be passed the observed object, starting index of the
9535
+ change as well a a count of the items to be removed and added. You can use
9536
+ these callbacks to optionally inspect the array during the change, clear
9537
+ caches, or do any other bookkeeping necessary.
9460
9538
 
9461
9539
  In addition to passing a target, you can also include an options hash
9462
9540
  which you can use to override the method names that will be invoked on the
@@ -13464,17 +13542,40 @@ Ember.SortableMixin = Ember.Mixin.create(Ember.MutableEnumerable, {
13464
13542
  @property {Boolean} sortAscending
13465
13543
  */
13466
13544
  sortAscending: true,
13545
+
13546
+ /**
13547
+ The function used to compare two values. You can override this if you
13548
+ want to do custom comparisons.Functions must be of the type expected by
13549
+ Array#sort, i.e.
13550
+ return 0 if the two parameters are equal,
13551
+ return a negative value if the first parameter is smaller than the second or
13552
+ return a positive value otherwise:
13553
+
13554
+ ```javascript
13555
+ function(x,y){ // These are assumed to be integers
13556
+ if(x === y)
13557
+ return 0;
13558
+ return x < y ? -1 : 1;
13559
+ }
13560
+ ```
13467
13561
 
13562
+ @property sortFunction
13563
+ @type {Function}
13564
+ @default Ember.compare
13565
+ */
13566
+ sortFunction: Ember.compare,
13567
+
13468
13568
  orderBy: function(item1, item2) {
13469
13569
  var result = 0,
13470
13570
  sortProperties = get(this, 'sortProperties'),
13471
- sortAscending = get(this, 'sortAscending');
13571
+ sortAscending = get(this, 'sortAscending'),
13572
+ sortFunction = get(this, 'sortFunction');
13472
13573
 
13473
13574
  Ember.assert("you need to define `sortProperties`", !!sortProperties);
13474
13575
 
13475
13576
  forEach(sortProperties, function(propertyName) {
13476
13577
  if (result === 0) {
13477
- result = Ember.compare(get(item1, propertyName), get(item2, propertyName));
13578
+ result = sortFunction(get(item1, propertyName), get(item2, propertyName));
13478
13579
  if ((result !== 0) && !sortAscending) {
13479
13580
  result = (-1) * result;
13480
13581
  }
@@ -13912,7 +14013,7 @@ Ember Runtime
13912
14013
  */
13913
14014
 
13914
14015
  var jQuery = Ember.imports.jQuery;
13915
- Ember.assert("Ember Views require jQuery 1.8, 1.9, 1.10, or 2.0", jQuery && (jQuery().jquery.match(/^((1\.(8|9|10))|2.0)(\.\d+)?(pre|rc\d?)?/) || Ember.ENV.FORCE_JQUERY));
14016
+ Ember.assert("Ember Views require jQuery 1.7, 1.8, 1.9, 1.10, or 2.0", jQuery && (jQuery().jquery.match(/^((1\.(7|8|9|10))|2.0)(\.\d+)?(pre|rc\d?)?/) || Ember.ENV.FORCE_JQUERY));
13916
14017
 
13917
14018
  /**
13918
14019
  Alias for jQuery
@@ -14610,6 +14711,47 @@ var get = Ember.get, set = Ember.set, fmt = Ember.String.fmt;
14610
14711
  */
14611
14712
  Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.prototype */{
14612
14713
 
14714
+ /**
14715
+ The set of events names (and associated handler function names) to be setup
14716
+ and dispatched by the `EventDispatcher`. Custom events can added to this list at setup
14717
+ time, generally via the `Ember.Application.customEvents` hash. Only override this
14718
+ default set to prevent the EventDispatcher from listening on some events all together.
14719
+
14720
+ This set will be modified by `setup` to also include any events added at that time.
14721
+
14722
+ @property events
14723
+ @type Object
14724
+ */
14725
+ events: {
14726
+ touchstart : 'touchStart',
14727
+ touchmove : 'touchMove',
14728
+ touchend : 'touchEnd',
14729
+ touchcancel : 'touchCancel',
14730
+ keydown : 'keyDown',
14731
+ keyup : 'keyUp',
14732
+ keypress : 'keyPress',
14733
+ mousedown : 'mouseDown',
14734
+ mouseup : 'mouseUp',
14735
+ contextmenu : 'contextMenu',
14736
+ click : 'click',
14737
+ dblclick : 'doubleClick',
14738
+ mousemove : 'mouseMove',
14739
+ focusin : 'focusIn',
14740
+ focusout : 'focusOut',
14741
+ mouseenter : 'mouseEnter',
14742
+ mouseleave : 'mouseLeave',
14743
+ submit : 'submit',
14744
+ input : 'input',
14745
+ change : 'change',
14746
+ dragstart : 'dragStart',
14747
+ drag : 'drag',
14748
+ dragenter : 'dragEnter',
14749
+ dragleave : 'dragLeave',
14750
+ dragover : 'dragOver',
14751
+ drop : 'drop',
14752
+ dragend : 'dragEnd'
14753
+ },
14754
+
14613
14755
  /**
14614
14756
  @private
14615
14757
 
@@ -14641,35 +14783,7 @@ Ember.EventDispatcher = Ember.Object.extend(/** @scope Ember.EventDispatcher.pro
14641
14783
  @param addedEvents {Hash}
14642
14784
  */
14643
14785
  setup: function(addedEvents, rootElement) {
14644
- var event, events = {
14645
- touchstart : 'touchStart',
14646
- touchmove : 'touchMove',
14647
- touchend : 'touchEnd',
14648
- touchcancel : 'touchCancel',
14649
- keydown : 'keyDown',
14650
- keyup : 'keyUp',
14651
- keypress : 'keyPress',
14652
- mousedown : 'mouseDown',
14653
- mouseup : 'mouseUp',
14654
- contextmenu : 'contextMenu',
14655
- click : 'click',
14656
- dblclick : 'doubleClick',
14657
- mousemove : 'mouseMove',
14658
- focusin : 'focusIn',
14659
- focusout : 'focusOut',
14660
- mouseenter : 'mouseEnter',
14661
- mouseleave : 'mouseLeave',
14662
- submit : 'submit',
14663
- input : 'input',
14664
- change : 'change',
14665
- dragstart : 'dragStart',
14666
- drag : 'drag',
14667
- dragenter : 'dragEnter',
14668
- dragleave : 'dragLeave',
14669
- dragover : 'dragOver',
14670
- drop : 'drop',
14671
- dragend : 'dragEnd'
14672
- };
14786
+ var event, events = get(this, 'events');
14673
14787
 
14674
14788
  Ember.$.extend(events, addedEvents || {});
14675
14789
 
@@ -14916,6 +15030,15 @@ Ember.warn("The VIEW_PRESERVES_CONTEXT flag has been removed and the functionali
14916
15030
  */
14917
15031
  Ember.TEMPLATES = {};
14918
15032
 
15033
+ /**
15034
+ `Ember.CoreView` is
15035
+
15036
+ @class CoreView
15037
+ @namespace Ember
15038
+ @extends Ember.Object
15039
+ @uses Ember.Evented
15040
+ */
15041
+
14919
15042
  Ember.CoreView = Ember.Object.extend(Ember.Evented, {
14920
15043
  isView: true,
14921
15044
 
@@ -15619,8 +15742,10 @@ class:
15619
15742
 
15620
15743
  ### Event Names
15621
15744
 
15622
- Possible events names for any of the responding approaches described above
15623
- are:
15745
+ All of the event handling approaches described above respond to the same set
15746
+ of events. The names of the built-in events are listed below. (The hash of
15747
+ built-in events exists in `Ember.EventDispatcher`.) Additional, custom events
15748
+ can be registered by using `Ember.Application.customEvents`.
15624
15749
 
15625
15750
  Touch events:
15626
15751
 
@@ -15673,8 +15798,7 @@ class:
15673
15798
 
15674
15799
  @class View
15675
15800
  @namespace Ember
15676
- @extends Ember.Object
15677
- @uses Ember.Evented
15801
+ @extends Ember.CoreView
15678
15802
  */
15679
15803
  Ember.View = Ember.CoreView.extend(
15680
15804
  /** @scope Ember.View.prototype */ {
@@ -15855,7 +15979,7 @@ Ember.View = Ember.CoreView.extend(
15855
15979
  If a value that affects template rendering changes, the view should be
15856
15980
  re-rendered to reflect the new value.
15857
15981
 
15858
- @method _displayPropertyDidChange
15982
+ @method _contextDidChange
15859
15983
  */
15860
15984
  _contextDidChange: Ember.observer(function() {
15861
15985
  this.rerender();
@@ -15985,6 +16109,8 @@ Ember.View = Ember.CoreView.extend(
15985
16109
  _parentViewDidChange: Ember.observer(function() {
15986
16110
  if (this.isDestroying) { return; }
15987
16111
 
16112
+ this.trigger('parentViewDidChange');
16113
+
15988
16114
  if (get(this, 'parentView.controller') && !get(this, 'controller')) {
15989
16115
  this.notifyPropertyChange('controller');
15990
16116
  }
@@ -16256,9 +16382,9 @@ Ember.View = Ember.CoreView.extend(
16256
16382
  For example, calling `view.$('li')` will return a jQuery object containing
16257
16383
  all of the `li` elements inside the DOM element of this view.
16258
16384
 
16259
- @property $
16385
+ @method $
16260
16386
  @param {String} [selector] a jQuery-compatible selector string
16261
- @return {jQuery} the CoreQuery object for the DOM node
16387
+ @return {jQuery} the jQuery object for the DOM node
16262
16388
  */
16263
16389
  $: function(sel) {
16264
16390
  return this.currentState.$(this, sel);
@@ -16939,31 +17065,32 @@ Ember.View = Ember.CoreView.extend(
16939
17065
  @return {Ember.View} new instance
16940
17066
  */
16941
17067
  createChildView: function(view, attrs) {
16942
- if (view.isView && view._parentView === this) { return view; }
17068
+ if (view.isView && view._parentView === this && view.container === this.container) {
17069
+ return view;
17070
+ }
17071
+
17072
+ attrs = attrs || {};
17073
+ attrs._parentView = this;
17074
+ attrs.container = this.container;
16943
17075
 
16944
17076
  if (Ember.CoreView.detect(view)) {
16945
- attrs = attrs || {};
16946
- attrs._parentView = this;
16947
- attrs.container = this.container;
16948
17077
  attrs.templateData = attrs.templateData || get(this, 'templateData');
16949
17078
 
16950
17079
  view = view.create(attrs);
16951
17080
 
16952
17081
  // don't set the property on a virtual view, as they are invisible to
16953
17082
  // consumers of the view API
16954
- if (view.viewName) { set(get(this, 'concreteView'), view.viewName, view); }
17083
+ if (view.viewName) {
17084
+ set(get(this, 'concreteView'), view.viewName, view);
17085
+ }
16955
17086
  } else {
16956
17087
  Ember.assert('You must pass instance or subclass of View', view.isView);
16957
17088
 
16958
- if (attrs) {
16959
- view.setProperties(attrs);
16960
- }
17089
+ Ember.setProperties(view, attrs);
16961
17090
 
16962
17091
  if (!get(view, 'templateData')) {
16963
17092
  set(view, 'templateData', get(this, 'templateData'));
16964
17093
  }
16965
-
16966
- set(view, '_parentView', this);
16967
17094
  }
16968
17095
 
16969
17096
  return view;
@@ -17310,8 +17437,8 @@ Ember.View.applyAttributeBindings = function(elem, name, value) {
17310
17437
  elem.attr(name, value);
17311
17438
  }
17312
17439
  } else if (name === 'value' || type === 'boolean') {
17313
- // We can't set properties to undefined
17314
- if (value === undefined) { value = null; }
17440
+ // We can't set properties to undefined or null
17441
+ if (!value) { value = ''; }
17315
17442
 
17316
17443
  if (value !== elem.prop(name)) {
17317
17444
  // value and booleans should always be properties
@@ -18011,6 +18138,7 @@ Ember.ContainerView = Ember.View.extend(Ember.MutableArray, {
18011
18138
  initializeViews: function(views, parentView, templateData) {
18012
18139
  forEach(views, function(view) {
18013
18140
  set(view, '_parentView', parentView);
18141
+ set(view, 'container', parentView && parentView.container);
18014
18142
 
18015
18143
  if (!get(view, 'templateData')) {
18016
18144
  set(view, 'templateData', templateData);
@@ -18476,6 +18604,103 @@ Ember.CollectionView.CONTAINER_MAP = {
18476
18604
 
18477
18605
 
18478
18606
 
18607
+ (function() {
18608
+ /**
18609
+ @module ember
18610
+ @submodule ember-views
18611
+ */
18612
+
18613
+ /**
18614
+ An `Ember.Component` is a view that is completely
18615
+ isolated. Property access in its templates go
18616
+ to the view object and actions are targeted at
18617
+ the view object. There is no access to the
18618
+ surrounding context or outer controller; all
18619
+ contextual information is passed in.
18620
+
18621
+ The easiest way to create an `Ember.Component` is via
18622
+ a template. If you name a template
18623
+ `controls/my-foo`, you will be able to use
18624
+ `{{my-foo}}` in other templates, which will make
18625
+ an instance of the isolated control.
18626
+
18627
+ ```html
18628
+ {{app-profile person=currentUser}}
18629
+ ```
18630
+
18631
+ ```html
18632
+ <!-- app-profile template -->
18633
+ <h1>{{person.title}}</h1>
18634
+ <img {{bindAttr src=person.avatar}}>
18635
+ <p class='signature'>{{person.signature}}</p>
18636
+ ```
18637
+
18638
+ You can also use `yield` inside a template to
18639
+ include the **contents** of the custom tag:
18640
+
18641
+ ```html
18642
+ {{#my-profile person=currentUser}}
18643
+ <p>Admin mode</p>
18644
+ {{/my-profile}}
18645
+ ```
18646
+
18647
+ ```html
18648
+ <!-- app-profile template -->
18649
+
18650
+ <h1>{{person.title}}</h1>
18651
+ {{yield}} <!-- block contents -->
18652
+ ```
18653
+
18654
+ If you want to customize the control, in order to
18655
+ handle events or actions, you implement a subclass
18656
+ of `Ember.Component` named after the name of the
18657
+ control.
18658
+
18659
+ For example, you could implement the action
18660
+ `hello` for the `app-profile` control:
18661
+
18662
+ ```js
18663
+ App.AppProfileComponent = Ember.Component.extend({
18664
+ hello: function(name) {
18665
+ console.log("Hello", name)
18666
+ }
18667
+ });
18668
+ ```
18669
+
18670
+ And then use it in the control's template:
18671
+
18672
+ ```html
18673
+ <!-- app-profile template -->
18674
+
18675
+ <h1>{{person.title}}</h1>
18676
+ {{yield}} <!-- block contents -->
18677
+
18678
+ <button {{action 'hello' person.name}}>
18679
+ Say Hello to {{person.name}}
18680
+ </button>
18681
+ ```
18682
+
18683
+ Components must have a `-` in their name to avoid
18684
+ conflicts with built-in controls that wrap HTML
18685
+ elements. This is consistent with the same
18686
+ requirement in web components.
18687
+
18688
+ @class Component
18689
+ @namespace Ember
18690
+ @extends Ember.View
18691
+ */
18692
+ Ember.Component = Ember.View.extend({
18693
+ init: function() {
18694
+ this._super();
18695
+ this.set('context', this);
18696
+ this.set('controller', this);
18697
+ }
18698
+ });
18699
+
18700
+ })();
18701
+
18702
+
18703
+
18479
18704
  (function() {
18480
18705
 
18481
18706
  })();
@@ -18546,7 +18771,6 @@ Ember.ViewTargetActionSupport = Ember.Mixin.create(Ember.TargetActionSupport, {
18546
18771
 
18547
18772
 
18548
18773
  (function() {
18549
- /*globals jQuery*/
18550
18774
  /**
18551
18775
  Ember Views
18552
18776
 
@@ -19071,7 +19295,71 @@ function makeBindings(options) {
19071
19295
  }
19072
19296
  }
19073
19297
 
19298
+ /**
19299
+ Register a bound helper or custom view helper.
19300
+
19301
+ ## Simple bound helper example
19302
+
19303
+ ```javascript
19304
+ Ember.Handlebars.helper('capitalize', function(value) {
19305
+ return value.toUpperCase();
19306
+ });
19307
+ ```
19308
+
19309
+ The above bound helper can be used inside of templates as follows:
19310
+
19311
+ ```handlebars
19312
+ {{capitalize name}}
19313
+ ```
19314
+
19315
+ In this case, when the `name` property of the template's context changes,
19316
+ the rendered value of the helper will update to reflect this change.
19317
+
19318
+ For more examples of bound helpers, see documentation for
19319
+ `Ember.Handlebars.registerBoundHelper`.
19320
+
19321
+ ## Custom view helper example
19322
+
19323
+ Assuming a view subclass named `App.CalenderView` were defined, a helper
19324
+ for rendering instances of this view could be registered as follows:
19325
+
19326
+ ```javascript
19327
+ Ember.Handlebars.helper('calendar', App.CalendarView):
19328
+ ```
19329
+
19330
+ The above bound helper can be used inside of templates as follows:
19331
+
19332
+ ```handlebars
19333
+ {{calendar}}
19334
+ ```
19335
+
19336
+ Which is functionally equivalent to:
19337
+
19338
+ ```handlebars
19339
+ {{view App.CalendarView}}
19340
+ ```
19341
+
19342
+ Options in the helper will be passed to the view in exactly the same
19343
+ manner as with the `view` helper.
19344
+
19345
+ @method helper
19346
+ @for Ember.Handlebars
19347
+ @param {String} name
19348
+ @param {Function|Ember.View} function or view class constructor
19349
+ @param {String} dependentKeys*
19350
+ */
19074
19351
  Ember.Handlebars.helper = function(name, value) {
19352
+ if (Ember.Component.detect(value)) {
19353
+ Ember.assert("You tried to register a component named '" + name + "', but component names must include a '-'", name.match(/-/));
19354
+
19355
+ var proto = value.proto();
19356
+ if (!proto.layoutName && !proto.templateName) {
19357
+ value.reopen({
19358
+ layoutName: 'components/' + name
19359
+ });
19360
+ }
19361
+ }
19362
+
19075
19363
  if (Ember.View.detect(value)) {
19076
19364
  Ember.Handlebars.registerHelper(name, function(options) {
19077
19365
  Ember.assert("You can only pass attributes as parameters (not values) to a application-defined helper", arguments.length < 2);
@@ -19832,7 +20120,7 @@ Ember._MetamorphView = Ember.View.extend(Ember._Metamorph);
19832
20120
  /**
19833
20121
  @class _SimpleMetamorphView
19834
20122
  @namespace Ember
19835
- @extends Ember.View
20123
+ @extends Ember.CoreView
19836
20124
  @uses Ember._Metamorph
19837
20125
  @private
19838
20126
  */
@@ -22648,7 +22936,6 @@ Ember.SelectOption = Ember.View.extend({
22648
22936
 
22649
22937
  ```html
22650
22938
  <select class="ember-select">
22651
- <option value>Please Select</option>
22652
22939
  <option value="1">Yehuda</option>
22653
22940
  <option value="2">Tom</option>
22654
22941
  </select>
@@ -22681,7 +22968,6 @@ Ember.SelectOption = Ember.View.extend({
22681
22968
 
22682
22969
  ```html
22683
22970
  <select class="ember-select">
22684
- <option value>Please Select</option>
22685
22971
  <option value="1">Yehuda</option>
22686
22972
  <option value="2" selected="selected">Tom</option>
22687
22973
  </select>
@@ -22719,7 +23005,6 @@ Ember.SelectOption = Ember.View.extend({
22719
23005
 
22720
23006
  ```html
22721
23007
  <select class="ember-select">
22722
- <option value>Please Select</option>
22723
23008
  <option value="1">Yehuda</option>
22724
23009
  <option value="2" selected="selected">Tom</option>
22725
23010
  </select>
@@ -22987,8 +23272,8 @@ function program3(depth0,data) {
22987
23272
  var selection = get(this, 'selection');
22988
23273
  var value = get(this, 'value');
22989
23274
 
22990
- if (selection) { this.selectionDidChange(); }
22991
- if (value) { this.valueDidChange(); }
23275
+ if (!Ember.isNone(selection)) { this.selectionDidChange(); }
23276
+ if (!Ember.isNone(value)) { this.valueDidChange(); }
22992
23277
 
22993
23278
  this._change();
22994
23279
  },
@@ -23185,6 +23470,31 @@ function bootstrap() {
23185
23470
  Ember.Handlebars.bootstrap( Ember.$(document) );
23186
23471
  }
23187
23472
 
23473
+ function registerComponents(container) {
23474
+ var templates = Ember.TEMPLATES, match;
23475
+ if (!templates) { return; }
23476
+
23477
+ for (var prop in templates) {
23478
+ if (match = prop.match(/^components\/(.*)$/)) {
23479
+ registerComponent(container, match[1]);
23480
+ }
23481
+ }
23482
+ }
23483
+
23484
+ function registerComponent(container, name) {
23485
+ Ember.assert("You provided a template named 'components/" + name + "', but custom components must include a '-'", name.match(/-/));
23486
+
23487
+ var className = name.replace(/-/g, '_');
23488
+ var Component = container.lookupFactory('component:' + className) || container.lookupFactory('component:' + name);
23489
+ var View = Component || Ember.Component.extend();
23490
+
23491
+ View.reopen({
23492
+ layoutName: 'components/' + name
23493
+ });
23494
+
23495
+ Ember.Handlebars.helper(name, View);
23496
+ }
23497
+
23188
23498
  /*
23189
23499
  We tie this to application.load to ensure that we've at least
23190
23500
  attempted to bootstrap at the point that the application is loaded.
@@ -23196,7 +23506,23 @@ function bootstrap() {
23196
23506
  from the DOM after processing.
23197
23507
  */
23198
23508
 
23199
- Ember.onLoad('application', bootstrap);
23509
+ Ember.onLoad('Ember.Application', function(Application) {
23510
+ if (Application.initializer) {
23511
+ Application.initializer({
23512
+ name: 'domTemplates',
23513
+ initialize: bootstrap
23514
+ });
23515
+
23516
+ Application.initializer({
23517
+ name: 'registerComponents',
23518
+ after: 'domTemplates',
23519
+ initialize: registerComponents
23520
+ });
23521
+ } else {
23522
+ // for ember-old-router
23523
+ Ember.onLoad('application', bootstrap);
23524
+ }
23525
+ });
23200
23526
 
23201
23527
  })();
23202
23528
 
@@ -23731,8 +24057,8 @@ define("route-recognizer",
23731
24057
 
23732
24058
  (function() {
23733
24059
  define("router",
23734
- ["route-recognizer"],
23735
- function(RouteRecognizer) {
24060
+ ["route-recognizer", "rsvp"],
24061
+ function(RouteRecognizer, RSVP) {
23736
24062
  "use strict";
23737
24063
  /**
23738
24064
  @private
@@ -23753,34 +24079,171 @@ define("router",
23753
24079
  */
23754
24080
 
23755
24081
 
23756
- function Router() {
23757
- this.recognizer = new RouteRecognizer();
23758
- }
23759
-
24082
+ var slice = Array.prototype.slice;
23760
24083
 
23761
- Router.prototype = {
23762
- /**
23763
- The main entry point into the router. The API is essentially
23764
- the same as the `map` method in `route-recognizer`.
23765
24084
 
23766
- This method extracts the String handler at the last `.to()`
23767
- call and uses it as the name of the whole route.
23768
24085
 
23769
- @param {Function} callback
23770
- */
23771
- map: function(callback) {
23772
- this.recognizer.delegate = this.delegate;
24086
+ /**
24087
+ @private
23773
24088
 
23774
- this.recognizer.map(callback, function(recognizer, route) {
23775
- var lastHandler = route[route.length - 1].handler;
23776
- var args = [route, { as: lastHandler }];
23777
- recognizer.add.apply(recognizer, args);
23778
- });
23779
- },
24089
+ A Transition is a thennable (a promise-like object) that represents
24090
+ an attempt to transition to another route. It can be aborted, either
24091
+ explicitly via `abort` or by attempting another transition while a
24092
+ previous one is still underway. An aborted transition can also
24093
+ be `retry()`d later.
24094
+ */
24095
+
24096
+ function Transition(router, promise) {
24097
+ this.router = router;
24098
+ this.promise = promise;
24099
+ this.data = {};
24100
+ this.resolvedModels = {};
24101
+ this.providedModels = {};
24102
+ this.providedModelsArray = [];
24103
+ this.sequence = ++Transition.currentSequence;
24104
+ this.params = {};
24105
+ }
24106
+
24107
+ Transition.currentSequence = 0;
24108
+
24109
+ Transition.prototype = {
24110
+ targetName: null,
24111
+ urlMethod: 'update',
24112
+ providedModels: null,
24113
+ resolvedModels: null,
24114
+ params: null,
23780
24115
 
23781
- hasRoute: function(route) {
23782
- return this.recognizer.hasRoute(route);
23783
- },
24116
+ /**
24117
+ The Transition's internal promise. Calling `.then` on this property
24118
+ is that same as calling `.then` on the Transition object itself, but
24119
+ this property is exposed for when you want to pass around a
24120
+ Transition's promise, but not the Transition object itself, since
24121
+ Transition object can be externally `abort`ed, while the promise
24122
+ cannot.
24123
+ */
24124
+ promise: null,
24125
+
24126
+ /**
24127
+ Custom state can be stored on a Transition's `data` object.
24128
+ This can be useful for decorating a Transition within an earlier
24129
+ hook and shared with a later hook. Properties set on `data` will
24130
+ be copied to new transitions generated by calling `retry` on this
24131
+ transition.
24132
+ */
24133
+ data: null,
24134
+
24135
+ /**
24136
+ A standard promise hook that resolves if the transition
24137
+ succeeds and rejects if it fails/redirects/aborts.
24138
+
24139
+ Forwards to the internal `promise` property which you can
24140
+ use in situations where you want to pass around a thennable,
24141
+ but not the Transition itself.
24142
+
24143
+ @param {Function} success
24144
+ @param {Function} failure
24145
+ */
24146
+ then: function(success, failure) {
24147
+ return this.promise.then(success, failure);
24148
+ },
24149
+
24150
+ /**
24151
+ Aborts the Transition. Note you can also implicitly abort a transition
24152
+ by initiating another transition while a previous one is underway.
24153
+ */
24154
+ abort: function() {
24155
+ if (this.isAborted) { return this; }
24156
+ log(this.router, this.sequence, this.targetName + ": transition was aborted");
24157
+ this.isAborted = true;
24158
+ this.router.activeTransition = null;
24159
+ return this;
24160
+ },
24161
+
24162
+ /**
24163
+ Retries a previously-aborted transition (making sure to abort the
24164
+ transition if it's still active). Returns a new transition that
24165
+ represents the new attempt to transition.
24166
+ */
24167
+ retry: function() {
24168
+ this.abort();
24169
+
24170
+ var recogHandlers = this.router.recognizer.handlersFor(this.targetName),
24171
+ newTransition = performTransition(this.router, recogHandlers, this.providedModelsArray, this.params, this.data);
24172
+
24173
+ return newTransition;
24174
+ },
24175
+
24176
+ /**
24177
+ Sets the URL-changing method to be employed at the end of a
24178
+ successful transition. By default, a new Transition will just
24179
+ use `updateURL`, but passing 'replace' to this method will
24180
+ cause the URL to update using 'replaceWith' instead. Omitting
24181
+ a parameter will disable the URL change, allowing for transitions
24182
+ that don't update the URL at completion (this is also used for
24183
+ handleURL, since the URL has already changed before the
24184
+ transition took place).
24185
+
24186
+ @param {String} method the type of URL-changing method to use
24187
+ at the end of a transition. Accepted values are 'replace',
24188
+ falsy values, or any other non-falsy value (which is
24189
+ interpreted as an updateURL transition).
24190
+
24191
+ @return {Transition} this transition
24192
+ */
24193
+ method: function(method) {
24194
+ this.urlMethod = method;
24195
+ return this;
24196
+ }
24197
+ };
24198
+
24199
+ function Router() {
24200
+ this.recognizer = new RouteRecognizer();
24201
+ }
24202
+
24203
+
24204
+
24205
+ /**
24206
+ Promise reject reasons passed to promise rejection
24207
+ handlers for failed transitions.
24208
+ */
24209
+ Router.UnrecognizedURLError = function(message) {
24210
+ this.message = (message || "UnrecognizedURLError");
24211
+ this.name = "UnrecognizedURLError";
24212
+ };
24213
+
24214
+ Router.TransitionAborted = function(message) {
24215
+ this.message = (message || "TransitionAborted");
24216
+ this.name = "TransitionAborted";
24217
+ };
24218
+
24219
+ function errorTransition(router, reason) {
24220
+ return new Transition(router, RSVP.reject(reason));
24221
+ }
24222
+
24223
+
24224
+ Router.prototype = {
24225
+ /**
24226
+ The main entry point into the router. The API is essentially
24227
+ the same as the `map` method in `route-recognizer`.
24228
+
24229
+ This method extracts the String handler at the last `.to()`
24230
+ call and uses it as the name of the whole route.
24231
+
24232
+ @param {Function} callback
24233
+ */
24234
+ map: function(callback) {
24235
+ this.recognizer.delegate = this.delegate;
24236
+
24237
+ this.recognizer.map(callback, function(recognizer, route) {
24238
+ var lastHandler = route[route.length - 1].handler;
24239
+ var args = [route, { as: lastHandler }];
24240
+ recognizer.add.apply(recognizer, args);
24241
+ });
24242
+ },
24243
+
24244
+ hasRoute: function(route) {
24245
+ return this.recognizer.hasRoute(route);
24246
+ },
23784
24247
 
23785
24248
  /**
23786
24249
  Clears the current and target route handlers and triggers exit
@@ -23788,7 +24251,8 @@ define("router",
23788
24251
  its ancestors.
23789
24252
  */
23790
24253
  reset: function() {
23791
- eachHandler(this.currentHandlerInfos || [], function(handler) {
24254
+ eachHandler(this.currentHandlerInfos || [], function(handlerInfo) {
24255
+ var handler = handlerInfo.handler;
23792
24256
  if (handler.exit) {
23793
24257
  handler.exit();
23794
24258
  }
@@ -23797,7 +24261,10 @@ define("router",
23797
24261
  this.targetHandlerInfos = null;
23798
24262
  },
23799
24263
 
24264
+ activeTransition: null,
24265
+
23800
24266
  /**
24267
+ var handler = handlerInfo.handler;
23801
24268
  The entry point for handling a change to the URL (usually
23802
24269
  via the back and forward button).
23803
24270
 
@@ -23809,13 +24276,9 @@ define("router",
23809
24276
  @return {Array} an Array of `[handler, parameter]` tuples
23810
24277
  */
23811
24278
  handleURL: function(url) {
23812
- var results = this.recognizer.recognize(url);
23813
-
23814
- if (!results) {
23815
- throw new Error("No route matched the URL '" + url + "'");
23816
- }
23817
-
23818
- collectObjects(this, results, 0, []);
24279
+ // Perform a URL-based transition, but don't change
24280
+ // the URL afterward, since it already happened.
24281
+ return doTransition(this, arguments).method(null);
23819
24282
  },
23820
24283
 
23821
24284
  /**
@@ -23847,8 +24310,7 @@ define("router",
23847
24310
  @param {String} name the name of the route
23848
24311
  */
23849
24312
  transitionTo: function(name) {
23850
- var args = Array.prototype.slice.call(arguments, 1);
23851
- doTransition(this, name, this.updateURL, args);
24313
+ return doTransition(this, arguments);
23852
24314
  },
23853
24315
 
23854
24316
  /**
@@ -23860,8 +24322,7 @@ define("router",
23860
24322
  @param {String} name the name of the route
23861
24323
  */
23862
24324
  replaceWith: function(name) {
23863
- var args = Array.prototype.slice.call(arguments, 1);
23864
- doTransition(this, name, this.replaceURL, args);
24325
+ return doTransition(this, arguments).method('replace');
23865
24326
  },
23866
24327
 
23867
24328
  /**
@@ -23875,8 +24336,7 @@ define("router",
23875
24336
  @return {Object} a serialized parameter hash
23876
24337
  */
23877
24338
  paramsForHandler: function(handlerName, callback) {
23878
- var output = this._paramsForHandler(handlerName, [].slice.call(arguments, 1));
23879
- return output.params;
24339
+ return paramsForHandler(this, handlerName, slice.call(arguments, 1));
23880
24340
  },
23881
24341
 
23882
24342
  /**
@@ -23890,109 +24350,17 @@ define("router",
23890
24350
  @return {String} a URL
23891
24351
  */
23892
24352
  generate: function(handlerName) {
23893
- var params = this.paramsForHandler.apply(this, arguments);
24353
+ var params = paramsForHandler(this, handlerName, slice.call(arguments, 1));
23894
24354
  return this.recognizer.generate(handlerName, params);
23895
24355
  },
23896
24356
 
23897
- /**
23898
- @private
23899
-
23900
- Used internally by `generate` and `transitionTo`.
23901
- */
23902
- _paramsForHandler: function(handlerName, objects, doUpdate) {
23903
- var handlers = this.recognizer.handlersFor(handlerName),
23904
- params = {},
23905
- toSetup = [],
23906
- startIdx = handlers.length,
23907
- objectsToMatch = objects.length,
23908
- object, objectChanged, handlerObj, handler, names, i;
23909
-
23910
- // Find out which handler to start matching at
23911
- for (i=handlers.length-1; i>=0 && objectsToMatch>0; i--) {
23912
- if (handlers[i].names.length) {
23913
- objectsToMatch--;
23914
- startIdx = i;
23915
- }
23916
- }
23917
-
23918
- if (objectsToMatch > 0) {
23919
- throw "More context objects were passed than there are dynamic segments for the route: "+handlerName;
23920
- }
23921
-
23922
- // Connect the objects to the routes
23923
- for (i=0; i<handlers.length; i++) {
23924
- handlerObj = handlers[i];
23925
- handler = this.getHandler(handlerObj.handler);
23926
- names = handlerObj.names;
23927
- objectChanged = false;
23928
-
23929
- // If it's a dynamic segment
23930
- if (names.length) {
23931
- // If we have objects, use them
23932
- if (i >= startIdx) {
23933
- object = objects.shift();
23934
- objectChanged = true;
23935
- // Otherwise use existing context
23936
- } else {
23937
- object = handler.context;
23938
- }
23939
-
23940
- // Serialize to generate params
23941
- if (handler.serialize) {
23942
- merge(params, handler.serialize(object, names));
23943
- }
23944
- // If it's not a dynamic segment and we're updating
23945
- } else if (doUpdate) {
23946
- // If we've passed the match point we need to deserialize again
23947
- // or if we never had a context
23948
- if (i > startIdx || !handler.hasOwnProperty('context')) {
23949
- if (handler.deserialize) {
23950
- object = handler.deserialize({});
23951
- objectChanged = true;
23952
- }
23953
- // Otherwise use existing context
23954
- } else {
23955
- object = handler.context;
23956
- }
23957
- }
23958
-
23959
- // Make sure that we update the context here so it's available to
23960
- // subsequent deserialize calls
23961
- if (doUpdate && objectChanged) {
23962
- // TODO: It's a bit awkward to set the context twice, see if we can DRY things up
23963
- setContext(handler, object);
23964
- }
23965
-
23966
- toSetup.push({
23967
- isDynamic: !!handlerObj.names.length,
23968
- name: handlerObj.handler,
23969
- handler: handler,
23970
- context: object
23971
- });
23972
-
23973
- if (i === handlers.length - 1) {
23974
- var lastHandler = toSetup[toSetup.length - 1],
23975
- additionalHandler;
23976
-
23977
- if (additionalHandler = lastHandler.handler.additionalHandler) {
23978
- handlers.push({
23979
- handler: additionalHandler.call(lastHandler.handler),
23980
- names: []
23981
- });
23982
- }
23983
- }
23984
- }
23985
-
23986
- return { params: params, toSetup: toSetup };
23987
- },
23988
-
23989
24357
  isActive: function(handlerName) {
23990
- var contexts = [].slice.call(arguments, 1);
24358
+ var contexts = slice.call(arguments, 1);
23991
24359
 
23992
24360
  var targetHandlerInfos = this.targetHandlerInfos,
23993
24361
  found = false, names, object, handlerInfo, handlerObj;
23994
24362
 
23995
- if (!targetHandlerInfos) { return; }
24363
+ if (!targetHandlerInfos) { return false; }
23996
24364
 
23997
24365
  for (var i=targetHandlerInfos.length-1; i>=0; i--) {
23998
24366
  handlerInfo = targetHandlerInfos[i];
@@ -24012,165 +24380,169 @@ define("router",
24012
24380
  },
24013
24381
 
24014
24382
  trigger: function(name) {
24015
- var args = [].slice.call(arguments);
24016
- trigger(this, args);
24017
- }
24018
- };
24383
+ var args = slice.call(arguments);
24384
+ trigger(this.currentHandlerInfos, false, args);
24385
+ },
24019
24386
 
24020
- function merge(hash, other) {
24021
- for (var prop in other) {
24022
- if (other.hasOwnProperty(prop)) { hash[prop] = other[prop]; }
24023
- }
24024
- }
24387
+ /**
24388
+ Hook point for logging transition status updates.
24025
24389
 
24026
- function isCurrent(currentHandlerInfos, handlerName) {
24027
- return currentHandlerInfos[currentHandlerInfos.length - 1].name === handlerName;
24028
- }
24390
+ @param {String} message The message to log.
24391
+ */
24392
+ log: null
24393
+ };
24029
24394
 
24030
24395
  /**
24031
24396
  @private
24032
24397
 
24033
- This function is called the first time the `collectObjects`
24034
- function encounters a promise while converting URL parameters
24035
- into objects.
24398
+ Used internally for both URL and named transition to determine
24399
+ a shared pivot parent route and other data necessary to perform
24400
+ a transition.
24401
+ */
24402
+ function getMatchPoint(router, handlers, objects, inputParams) {
24403
+
24404
+ var objectsToMatch = objects.length,
24405
+ matchPoint = handlers.length,
24406
+ providedModels = {}, i,
24407
+ currentHandlerInfos = router.currentHandlerInfos || [],
24408
+ params = {},
24409
+ oldParams = router.currentParams || {},
24410
+ activeTransition = router.activeTransition,
24411
+ handlerParams = {};
24412
+
24413
+ merge(params, inputParams);
24414
+
24415
+ for (i = handlers.length - 1; i >= 0; i--) {
24416
+ var handlerObj = handlers[i],
24417
+ handlerName = handlerObj.handler,
24418
+ oldHandlerInfo = currentHandlerInfos[i],
24419
+ hasChanged = false;
24036
24420
 
24037
- It triggers the `enter` and `setup` methods on the `loading`
24038
- handler.
24421
+ // Check if handler names have changed.
24422
+ if (!oldHandlerInfo || oldHandlerInfo.name !== handlerObj.handler) { hasChanged = true; }
24039
24423
 
24040
- @param {Router} router
24041
- */
24042
- function loading(router) {
24043
- if (!router.isLoading) {
24044
- router.isLoading = true;
24045
- var handler = router.getHandler('loading');
24046
-
24047
- if (handler) {
24048
- if (handler.enter) { handler.enter(); }
24049
- if (handler.setup) { handler.setup(); }
24050
- }
24051
- }
24052
- }
24424
+ if (handlerObj.isDynamic) {
24425
+ // URL transition.
24053
24426
 
24054
- /**
24055
- @private
24427
+ if (objectsToMatch > 0) {
24428
+ hasChanged = true;
24429
+ providedModels[handlerName] = objects[--objectsToMatch];
24430
+ } else {
24431
+ handlerParams[handlerName] = {};
24432
+ for (var prop in handlerObj.params) {
24433
+ if (!handlerObj.params.hasOwnProperty(prop)) { continue; }
24434
+ var newParam = handlerObj.params[prop];
24435
+ if (oldParams[prop] !== newParam) { hasChanged = true; }
24436
+ handlerParams[handlerName][prop] = params[prop] = newParam;
24437
+ }
24438
+ }
24439
+ } else if (handlerObj.hasOwnProperty('names') && handlerObj.names.length) {
24440
+ // Named transition.
24441
+
24442
+ if (objectsToMatch > 0) {
24443
+ hasChanged = true;
24444
+ providedModels[handlerName] = objects[--objectsToMatch];
24445
+ } else if (activeTransition && activeTransition.providedModels[handlerName]) {
24446
+
24447
+ // Use model from previous transition attempt, preferably the resolved one.
24448
+ hasChanged = true;
24449
+ providedModels[handlerName] = activeTransition.providedModels[handlerName] ||
24450
+ activeTransition.resolvedModels[handlerName];
24451
+ } else {
24452
+ var names = handlerObj.names;
24453
+ handlerParams[handlerName] = {};
24454
+ for (var j = 0, len = names.length; j < len; ++j) {
24455
+ var name = names[j];
24456
+ handlerParams[handlerName][name] = params[name] = oldParams[name];
24457
+ }
24458
+ }
24459
+ }
24056
24460
 
24057
- This function is called if a promise was previously
24058
- encountered once all promises are resolved.
24461
+ if (hasChanged) { matchPoint = i; }
24462
+ }
24059
24463
 
24060
- It triggers the `exit` method on the `loading` handler.
24464
+ if (objectsToMatch > 0) {
24465
+ throw "More context objects were passed than there are dynamic segments for the route: " + handlers[handlers.length - 1].handler;
24466
+ }
24061
24467
 
24062
- @param {Router} router
24063
- */
24064
- function loaded(router) {
24065
- router.isLoading = false;
24066
- var handler = router.getHandler('loading');
24067
- if (handler && handler.exit) { handler.exit(); }
24468
+ return { matchPoint: matchPoint, providedModels: providedModels, params: params, handlerParams: handlerParams };
24068
24469
  }
24069
24470
 
24070
24471
  /**
24071
24472
  @private
24072
24473
 
24073
- This function is called if any encountered promise
24074
- is rejected.
24075
-
24076
- It triggers the `exit` method on the `loading` handler,
24077
- the `enter` method on the `failure` handler, and the
24078
- `setup` method on the `failure` handler with the
24079
- `error`.
24474
+ This method takes a handler name and a list of contexts and returns
24475
+ a serialized parameter hash suitable to pass to `recognizer.generate()`.
24080
24476
 
24081
24477
  @param {Router} router
24082
- @param {Object} error the reason for the promise
24083
- rejection, to pass into the failure handler's
24084
- `setup` method.
24478
+ @param {String} handlerName
24479
+ @param {Array[Object]} objects
24480
+ @return {Object} a serialized parameter hash
24085
24481
  */
24086
- function failure(router, error) {
24087
- loaded(router);
24088
- var handler = router.getHandler('failure');
24089
- if (handler) {
24090
- if (handler.enter) { handler.enter(); }
24091
- if (handler.setup) { handler.setup(error); }
24482
+ function paramsForHandler(router, handlerName, objects) {
24483
+
24484
+ var handlers = router.recognizer.handlersFor(handlerName),
24485
+ params = {},
24486
+ matchPoint = getMatchPoint(router, handlers, objects).matchPoint,
24487
+ object, handlerObj, handler, names, i;
24488
+
24489
+ for (i=0; i<handlers.length; i++) {
24490
+ handlerObj = handlers[i];
24491
+ handler = router.getHandler(handlerObj.handler);
24492
+ names = handlerObj.names;
24493
+
24494
+ // If it's a dynamic segment
24495
+ if (names.length) {
24496
+ // If we have objects, use them
24497
+ if (i >= matchPoint) {
24498
+ object = objects.shift();
24499
+ // Otherwise use existing context
24500
+ } else {
24501
+ object = handler.context;
24502
+ }
24503
+
24504
+ // Serialize to generate params
24505
+ merge(params, serialize(handler, object, names));
24506
+ }
24507
+ }
24508
+ return params;
24509
+ }
24510
+
24511
+ function merge(hash, other) {
24512
+ for (var prop in other) {
24513
+ if (other.hasOwnProperty(prop)) { hash[prop] = other[prop]; }
24092
24514
  }
24093
24515
  }
24094
24516
 
24095
24517
  /**
24096
24518
  @private
24097
24519
  */
24098
- function doTransition(router, name, method, args) {
24099
- var output = router._paramsForHandler(name, args, true);
24100
- var params = output.params, toSetup = output.toSetup;
24520
+ function createNamedTransition(router, args) {
24521
+ var handlers = router.recognizer.handlersFor(args[0]);
24101
24522
 
24102
- var url = router.recognizer.generate(name, params);
24103
- method.call(router, url);
24523
+ log(router, "Attempting transition to " + args[0]);
24104
24524
 
24105
- setupContexts(router, toSetup);
24525
+ return performTransition(router, handlers, slice.call(args, 1), router.currentParams);
24106
24526
  }
24107
24527
 
24108
24528
  /**
24109
24529
  @private
24110
-
24111
- This function is called after a URL change has been handled
24112
- by `router.handleURL`.
24113
-
24114
- Takes an Array of `RecognizedHandler`s, and converts the raw
24115
- params hashes into deserialized objects by calling deserialize
24116
- on the handlers. This process builds up an Array of
24117
- `HandlerInfo`s. It then calls `setupContexts` with the Array.
24118
-
24119
- If the `deserialize` method on a handler returns a promise
24120
- (i.e. has a method called `then`), this function will pause
24121
- building up the `HandlerInfo` Array until the promise is
24122
- resolved. It will use the resolved value as the context of
24123
- `HandlerInfo`.
24124
24530
  */
24125
- function collectObjects(router, results, index, objects) {
24126
- if (results.length === index) {
24127
- var lastObject = objects[objects.length - 1],
24128
- lastHandler = lastObject && lastObject.handler;
24129
-
24130
- if (lastHandler && lastHandler.additionalHandler) {
24131
- var additionalResult = {
24132
- handler: lastHandler.additionalHandler(),
24133
- params: {},
24134
- isDynamic: false
24135
- };
24136
- results.push(additionalResult);
24137
- } else {
24138
- loaded(router);
24139
- setupContexts(router, objects);
24140
- return;
24141
- }
24142
- }
24531
+ function createURLTransition(router, url) {
24143
24532
 
24144
- var result = results[index];
24145
- var handler = router.getHandler(result.handler);
24146
- var object = handler.deserialize && handler.deserialize(result.params);
24533
+ var results = router.recognizer.recognize(url),
24534
+ currentHandlerInfos = router.currentHandlerInfos;
24147
24535
 
24148
- if (object && typeof object.then === 'function') {
24149
- loading(router);
24536
+ log(router, "Attempting URL transition to " + url);
24150
24537
 
24151
- // The chained `then` means that we can also catch errors that happen in `proceed`
24152
- object.then(proceed).then(null, function(error) {
24153
- failure(router, error);
24154
- });
24155
- } else {
24156
- proceed(object);
24538
+ if (!results) {
24539
+ return errorTransition(router, new Router.UnrecognizedURLError(url));
24157
24540
  }
24158
24541
 
24159
- function proceed(value) {
24160
- if (handler.context !== object) {
24161
- setContext(handler, object);
24162
- }
24163
-
24164
- var updatedObjects = objects.concat([{
24165
- context: value,
24166
- name: result.handler,
24167
- handler: router.getHandler(result.handler),
24168
- isDynamic: result.isDynamic
24169
- }]);
24170
- collectObjects(router, results, index + 1, updatedObjects);
24171
- }
24542
+ return performTransition(router, results, [], {});
24172
24543
  }
24173
24544
 
24545
+
24174
24546
  /**
24175
24547
  @private
24176
24548
 
@@ -24193,7 +24565,7 @@ define("router",
24193
24565
  Consider the following transitions:
24194
24566
 
24195
24567
  1. A URL transition to `/posts/1`.
24196
- 1. Triggers the `deserialize` callback on the
24568
+ 1. Triggers the `*model` callbacks on the
24197
24569
  `index`, `posts`, and `showPost` handlers
24198
24570
  2. Triggers the `enter` callback on the same
24199
24571
  3. Triggers the `setup` callback on the same
@@ -24209,16 +24581,17 @@ define("router",
24209
24581
  3. Triggers the `enter` callback on `about`
24210
24582
  4. Triggers the `setup` callback on `about`
24211
24583
 
24212
- @param {Router} router
24584
+ @param {Transition} transition
24213
24585
  @param {Array[HandlerInfo]} handlerInfos
24214
24586
  */
24215
- function setupContexts(router, handlerInfos) {
24216
- var partition =
24217
- partitionHandlers(router.currentHandlerInfos || [], handlerInfos);
24587
+ function setupContexts(transition, handlerInfos) {
24588
+ var router = transition.router,
24589
+ partition = partitionHandlers(router.currentHandlerInfos || [], handlerInfos);
24218
24590
 
24219
24591
  router.targetHandlerInfos = handlerInfos;
24220
24592
 
24221
- eachHandler(partition.exited, function(handler, context) {
24593
+ eachHandler(partition.exited, function(handlerInfo) {
24594
+ var handler = handlerInfo.handler;
24222
24595
  delete handler.context;
24223
24596
  if (handler.exit) { handler.exit(); }
24224
24597
  });
@@ -24226,33 +24599,51 @@ define("router",
24226
24599
  var currentHandlerInfos = partition.unchanged.slice();
24227
24600
  router.currentHandlerInfos = currentHandlerInfos;
24228
24601
 
24229
- eachHandler(partition.updatedContext, function(handler, context, handlerInfo) {
24230
- setContext(handler, context);
24231
- if (handler.setup) { handler.setup(context); }
24232
- currentHandlerInfos.push(handlerInfo);
24602
+ eachHandler(partition.updatedContext, function(handlerInfo) {
24603
+ handlerEnteredOrUpdated(transition, currentHandlerInfos, handlerInfo, false);
24604
+ });
24605
+
24606
+ eachHandler(partition.entered, function(handlerInfo) {
24607
+ handlerEnteredOrUpdated(transition, currentHandlerInfos, handlerInfo, true);
24233
24608
  });
24234
24609
 
24235
- var aborted = false;
24236
- eachHandler(partition.entered, function(handler, context, handlerInfo) {
24237
- if (aborted) { return; }
24238
- if (handler.enter) { handler.enter(); }
24610
+ if (router.didTransition) {
24611
+ router.didTransition(handlerInfos);
24612
+ }
24613
+ }
24614
+
24615
+ /**
24616
+ @private
24617
+
24618
+ Helper method used by setupContexts. Handles errors or redirects
24619
+ that may happen in enter/setup.
24620
+ */
24621
+ function handlerEnteredOrUpdated(transition, currentHandlerInfos, handlerInfo, enter) {
24622
+ var handler = handlerInfo.handler,
24623
+ context = handlerInfo.context;
24624
+
24625
+ try {
24626
+ if (enter && handler.enter) { handler.enter(); }
24627
+ checkAbort(transition);
24628
+
24239
24629
  setContext(handler, context);
24240
- if (handler.setup) {
24241
- if (false === handler.setup(context)) {
24242
- aborted = true;
24243
- }
24244
- }
24245
24630
 
24246
- if (!aborted) {
24247
- currentHandlerInfos.push(handlerInfo);
24631
+ if (handler.setup) { handler.setup(context); }
24632
+ checkAbort(transition);
24633
+ } catch(e) {
24634
+ if (!(e instanceof Router.TransitionAborted)) {
24635
+ // Trigger the `error` event starting from this failed handler.
24636
+ trigger(currentHandlerInfos.concat(handlerInfo), true, ['error', e, transition]);
24248
24637
  }
24249
- });
24250
24638
 
24251
- if (!aborted && router.didTransition) {
24252
- router.didTransition(handlerInfos);
24639
+ // Propagate the error so that the transition promise will reject.
24640
+ throw e;
24253
24641
  }
24642
+
24643
+ currentHandlerInfos.push(handlerInfo);
24254
24644
  }
24255
24645
 
24646
+
24256
24647
  /**
24257
24648
  @private
24258
24649
 
@@ -24264,11 +24655,7 @@ define("router",
24264
24655
  */
24265
24656
  function eachHandler(handlerInfos, callback) {
24266
24657
  for (var i=0, l=handlerInfos.length; i<l; i++) {
24267
- var handlerInfo = handlerInfos[i],
24268
- handler = handlerInfo.handler,
24269
- context = handlerInfo.context;
24270
-
24271
- callback(handler, context, handlerInfo);
24658
+ callback(handlerInfos[i]);
24272
24659
  }
24273
24660
  }
24274
24661
 
@@ -24348,19 +24735,19 @@ define("router",
24348
24735
  return handlers;
24349
24736
  }
24350
24737
 
24351
- function trigger(router, args) {
24352
- var currentHandlerInfos = router.currentHandlerInfos;
24738
+ function trigger(handlerInfos, ignoreFailure, args) {
24353
24739
 
24354
24740
  var name = args.shift();
24355
24741
 
24356
- if (!currentHandlerInfos) {
24742
+ if (!handlerInfos) {
24743
+ if (ignoreFailure) { return; }
24357
24744
  throw new Error("Could not trigger event '" + name + "'. There are no active handlers");
24358
24745
  }
24359
24746
 
24360
24747
  var eventWasHandled = false;
24361
24748
 
24362
- for (var i=currentHandlerInfos.length-1; i>=0; i--) {
24363
- var handlerInfo = currentHandlerInfos[i],
24749
+ for (var i=handlerInfos.length-1; i>=0; i--) {
24750
+ var handlerInfo = handlerInfos[i],
24364
24751
  handler = handlerInfo.handler;
24365
24752
 
24366
24753
  if (handler.events && handler.events[name]) {
@@ -24372,7 +24759,7 @@ define("router",
24372
24759
  }
24373
24760
  }
24374
24761
 
24375
- if (!eventWasHandled) {
24762
+ if (!eventWasHandled && !ignoreFailure) {
24376
24763
  throw new Error("Nothing handled the event '" + name + "'.");
24377
24764
  }
24378
24765
  }
@@ -24381,10 +24768,372 @@ define("router",
24381
24768
  handler.context = context;
24382
24769
  if (handler.contextDidChange) { handler.contextDidChange(); }
24383
24770
  }
24771
+
24772
+ /**
24773
+ @private
24774
+
24775
+ Creates, begins, and returns a Transition.
24776
+ */
24777
+ function performTransition(router, recogHandlers, providedModelsArray, params, data) {
24778
+
24779
+ var matchPointResults = getMatchPoint(router, recogHandlers, providedModelsArray, params),
24780
+ targetName = recogHandlers[recogHandlers.length - 1].handler,
24781
+ wasTransitioning = false;
24782
+
24783
+ // Check if there's already a transition underway.
24784
+ if (router.activeTransition) {
24785
+ if (transitionsIdentical(router.activeTransition, targetName, providedModelsArray)) {
24786
+ return router.activeTransition;
24787
+ }
24788
+ router.activeTransition.abort();
24789
+ wasTransitioning = true;
24790
+ }
24791
+
24792
+ var deferred = RSVP.defer(),
24793
+ transition = new Transition(router, deferred.promise);
24794
+
24795
+ transition.targetName = targetName;
24796
+ transition.providedModels = matchPointResults.providedModels;
24797
+ transition.providedModelsArray = providedModelsArray;
24798
+ transition.params = matchPointResults.params;
24799
+ transition.data = data || {};
24800
+ router.activeTransition = transition;
24801
+
24802
+ var handlerInfos = generateHandlerInfos(router, recogHandlers);
24803
+
24804
+ // Fire 'willTransition' event on current handlers, but don't fire it
24805
+ // if a transition was already underway.
24806
+ if (!wasTransitioning) {
24807
+ trigger(router.currentHandlerInfos, true, ['willTransition', transition]);
24808
+ }
24809
+
24810
+ log(router, transition.sequence, "Beginning validation for transition to " + transition.targetName);
24811
+ validateEntry(transition, handlerInfos, 0, matchPointResults.matchPoint, matchPointResults.handlerParams)
24812
+ .then(transitionSuccess, transitionFailure);
24813
+
24814
+ return transition;
24815
+
24816
+ function transitionSuccess() {
24817
+ checkAbort(transition);
24818
+
24819
+ try {
24820
+ finalizeTransition(transition, handlerInfos);
24821
+
24822
+ // Resolve with the final handler.
24823
+ deferred.resolve(handlerInfos[handlerInfos.length - 1].handler);
24824
+ } catch(e) {
24825
+ deferred.reject(e);
24826
+ }
24827
+
24828
+ // Don't nullify if another transition is underway (meaning
24829
+ // there was a transition initiated with enter/setup).
24830
+ if (!transition.isAborted) {
24831
+ router.activeTransition = null;
24832
+ }
24833
+ }
24834
+
24835
+ function transitionFailure(reason) {
24836
+ deferred.reject(reason);
24837
+ }
24838
+ }
24839
+
24840
+ /**
24841
+ @private
24842
+
24843
+ Accepts handlers in Recognizer format, either returned from
24844
+ recognize() or handlersFor(), and returns unified
24845
+ `HandlerInfo`s.
24846
+ */
24847
+ function generateHandlerInfos(router, recogHandlers) {
24848
+ var handlerInfos = [];
24849
+ for (var i = 0, len = recogHandlers.length; i < len; ++i) {
24850
+ var handlerObj = recogHandlers[i],
24851
+ isDynamic = handlerObj.isDynamic || (handlerObj.names && handlerObj.names.length);
24852
+
24853
+ handlerInfos.push({
24854
+ isDynamic: !!isDynamic,
24855
+ name: handlerObj.handler,
24856
+ handler: router.getHandler(handlerObj.handler)
24857
+ });
24858
+ }
24859
+ return handlerInfos;
24860
+ }
24861
+
24862
+ /**
24863
+ @private
24864
+ */
24865
+ function transitionsIdentical(oldTransition, targetName, providedModelsArray) {
24866
+
24867
+ if (oldTransition.targetName !== targetName) { return false; }
24868
+
24869
+ var oldModels = oldTransition.providedModelsArray;
24870
+ if (oldModels.length !== providedModelsArray.length) { return false; }
24871
+
24872
+ for (var i = 0, len = oldModels.length; i < len; ++i) {
24873
+ if (oldModels[i] !== providedModelsArray[i]) { return false; }
24874
+ }
24875
+ return true;
24876
+ }
24877
+
24878
+ /**
24879
+ @private
24880
+
24881
+ Updates the URL (if necessary) and calls `setupContexts`
24882
+ to update the router's array of `currentHandlerInfos`.
24883
+ */
24884
+ function finalizeTransition(transition, handlerInfos) {
24885
+
24886
+ var router = transition.router,
24887
+ seq = transition.sequence,
24888
+ handlerName = handlerInfos[handlerInfos.length - 1].name;
24889
+
24890
+ log(router, seq, "Validation succeeded, finalizing transition;");
24891
+
24892
+ // Collect params for URL.
24893
+ var objects = [];
24894
+ for (var i = 0, len = handlerInfos.length; i < len; ++i) {
24895
+ var handlerInfo = handlerInfos[i];
24896
+ if (handlerInfo.isDynamic) {
24897
+ objects.push(handlerInfo.context);
24898
+ }
24899
+ }
24900
+
24901
+ var params = paramsForHandler(router, handlerName, objects);
24902
+
24903
+ transition.providedModelsArray = [];
24904
+ transition.providedContexts = {};
24905
+ router.currentParams = params;
24906
+
24907
+ var urlMethod = transition.urlMethod;
24908
+ if (urlMethod) {
24909
+ var url = router.recognizer.generate(handlerName, params);
24910
+
24911
+ if (urlMethod === 'replace') {
24912
+ router.replaceURL(url);
24913
+ } else {
24914
+ // Assume everything else is just a URL update for now.
24915
+ router.updateURL(url);
24916
+ }
24917
+ }
24918
+
24919
+ setupContexts(transition, handlerInfos);
24920
+ log(router, seq, "TRANSITION COMPLETE.");
24921
+ }
24922
+
24923
+ /**
24924
+ @private
24925
+
24926
+ Internal function used to construct the chain of promises used
24927
+ to validate a transition. Wraps calls to `beforeModel`, `model`,
24928
+ and `afterModel` in promises, and checks for redirects/aborts
24929
+ between each.
24930
+ */
24931
+ function validateEntry(transition, handlerInfos, index, matchPoint, handlerParams) {
24932
+
24933
+ if (index === handlerInfos.length) {
24934
+ // No more contexts to resolve.
24935
+ return RSVP.resolve(transition.resolvedModels);
24936
+ }
24937
+
24938
+ var router = transition.router,
24939
+ handlerInfo = handlerInfos[index],
24940
+ handler = handlerInfo.handler,
24941
+ handlerName = handlerInfo.name,
24942
+ seq = transition.sequence,
24943
+ errorAlreadyHandled = false,
24944
+ resolvedModel;
24945
+
24946
+ if (index < matchPoint) {
24947
+ log(router, seq, handlerName + ": using context from already-active handler");
24948
+
24949
+ // We're before the match point, so don't run any hooks,
24950
+ // just use the already resolved context from the handler.
24951
+ resolvedModel = handlerInfo.handler.context;
24952
+ return proceed();
24953
+ }
24954
+
24955
+ return RSVP.resolve().then(handleAbort)
24956
+ .then(beforeModel)
24957
+ .then(null, handleError)
24958
+ .then(handleAbort)
24959
+ .then(model)
24960
+ .then(null, handleError)
24961
+ .then(handleAbort)
24962
+ .then(afterModel)
24963
+ .then(null, handleError)
24964
+ .then(handleAbort)
24965
+ .then(proceed);
24966
+
24967
+ function handleAbort(result) {
24968
+
24969
+ if (transition.isAborted) {
24970
+ log(transition.router, transition.sequence, "detected abort.");
24971
+ errorAlreadyHandled = true;
24972
+ return RSVP.reject(new Router.TransitionAborted());
24973
+ }
24974
+
24975
+ return result;
24976
+ }
24977
+
24978
+ function handleError(reason) {
24979
+
24980
+ if (errorAlreadyHandled) { return RSVP.reject(reason); }
24981
+ errorAlreadyHandled = true;
24982
+ transition.abort();
24983
+
24984
+ log(router, seq, handlerName + ": handling error: " + reason);
24985
+
24986
+ // An error was thrown / promise rejected, so fire an
24987
+ // `error` event from this handler info up to root.
24988
+ trigger(handlerInfos.slice(0, index + 1), true, ['error', reason, transition]);
24989
+
24990
+ if (handler.error) {
24991
+ handler.error(reason, transition); }
24992
+
24993
+
24994
+ // Propagate the original error.
24995
+ return RSVP.reject(reason);
24996
+ }
24997
+
24998
+ function beforeModel() {
24999
+
25000
+ log(router, seq, handlerName + ": calling beforeModel hook");
25001
+
25002
+ return handler.beforeModel && handler.beforeModel(transition);
25003
+ }
25004
+
25005
+ function model() {
25006
+ log(router, seq, handlerName + ": resolving model");
25007
+
25008
+ return getModel(handlerInfo, transition, handlerParams[handlerName], index >= matchPoint);
25009
+ }
25010
+
25011
+ function afterModel(context) {
25012
+
25013
+ log(router, seq, handlerName + ": calling afterModel hook");
25014
+
25015
+ // Pass the context and resolved parent contexts to afterModel, but we don't
25016
+ // want to use the value returned from `afterModel` in any way, but rather
25017
+ // always resolve with the original `context` object.
25018
+
25019
+ resolvedModel = context;
25020
+ return handler.afterModel && handler.afterModel(resolvedModel, transition);
25021
+ }
25022
+
25023
+ function proceed() {
25024
+ log(router, seq, handlerName + ": validation succeeded, proceeding");
25025
+
25026
+ handlerInfo.context = transition.resolvedModels[handlerInfo.name] = resolvedModel;
25027
+ return validateEntry(transition, handlerInfos, index + 1, matchPoint, handlerParams);
25028
+ }
25029
+ }
25030
+
25031
+ /**
25032
+ @private
25033
+
25034
+ Throws a TransitionAborted if the provided transition has been aborted.
25035
+ */
25036
+ function checkAbort(transition) {
25037
+ if (transition.isAborted) {
25038
+ log(transition.router, transition.sequence, "detected abort.");
25039
+ throw new Router.TransitionAborted();
25040
+ }
25041
+ }
25042
+
25043
+ /**
25044
+ @private
25045
+
25046
+ Encapsulates the logic for whether to call `model` on a route,
25047
+ or use one of the models provided to `transitionTo`.
25048
+ */
25049
+ function getModel(handlerInfo, transition, handlerParams, needsUpdate) {
25050
+
25051
+ var handler = handlerInfo.handler,
25052
+ handlerName = handlerInfo.name;
25053
+
25054
+ if (!needsUpdate && handler.hasOwnProperty('context')) {
25055
+ return handler.context;
25056
+ }
25057
+
25058
+ if (handlerInfo.isDynamic && transition.providedModels.hasOwnProperty(handlerName)) {
25059
+ var providedModel = transition.providedModels[handlerName];
25060
+ return typeof providedModel === 'function' ? providedModel() : providedModel;
25061
+ }
25062
+
25063
+ return handler.model && handler.model(handlerParams || {}, transition);
25064
+ }
25065
+
25066
+ /**
25067
+ @private
25068
+ */
25069
+ function log(router, sequence, msg) {
25070
+
25071
+ if (!router.log) { return; }
25072
+
25073
+ if (arguments.length === 3) {
25074
+ router.log("Transition #" + sequence + ": " + msg);
25075
+ } else {
25076
+ msg = sequence;
25077
+ router.log(msg);
25078
+ }
25079
+ }
25080
+
25081
+ /**
25082
+ @private
25083
+
25084
+ Begins and returns a Transition based on the provided
25085
+ arguments. Accepts arguments in the form of both URL
25086
+ transitions and named transitions.
25087
+
25088
+ @param {Router} router
25089
+ @param {Array[Object]} args arguments passed to transitionTo,
25090
+ replaceWith, or handleURL
25091
+ */
25092
+ function doTransition(router, args) {
25093
+ // Normalize blank transitions to root URL transitions.
25094
+ var name = args[0] || '/';
25095
+
25096
+ if (name.charAt(0) === '/') {
25097
+ return createURLTransition(router, name);
25098
+ } else {
25099
+ return createNamedTransition(router, args);
25100
+ }
25101
+ }
25102
+
25103
+ /**
25104
+ @private
25105
+
25106
+ Serializes a handler using its custom `serialize` method or
25107
+ by a default that looks up the expected property name from
25108
+ the dynamic segment.
25109
+
25110
+ @param {Object} handler a router handler
25111
+ @param {Object} model the model to be serialized for this handler
25112
+ @param {Array[Object]} names the names array attached to an
25113
+ handler object returned from router.recognizer.handlersFor()
25114
+ */
25115
+ function serialize(handler, model, names) {
25116
+
25117
+ // Use custom serialize if it exists.
25118
+ if (handler.serialize) {
25119
+ return handler.serialize(model, names);
25120
+ }
25121
+
25122
+ if (names.length !== 1) { return; }
25123
+
25124
+ var name = names[0], object = {};
25125
+
25126
+ if (/_id$/.test(name)) {
25127
+ object[name] = model.id;
25128
+ } else {
25129
+ object[name] = model;
25130
+ }
25131
+ return object;
25132
+ }
25133
+
24384
25134
  return Router;
24385
25135
  });
24386
25136
 
24387
-
24388
25137
  })();
24389
25138
 
24390
25139
 
@@ -24570,7 +25319,7 @@ Ember.Router = Ember.Object.extend({
24570
25319
  location: 'hash',
24571
25320
 
24572
25321
  init: function() {
24573
- this.router = this.constructor.router;
25322
+ this.router = this.constructor.router || this.constructor.map(Ember.K);
24574
25323
  this._activeViews = {};
24575
25324
  setupLocation(this);
24576
25325
  },
@@ -24615,34 +25364,21 @@ Ember.Router = Ember.Object.extend({
24615
25364
  },
24616
25365
 
24617
25366
  handleURL: function(url) {
24618
- this.router.handleURL(url);
24619
- this.notifyPropertyChange('url');
24620
- },
24621
-
24622
- /**
24623
- Transition to another route via the `routeTo` event which
24624
- will by default be handled by ApplicationRoute.
25367
+ scheduleLoadingStateEntry(this);
24625
25368
 
24626
- @method routeTo
24627
- @param {TransitionEvent} transitionEvent
24628
- */
24629
- routeTo: function(transitionEvent) {
24630
- var handlerInfos = this.router.currentHandlerInfos;
24631
- if (handlerInfos) {
24632
- transitionEvent.sourceRoute = handlerInfos[handlerInfos.length - 1].handler;
24633
- }
25369
+ var self = this;
24634
25370
 
24635
- this.send('routeTo', transitionEvent);
25371
+ return this.router.handleURL(url).then(function() {
25372
+ transitionCompleted(self);
25373
+ });
24636
25374
  },
24637
25375
 
24638
- transitionTo: function(name) {
24639
- var args = [].slice.call(arguments);
24640
- doTransition(this, 'transitionTo', args);
25376
+ transitionTo: function() {
25377
+ return doTransition(this, 'transitionTo', arguments);
24641
25378
  },
24642
25379
 
24643
25380
  replaceWith: function() {
24644
- var args = [].slice.call(arguments);
24645
- doTransition(this, 'replaceWith', args);
25381
+ return doTransition(this, 'replaceWith', arguments);
24646
25382
  },
24647
25383
 
24648
25384
  generate: function() {
@@ -24696,17 +25432,6 @@ Ember.Router = Ember.Object.extend({
24696
25432
  }
24697
25433
  });
24698
25434
 
24699
- Ember.Router.reopenClass({
24700
- defaultFailureHandler: {
24701
- setup: function(error) {
24702
- Ember.Logger.error('Error while loading route:', error);
24703
-
24704
- // Using setTimeout allows us to escape from the Promise's try/catch block
24705
- setTimeout(function() { throw error; });
24706
- }
24707
- }
24708
- });
24709
-
24710
25435
  function getHandlerFunction(router) {
24711
25436
  var seen = {}, container = router.container,
24712
25437
  DefaultRoute = container.resolve('route:basic');
@@ -24721,7 +25446,6 @@ function getHandlerFunction(router) {
24721
25446
 
24722
25447
  if (!handler) {
24723
25448
  if (name === 'loading') { return {}; }
24724
- if (name === 'failure') { return router.constructor.defaultFailureHandler; }
24725
25449
 
24726
25450
  container.register(routeName, DefaultRoute.extend());
24727
25451
  handler = container.lookup(routeName);
@@ -24732,9 +25456,9 @@ function getHandlerFunction(router) {
24732
25456
  }
24733
25457
 
24734
25458
  if (name === 'application') {
24735
- // Inject default `routeTo` handler.
25459
+ // Inject default `error` handler.
24736
25460
  handler.events = handler.events || {};
24737
- handler.events.routeTo = handler.events.routeTo || Ember.TransitionEvent.defaultHandler;
25461
+ handler.events.error = handler.events.error || defaultErrorHandler;
24738
25462
  }
24739
25463
 
24740
25464
  handler.routeName = name;
@@ -24742,6 +25466,14 @@ function getHandlerFunction(router) {
24742
25466
  };
24743
25467
  }
24744
25468
 
25469
+ function defaultErrorHandler(error, transition) {
25470
+ Ember.Logger.error('Error while loading route:', error);
25471
+
25472
+ // Using setTimeout allows us to escape from the Promise's try/catch block
25473
+ setTimeout(function() { throw error; });
25474
+ }
25475
+
25476
+
24745
25477
  function routePath(handlerInfos) {
24746
25478
  var path = [];
24747
25479
 
@@ -24786,24 +25518,76 @@ function setupRouter(emberRouter, router, location) {
24786
25518
  }
24787
25519
 
24788
25520
  function doTransition(router, method, args) {
25521
+ // Normalize blank route to root URL.
25522
+ args = [].slice.call(args);
25523
+ args[0] = args[0] || '/';
25524
+
24789
25525
  var passedName = args[0], name;
24790
25526
 
24791
- if (!router.router.hasRoute(args[0])) {
24792
- name = args[0] = passedName + '.index';
24793
- } else {
25527
+ if (passedName.charAt(0) === '/') {
24794
25528
  name = passedName;
25529
+ } else {
25530
+ if (!router.router.hasRoute(passedName)) {
25531
+ name = args[0] = passedName + '.index';
25532
+ } else {
25533
+ name = passedName;
25534
+ }
25535
+
25536
+ Ember.assert("The route " + passedName + " was not found", router.router.hasRoute(name));
25537
+ }
25538
+
25539
+ scheduleLoadingStateEntry(router);
25540
+
25541
+ var transitionPromise = router.router[method].apply(router.router, args);
25542
+ transitionPromise.then(function() {
25543
+ transitionCompleted(router);
25544
+ });
25545
+
25546
+ // We want to return the configurable promise object
25547
+ // so that callers of this function can use `.method()` on it,
25548
+ // which obviously doesn't exist for normal RSVP promises.
25549
+ return transitionPromise;
25550
+ }
25551
+
25552
+ function scheduleLoadingStateEntry(router) {
25553
+ if (router._loadingStateActive) { return; }
25554
+ router._shouldEnterLoadingState = true;
25555
+ Ember.run.scheduleOnce('routerTransitions', null, enterLoadingState, router);
25556
+ }
25557
+
25558
+ function enterLoadingState(router) {
25559
+ if (router._loadingStateActive || !router._shouldEnterLoadingState) { return; }
25560
+
25561
+ var loadingRoute = router.router.getHandler('loading');
25562
+ if (loadingRoute) {
25563
+ if (loadingRoute.enter) { loadingRoute.enter(); }
25564
+ if (loadingRoute.setup) { loadingRoute.setup(); }
25565
+ router._loadingStateActive = true;
24795
25566
  }
25567
+ }
25568
+
25569
+ function exitLoadingState(router) {
25570
+ router._shouldEnterLoadingState = false;
25571
+ if (!router._loadingStateActive) { return; }
24796
25572
 
24797
- Ember.assert("The route " + passedName + " was not found", router.router.hasRoute(name));
25573
+ var loadingRoute = router.router.getHandler('loading');
25574
+ if (loadingRoute && loadingRoute.exit) { loadingRoute.exit(); }
25575
+ router._loadingStateActive = false;
25576
+ }
24798
25577
 
24799
- router.router[method].apply(router.router, args);
25578
+ function transitionCompleted(router) {
24800
25579
  router.notifyPropertyChange('url');
25580
+ exitLoadingState(router);
24801
25581
  }
24802
25582
 
24803
25583
  Ember.Router.reopenClass({
24804
25584
  map: function(callback) {
24805
25585
  var router = this.router = new Router();
24806
25586
 
25587
+ if (get(this, 'namespace.LOG_TRANSITIONS_INTERNAL')) {
25588
+ router.log = Ember.Logger.debug;
25589
+ }
25590
+
24807
25591
  var dsl = Ember.RouterDSL.map(function() {
24808
25592
  this.resource('application', { path: "/" }, function() {
24809
25593
  callback.call(this);
@@ -24815,6 +25599,7 @@ Ember.Router.reopenClass({
24815
25599
  }
24816
25600
  });
24817
25601
 
25602
+
24818
25603
  })();
24819
25604
 
24820
25605
 
@@ -24827,7 +25612,9 @@ Ember.Router.reopenClass({
24827
25612
 
24828
25613
  var get = Ember.get, set = Ember.set,
24829
25614
  classify = Ember.String.classify,
24830
- fmt = Ember.String.fmt;
25615
+ fmt = Ember.String.fmt,
25616
+ a_forEach = Ember.EnumerableUtils.forEach,
25617
+ a_replace = Ember.EnumerableUtils.replace;
24831
25618
 
24832
25619
  /**
24833
25620
  The `Ember.Route` class is used to define individual routes. Refer to
@@ -24845,7 +25632,7 @@ Ember.Route = Ember.Object.extend({
24845
25632
  */
24846
25633
  exit: function() {
24847
25634
  this.deactivate();
24848
- teardownView(this);
25635
+ this.teardownViews();
24849
25636
  },
24850
25637
 
24851
25638
  /**
@@ -24869,6 +25656,104 @@ Ember.Route = Ember.Object.extend({
24869
25656
 
24870
25657
  The context of the event will be this route.
24871
25658
 
25659
+ ## Bubbling
25660
+
25661
+ By default, an event will stop bubbling once a handler defined
25662
+ on the `events` hash handles it. To continue bubbling the event,
25663
+ you must return `true` from the handler.
25664
+
25665
+ ## Built-in events
25666
+
25667
+ There are a few built-in events pertaining to transitions that you
25668
+ can use to customize transition behavior: `willTransition` and
25669
+ `error`.
25670
+
25671
+ ### `willTransition`
25672
+
25673
+ The `willTransition` event is fired at the beginning of any
25674
+ attempted transition with a `Transition` object as the sole
25675
+ argument. This event can be used for aborting, redirecting,
25676
+ or decorating the transition from the currently active routes.
25677
+
25678
+ A good example is preventing navigation when a form is
25679
+ half-filled out:
25680
+
25681
+ ```js
25682
+ App.ContactFormRoute = Ember.Route.extend({
25683
+ events: {
25684
+ willTransition: function(transition) {
25685
+ if (this.controller.get('userHasEnteredData')) {
25686
+ this.controller.displayNavigationConfirm();
25687
+ transition.abort();
25688
+ }
25689
+ }
25690
+ }
25691
+ });
25692
+ ```
25693
+
25694
+ You can also redirect elsewhere by calling
25695
+ `this.transitionTo('elsewhere')` from within `willTransition`.
25696
+ Note that `willTransition` will not be fired for the
25697
+ redirecting `transitionTo`, since `willTransition` doesn't
25698
+ fire when there is already a transition underway. If you want
25699
+ subsequent `willTransition` events to fire for the redirecting
25700
+ transition, you must first explicitly call
25701
+ `transition.abort()`.
25702
+
25703
+ ### `error`
25704
+
25705
+ When attempting to transition into a route, any of the hooks
25706
+ may throw an error, or return a promise that rejects, at which
25707
+ point an `error` event will be fired on the partially-entered
25708
+ routes, allowing for per-route error handling logic, or shared
25709
+ error handling logic defined on a parent route.
25710
+
25711
+ Here is an example of an error handler that will be invoked
25712
+ for rejected promises / thrown errors from the various hooks
25713
+ on the route, as well as any unhandled errors from child
25714
+ routes:
25715
+
25716
+ ```js
25717
+ App.AdminRoute = Ember.Route.extend({
25718
+ beforeModel: function() {
25719
+ throw "bad things!";
25720
+ // ...or, equivalently:
25721
+ return Ember.RSVP.reject("bad things!");
25722
+ },
25723
+
25724
+ events: {
25725
+ error: function(error, transition) {
25726
+ // Assuming we got here due to the error in `beforeModel`,
25727
+ // we can expect that error === "bad things!",
25728
+ // but a promise model rejecting would also
25729
+ // call this hook, as would any errors encountered
25730
+ // in `afterModel`.
25731
+
25732
+ // The `error` hook is also provided the failed
25733
+ // `transition`, which can be stored and later
25734
+ // `.retry()`d if desired.
25735
+
25736
+ this.transitionTo('login');
25737
+ }
25738
+ }
25739
+ });
25740
+ ```
25741
+
25742
+ `error` events that bubble up all the way to `ApplicationRoute`
25743
+ will fire a default error handler that logs the error. You can
25744
+ specify your own global default error handler by overriding the
25745
+ `error` handler on `ApplicationRoute`:
25746
+
25747
+ ```js
25748
+ App.ApplicationRoute = Ember.Route.extend({
25749
+ events: {
25750
+ error: function(error, transition) {
25751
+ this.controllerFor('banner').displayError(error.message);
25752
+ }
25753
+ }
25754
+ });
25755
+ ```
25756
+
24872
25757
  @see {Ember.Route#send}
24873
25758
  @see {Handlebars.helpers.action}
24874
25759
 
@@ -24894,17 +25779,6 @@ Ember.Route = Ember.Object.extend({
24894
25779
  */
24895
25780
  activate: Ember.K,
24896
25781
 
24897
- /**
24898
- Transition to another route via the `routeTo` event which
24899
- will by default be handled by ApplicationRoute.
24900
-
24901
- @method routeTo
24902
- @param {TransitionEvent} transitionEvent
24903
- */
24904
- routeTo: function(transitionEvent) {
24905
- this.router.routeTo(transitionEvent);
24906
- },
24907
-
24908
25782
  /**
24909
25783
  Transition into another route. Optionally supply a model for the
24910
25784
  route in question. The model will be serialized into the URL
@@ -24916,13 +25790,6 @@ Ember.Route = Ember.Object.extend({
24916
25790
  */
24917
25791
  transitionTo: function(name, context) {
24918
25792
  var router = this.router;
24919
-
24920
- // If the transition is a no-op, just bail.
24921
- if (router.isActive.apply(router, arguments)) {
24922
- return;
24923
- }
24924
-
24925
- if (this._checkingRedirect) { this._redirected[this._redirectDepth] = true; }
24926
25793
  return router.transitionTo.apply(router, arguments);
24927
25794
  },
24928
25795
 
@@ -24939,13 +25806,6 @@ Ember.Route = Ember.Object.extend({
24939
25806
  */
24940
25807
  replaceWith: function() {
24941
25808
  var router = this.router;
24942
-
24943
- // If the transition is a no-op, just bail.
24944
- if (router.isActive.apply(router, arguments)) {
24945
- return;
24946
- }
24947
-
24948
- if (this._checkingRedirect) { this._redirected[this._redirectDepth] = true; }
24949
25809
  return this.router.replaceWith.apply(this.router, arguments);
24950
25810
  },
24951
25811
 
@@ -24953,15 +25813,6 @@ Ember.Route = Ember.Object.extend({
24953
25813
  return this.router.send.apply(this.router, arguments);
24954
25814
  },
24955
25815
 
24956
- /**
24957
- @private
24958
-
24959
- Internal counter for tracking whether a route handler has
24960
- called transitionTo or replaceWith inside its redirect hook.
24961
-
24962
- */
24963
- _redirectDepth: 0,
24964
-
24965
25816
  /**
24966
25817
  @private
24967
25818
 
@@ -24970,58 +25821,6 @@ Ember.Route = Ember.Object.extend({
24970
25821
  @method setup
24971
25822
  */
24972
25823
  setup: function(context) {
24973
- // Determine if this is the top-most transition.
24974
- // If so, we'll set up a data structure to track
24975
- // whether `transitionTo` or replaceWith gets called
24976
- // inside our `redirect` hook.
24977
- //
24978
- // This is necessary because we set a flag on the route
24979
- // inside transitionTo/replaceWith to determine afterwards
24980
- // if they were called, but `setup` can be called
24981
- // recursively and we need to disambiguate where in the
24982
- // call stack the redirect happened.
24983
-
24984
- // Are we the first call to setup? If so, set up the
24985
- // redirect tracking data structure, and remember that
24986
- // we're the top-most so we can clean it up later.
24987
- var isTop;
24988
- if (!this._redirected) {
24989
- isTop = true;
24990
- this._redirected = [];
24991
- }
24992
-
24993
- // Set a flag on this route saying that we are interested in
24994
- // tracking redirects, and increment the depth count.
24995
- this._checkingRedirect = true;
24996
- var depth = ++this._redirectDepth;
24997
-
24998
- // Check to see if context is set. This check preserves
24999
- // the correct arguments.length inside the `redirect` hook.
25000
- if (context === undefined) {
25001
- this.redirect();
25002
- } else {
25003
- this.redirect(context);
25004
- }
25005
-
25006
- // After the call to `redirect` returns, decrement the depth count.
25007
- this._redirectDepth--;
25008
- this._checkingRedirect = false;
25009
-
25010
- // Save off the data structure so we can reset it on the route but
25011
- // still reference it later in this method.
25012
- var redirected = this._redirected;
25013
-
25014
- // If this is the top `setup` call in the call stack, clear the
25015
- // redirect tracking data structure.
25016
- if (isTop) { this._redirected = null; }
25017
-
25018
- // If we were redirected, there is nothing left for us to do.
25019
- // Returning false tells router.js not to continue calling setup
25020
- // on any children route handlers.
25021
- if (redirected[depth]) {
25022
- return false;
25023
- }
25024
-
25025
25824
  var controller = this.controllerFor(this.routeName, context);
25026
25825
 
25027
25826
  // Assign the route's controller so that it can more easily be
@@ -25044,29 +25843,131 @@ Ember.Route = Ember.Object.extend({
25044
25843
  },
25045
25844
 
25046
25845
  /**
25846
+ @deprecated
25847
+
25047
25848
  A hook you can implement to optionally redirect to another route.
25048
25849
 
25049
25850
  If you call `this.transitionTo` from inside of this hook, this route
25050
25851
  will not be entered in favor of the other hook.
25051
25852
 
25853
+ This hook is deprecated in favor of using the `afterModel` hook
25854
+ for performing redirects after the model has resolved.
25855
+
25052
25856
  @method redirect
25053
25857
  @param {Object} model the model for this route
25054
25858
  */
25055
25859
  redirect: Ember.K,
25056
25860
 
25057
25861
  /**
25058
- @private
25862
+ This hook is the first of the route entry validation hooks
25863
+ called when an attempt is made to transition into a route
25864
+ or one of its children. It is called before `model` and
25865
+ `afterModel`, and is appropriate for cases when:
25866
+
25867
+ 1) A decision can be made to redirect elsewhere without
25868
+ needing to resolve the model first.
25869
+ 2) Any async operations need to occur first before the
25870
+ model is attempted to be resolved.
25871
+
25872
+ This hook is provided the current `transition` attempt
25873
+ as a parameter, which can be used to `.abort()` the transition,
25874
+ save it for a later `.retry()`, or retrieve values set
25875
+ on it from a previous hook. You can also just call
25876
+ `this.transitionTo` to another route to implicitly
25877
+ abort the `transition`.
25878
+
25879
+ You can return a promise from this hook to pause the
25880
+ transition until the promise resolves (or rejects). This could
25881
+ be useful, for instance, for retrieving async code from
25882
+ the server that is required to enter a route.
25883
+
25884
+ ```js
25885
+ App.PostRoute = Ember.Route.extend({
25886
+ beforeModel: function(transition) {
25887
+ if (!App.Post) {
25888
+ return Ember.$.getScript('/models/post.js');
25889
+ }
25890
+ }
25891
+ });
25892
+ ```
25893
+
25894
+ If `App.Post` doesn't exist in the above example,
25895
+ `beforeModel` will use jQuery's `getScript`, which
25896
+ returns a promise that resolves after the server has
25897
+ successfully retrieved and executed the code from the
25898
+ server. Note that if an error were to occur, it would
25899
+ be passed to the `error` hook on `Ember.Route`, but
25900
+ it's also possible to handle errors specific to
25901
+ `beforeModel` right from within the hook (to distinguish
25902
+ from the shared error handling behavior of the `error`
25903
+ hook):
25059
25904
 
25060
- The hook called by `router.js` to convert parameters into the context
25061
- for this handler. The public Ember hook is `model`.
25905
+ ```js
25906
+ App.PostRoute = Ember.Route.extend({
25907
+ beforeModel: function(transition) {
25908
+ if (!App.Post) {
25909
+ var self = this;
25910
+ return Ember.$.getScript('post.js').then(null, function(e) {
25911
+ self.transitionTo('help');
25912
+
25913
+ // Note that the above transitionTo will implicitly
25914
+ // halt the transition. If you were to return
25915
+ // nothing from this promise reject handler,
25916
+ // according to promise semantics, that would
25917
+ // convert the reject into a resolve and the
25918
+ // transition would continue. To propagate the
25919
+ // error so that it'd be handled by the `error`
25920
+ // hook, you would have to either
25921
+ return Ember.RSVP.reject(e);
25922
+ // or
25923
+ throw e;
25924
+ });
25925
+ }
25926
+ }
25927
+ });
25928
+ ```
25062
25929
 
25063
- @method deserialize
25930
+ @param {Transition} transition
25931
+ @return {Promise} if the value returned from this hook is
25932
+ a promise, the transition will pause until the transition
25933
+ resolves. Otherwise, non-promise return values are not
25934
+ utilized in any way.
25064
25935
  */
25065
- deserialize: function(params) {
25066
- var model = this.model(params);
25067
- return this.currentModel = model;
25936
+ beforeModel: Ember.K,
25937
+
25938
+ /**
25939
+ This hook is called after this route's model has resolved.
25940
+ It follows identical async/promise semantics to `beforeModel`
25941
+ but is provided the route's resolved model in addition to
25942
+ the `transition`, and is therefore suited to performing
25943
+ logic that can only take place after the model has already
25944
+ resolved.
25945
+
25946
+ ```js
25947
+ App.PostRoute = Ember.Route.extend({
25948
+ afterModel: function(posts, transition) {
25949
+ if (posts.length === 1) {
25950
+ this.transitionTo('post.show', posts[0]);
25951
+ }
25952
+ }
25953
+ });
25954
+ ```
25955
+
25956
+ Refer to documentation for `beforeModel` for a description
25957
+ of transition-pausing semantics when a promise is returned
25958
+ from this hook.
25959
+
25960
+ @param {Transition} transition
25961
+ @return {Promise} if the value returned from this hook is
25962
+ a promise, the transition will pause until the transition
25963
+ resolves. Otherwise, non-promise return values are not
25964
+ utilized in any way.
25965
+ */
25966
+ afterModel: function(resolvedModel, transition) {
25967
+ this.redirect(resolvedModel, transition);
25068
25968
  },
25069
25969
 
25970
+
25070
25971
  /**
25071
25972
  @private
25072
25973
 
@@ -25104,10 +26005,15 @@ Ember.Route = Ember.Object.extend({
25104
26005
  is not called. Routes without dynamic segments will always
25105
26006
  execute the model hook.
25106
26007
 
26008
+ This hook follows the asynchronous/promise semantics
26009
+ described in the documentation for `beforeModel`. In particular,
26010
+ if a promise returned from `model` fails, the error will be
26011
+ handled by the `error` hook on `Ember.Route`.
26012
+
25107
26013
  @method model
25108
26014
  @param {Object} params the parameters extracted from the URL
25109
26015
  */
25110
- model: function(params) {
26016
+ model: function(params, resolvedParentModels) {
25111
26017
  var match, name, sawParams, value;
25112
26018
 
25113
26019
  for (var prop in params) {
@@ -25265,7 +26171,19 @@ Ember.Route = Ember.Object.extend({
25265
26171
  @return {Object} the model object
25266
26172
  */
25267
26173
  modelFor: function(name) {
25268
- var route = this.container.lookup('route:' + name);
26174
+
26175
+ var route = this.container.lookup('route:' + name),
26176
+ transition = this.router.router.activeTransition;
26177
+
26178
+ // If we are mid-transition, we want to try and look up
26179
+ // resolved parent contexts on the current transitionEvent.
26180
+ if (transition) {
26181
+ var modelLookupName = (route && route.routeName) || name;
26182
+ if (transition.resolvedModels.hasOwnProperty(modelLookupName)) {
26183
+ return transition.resolvedModels[modelLookupName];
26184
+ }
26185
+ }
26186
+
25269
26187
  return route && route.currentModel;
25270
26188
  },
25271
26189
 
@@ -25368,7 +26286,22 @@ Ember.Route = Ember.Object.extend({
25368
26286
  },
25369
26287
 
25370
26288
  willDestroy: function() {
25371
- teardownView(this);
26289
+ this.teardownViews();
26290
+ },
26291
+
26292
+ teardownViews: function() {
26293
+ // Tear down the top level view
26294
+ if (this.teardownTopLevelView) { this.teardownTopLevelView(); }
26295
+
26296
+ // Tear down any outlets rendered with 'into'
26297
+ var teardownOutletViews = this.teardownOutletViews || [];
26298
+ a_forEach(teardownOutletViews, function(teardownOutletView) {
26299
+ teardownOutletView();
26300
+ });
26301
+
26302
+ delete this.teardownTopLevelView;
26303
+ delete this.teardownOutletViews;
26304
+ delete this.lastRenderedTemplate;
25372
26305
  }
25373
26306
  });
25374
26307
 
@@ -25457,94 +26390,30 @@ function setupView(view, container, options) {
25457
26390
  function appendView(route, view, options) {
25458
26391
  if (options.into) {
25459
26392
  var parentView = route.router._lookupActiveView(options.into);
25460
- route.teardownView = teardownOutlet(parentView, options.outlet);
26393
+ var teardownOutletView = generateOutletTeardown(parentView, options.outlet);
26394
+ if (!route.teardownOutletViews) { route.teardownOutletViews = []; }
26395
+ a_replace(route.teardownOutletViews, 0, 0, [teardownOutletView]);
25461
26396
  parentView.connectOutlet(options.outlet, view);
25462
26397
  } else {
25463
26398
  var rootElement = get(route, 'router.namespace.rootElement');
25464
26399
  // tear down view if one is already rendered
25465
- if (route.teardownView) {
25466
- route.teardownView();
26400
+ if (route.teardownTopLevelView) {
26401
+ route.teardownTopLevelView();
25467
26402
  }
25468
26403
  route.router._connectActiveView(options.name, view);
25469
- route.teardownView = teardownTopLevel(view);
26404
+ route.teardownTopLevelView = generateTopLevelTeardown(view);
25470
26405
  view.appendTo(rootElement);
25471
26406
  }
25472
26407
  }
25473
26408
 
25474
- function teardownTopLevel(view) {
26409
+ function generateTopLevelTeardown(view) {
25475
26410
  return function() { view.destroy(); };
25476
26411
  }
25477
26412
 
25478
- function teardownOutlet(parentView, outlet) {
26413
+ function generateOutletTeardown(parentView, outlet) {
25479
26414
  return function() { parentView.disconnectOutlet(outlet); };
25480
26415
  }
25481
26416
 
25482
- function teardownView(route) {
25483
- if (route.teardownView) { route.teardownView(); }
25484
-
25485
- delete route.teardownView;
25486
- delete route.lastRenderedTemplate;
25487
- }
25488
-
25489
- })();
25490
-
25491
-
25492
-
25493
- (function() {
25494
- /**
25495
- @module ember
25496
- @submodule ember-routing
25497
- */
25498
-
25499
-
25500
- /*
25501
- A TransitionEvent is passed as the argument for `transitionTo`
25502
- events and contains information about an attempted transition
25503
- that can be modified or decorated by leafier `transitionTo` event
25504
- handlers before the actual transition is committed by ApplicationRoute.
25505
-
25506
- @class TransitionEvent
25507
- @namespace Ember
25508
- @extends Ember.Deferred
25509
- */
25510
- Ember.TransitionEvent = Ember.Object.extend({
25511
-
25512
- /*
25513
- The Ember.Route method used to perform the transition. Presently,
25514
- the only valid values are 'transitionTo' and 'replaceWith'.
25515
- */
25516
- transitionMethod: 'transitionTo',
25517
- destinationRouteName: null,
25518
- sourceRoute: null,
25519
- contexts: null,
25520
-
25521
- init: function() {
25522
- this._super();
25523
- this.contexts = this.contexts || [];
25524
- },
25525
-
25526
- /*
25527
- Convenience method that returns an array that can be used for
25528
- legacy `transitionTo` and `replaceWith`.
25529
- */
25530
- transitionToArgs: function() {
25531
- return [this.destinationRouteName].concat(this.contexts);
25532
- }
25533
- });
25534
-
25535
-
25536
- Ember.TransitionEvent.reopenClass({
25537
- /*
25538
- This is the default transition event handler that will be injected
25539
- into ApplicationRoute. The context, like all route event handlers in
25540
- the events hash, will be an `Ember.Route`.
25541
- */
25542
- defaultHandler: function(transitionEvent) {
25543
- var router = this.router;
25544
- router[transitionEvent.transitionMethod].apply(router, transitionEvent.transitionToArgs());
25545
- }
25546
- });
25547
-
25548
26417
  })();
25549
26418
 
25550
26419
 
@@ -25622,34 +26491,133 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
25622
26491
  }
25623
26492
 
25624
26493
  /**
26494
+ `Ember.LinkView` renders an element whose `click` event triggers a
26495
+ transition of the application's instance of `Ember.Router` to
26496
+ a supplied route by name.
26497
+
26498
+ Instances of `LinkView` will most likely be created through
26499
+ the `linkTo` Handlebars helper, but properties of this class
26500
+ can be overridden to customize application-wide behavior.
26501
+
25625
26502
  @class LinkView
25626
26503
  @namespace Ember
25627
26504
  @extends Ember.View
26505
+ @see {Handlebars.helpers.linkTo}
25628
26506
  **/
25629
26507
  var LinkView = Ember.LinkView = Ember.View.extend({
25630
26508
  tagName: 'a',
25631
26509
  namedRoute: null,
25632
26510
  currentWhen: null,
26511
+
26512
+ /**
26513
+ Sets the `title` attribute of the `LinkView`'s HTML element.
26514
+
26515
+ @property title
26516
+ @default null
26517
+ **/
25633
26518
  title: null,
26519
+
26520
+ /**
26521
+ The CSS class to apply to `LinkView`'s element when its `active`
26522
+ property is `true`.
26523
+
26524
+ @property activeClass
26525
+ @type String
26526
+ @default active
26527
+ **/
25634
26528
  activeClass: 'active',
26529
+
26530
+ /**
26531
+ The CSS class to apply to a `LinkView`'s element when its `disabled`
26532
+ property is `true`.
26533
+
26534
+ @property disabledClass
26535
+ @type String
26536
+ @default disabled
26537
+ **/
25635
26538
  disabledClass: 'disabled',
25636
26539
  _isDisabled: false,
26540
+
26541
+ /**
26542
+ Determines whether the `LinkView` will trigger routing via
26543
+ the `replaceWith` routing strategy.
26544
+
26545
+ @type Boolean
26546
+ @default false
26547
+ **/
25637
26548
  replace: false,
25638
26549
  attributeBindings: ['href', 'title'],
25639
26550
  classNameBindings: ['active', 'disabled'],
25640
26551
 
25641
- // Even though this isn't a virtual view, we want to treat it as if it is
25642
- // so that you can access the parent with {{view.prop}}
26552
+ /**
26553
+ By default the `{{linkTo}}` helper responds to the `click` event. You
26554
+ can override this globally by setting this property to your custom
26555
+ event name.
26556
+
26557
+ This is particularly useful on mobile when one wants to avoid the 300ms
26558
+ click delay using some sort of custom `tap` event.
26559
+
26560
+ @property eventName
26561
+ @type String
26562
+ @default click
26563
+ */
26564
+ eventName: 'click',
26565
+
26566
+ // this is doc'ed here so it shows up in the events
26567
+ // section of the API documentation, which is where
26568
+ // people will likely go looking for it.
26569
+ /**
26570
+ Triggers the `LinkView`'s routing behavior. If
26571
+ `eventName` is changed to a value other than `click`
26572
+ the routing behavior will trigger on that custom event
26573
+ instead.
26574
+
26575
+ @event click
26576
+ **/
26577
+
26578
+ init: function() {
26579
+ this._super();
26580
+ // Map desired event name to invoke function
26581
+ var eventName = get(this, 'eventName');
26582
+ this.on(eventName, this, this._invoke);
26583
+ },
26584
+
26585
+ /**
26586
+ @private
26587
+
26588
+ Even though this isn't a virtual view, we want to treat it as if it is
26589
+ so that you can access the parent with {{view.prop}}
26590
+
26591
+ @method concreteView
26592
+ **/
25643
26593
  concreteView: Ember.computed(function() {
25644
26594
  return get(this, 'parentView');
25645
26595
  }).property('parentView'),
25646
26596
 
26597
+ /**
26598
+
26599
+ Accessed as a classname binding to apply the `LinkView`'s `disabledClass`
26600
+ CSS `class` to the element when the link is disabled.
26601
+
26602
+ When `true` interactions with the element will not trigger route changes.
26603
+ @property disabled
26604
+ */
25647
26605
  disabled: Ember.computed(function(key, value) {
25648
26606
  if (value !== undefined) { this.set('_isDisabled', value); }
25649
26607
 
25650
26608
  return value ? this.get('disabledClass') : false;
25651
26609
  }),
25652
26610
 
26611
+ /**
26612
+ Accessed as a classname binding to apply the `LinkView`'s `activeClass`
26613
+ CSS `class` to the element when the link is active.
26614
+
26615
+ A `LinkView` is considered active when its `currentWhen` property is `true`
26616
+ or the application's current route is the route the `LinkView` would trigger
26617
+ transitions into.
26618
+
26619
+ @property active
26620
+ **/
25653
26621
  active: Ember.computed(function() {
25654
26622
  var router = this.get('router'),
25655
26623
  params = resolvedPaths(this.parameters),
@@ -25664,7 +26632,15 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
25664
26632
  return this.get('controller').container.lookup('router:main');
25665
26633
  }),
25666
26634
 
25667
- click: function(event) {
26635
+ /**
26636
+ @private
26637
+
26638
+ Event handler that invokes the link, activating the associated route.
26639
+
26640
+ @method _invoke
26641
+ @param {Event} event
26642
+ */
26643
+ _invoke: function(event) {
25668
26644
  if (!isSimpleClick(event)) { return true; }
25669
26645
 
25670
26646
  event.preventDefault();
@@ -25672,26 +26648,25 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
25672
26648
 
25673
26649
  if (get(this, '_isDisabled')) { return false; }
25674
26650
 
25675
- var router = this.get('router');
25676
-
25677
- if (Ember.ENV.ENABLE_ROUTE_TO) {
25678
-
25679
- var routeArgs = args(this, router);
26651
+ var router = this.get('router'),
26652
+ routeArgs = args(this, router);
25680
26653
 
25681
- router.routeTo(Ember.TransitionEvent.create({
25682
- transitionMethod: this.get('replace') ? 'replaceWith' : 'transitionTo',
25683
- destinationRouteName: routeArgs[0],
25684
- contexts: routeArgs.slice(1)
25685
- }));
26654
+ if (this.get('replace')) {
26655
+ router.replaceWith.apply(router, routeArgs);
25686
26656
  } else {
25687
- if (this.get('replace')) {
25688
- router.replaceWith.apply(router, args(this, router));
25689
- } else {
25690
- router.transitionTo.apply(router, args(this, router));
25691
- }
26657
+ router.transitionTo.apply(router, routeArgs);
25692
26658
  }
25693
26659
  },
25694
26660
 
26661
+ /**
26662
+ Sets the element's `href` attribute to the url for
26663
+ the `LinkView`'s targeted route.
26664
+
26665
+ If the `LinkView`'s `tagName` is changed to a value other
26666
+ than `a`, this property will be ignored.
26667
+
26668
+ @property href
26669
+ **/
25695
26670
  href: Ember.computed(function() {
25696
26671
  if (this.get('tagName') !== 'a') { return false; }
25697
26672
 
@@ -25852,6 +26827,15 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
25852
26827
  })
25853
26828
  ```
25854
26829
 
26830
+ It is also possible to override the default event in
26831
+ this manner:
26832
+
26833
+ ``` javascript
26834
+ Ember.LinkView.reopen({
26835
+ eventName: 'customEventName'
26836
+ });
26837
+ ```
26838
+
25855
26839
  @method linkTo
25856
26840
  @for Ember.Handlebars.helpers
25857
26841
  @param {String} routeName
@@ -26043,8 +27027,12 @@ Ember.onLoad('Ember.Handlebars', function(Handlebars) {
26043
27027
 
26044
27028
  view = container.lookup('view:' + name) || container.lookup('view:default');
26045
27029
 
26046
- if (controller = options.hash.controller) {
26047
- controller = container.lookup('controller:' + controller, lookupOptions);
27030
+ var controllerName = options.hash.controller;
27031
+
27032
+ // Look up the controller by name, if provided.
27033
+ if (controllerName) {
27034
+ controller = container.lookup('controller:' + controllerName, lookupOptions);
27035
+ Ember.assert("The controller name you supplied '" + controllerName + "' did not resolve to a controller.", !!controller);
26048
27036
  } else {
26049
27037
  controller = Ember.controllerFor(container, name, context, lookupOptions);
26050
27038
  }
@@ -26465,10 +27453,12 @@ if (Ember.ENV.EXPERIMENTAL_CONTROL_HELPER) {
26465
27453
  childView.rerender();
26466
27454
  }
26467
27455
 
26468
- Ember.addObserver(this, modelPath, observer);
26469
- childView.one('willDestroyElement', this, function() {
26470
- Ember.removeObserver(this, modelPath, observer);
26471
- });
27456
+ if (modelPath) {
27457
+ Ember.addObserver(this, modelPath, observer);
27458
+ childView.one('willDestroyElement', this, function() {
27459
+ Ember.removeObserver(this, modelPath, observer);
27460
+ });
27461
+ }
26472
27462
 
26473
27463
  Ember.Handlebars.helpers.view.call(this, childView, options);
26474
27464
  });
@@ -26593,7 +27583,7 @@ Ember.View.reopen({
26593
27583
  _hasEquivalentView: function(outletName, view) {
26594
27584
  var existingView = get(this, '_outlets.'+outletName);
26595
27585
  return existingView &&
26596
- existingView.prototype === view.prototype &&
27586
+ existingView.constructor === view.constructor &&
26597
27587
  existingView.get('template') === view.get('template') &&
26598
27588
  existingView.get('context') === view.get('context');
26599
27589
  },
@@ -26622,6 +27612,19 @@ Ember.View.reopen({
26622
27612
 
26623
27613
 
26624
27614
  (function() {
27615
+ /**
27616
+ @module ember
27617
+ @submodule ember-views
27618
+ */
27619
+
27620
+ // Add a new named queue after the 'actions' queue (where RSVP promises
27621
+ // resolve), which is used in router transitions to prevent unnecessary
27622
+ // loading state entry if all context promises resolve on the
27623
+ // 'actions' queue first.
27624
+
27625
+ var queues = Ember.run.queues,
27626
+ indexOf = Ember.ArrayPolyfills.indexOf;
27627
+ queues.splice(indexOf.call(queues, 'actions') + 1, 0, 'routerTransitions');
26625
27628
 
26626
27629
  })();
26627
27630
 
@@ -27505,11 +28508,14 @@ DeprecatedContainer.prototype = {
27505
28508
 
27506
28509
  In addition to creating your application's router, `Ember.Application` is
27507
28510
  also responsible for telling the router when to start routing. Transitions
27508
- between routes can be logged with the LOG_TRANSITIONS flag:
28511
+ between routes can be logged with the LOG_TRANSITIONS flag, and more
28512
+ detailed intra-transition logging can be logged with
28513
+ the LOG_TRANSITIONS_INTERNAL flag:
27509
28514
 
27510
28515
  ```javascript
27511
28516
  window.App = Ember.Application.create({
27512
- LOG_TRANSITIONS: true
28517
+ LOG_TRANSITIONS: true, // basic logging of successful transitions
28518
+ LOG_TRANSITIONS_INTERNAL: true // detailed logging of all routing steps
27513
28519
  });
27514
28520
  ```
27515
28521
 
@@ -28216,6 +29222,8 @@ Ember.ControllerMixin.reopen({
28216
29222
  this.get('controllers.post'); // instance of App.PostController
28217
29223
  ```
28218
29224
 
29225
+ This is only available for singleton controllers.
29226
+
28219
29227
  @property {Array} needs
28220
29228
  @default []
28221
29229
  */
@@ -28278,8 +29286,6 @@ var get = Ember.get, set = Ember.set;
28278
29286
  */
28279
29287
  Ember.State = Ember.Object.extend(Ember.Evented,
28280
29288
  /** @scope Ember.State.prototype */{
28281
- isState: true,
28282
-
28283
29289
  /**
28284
29290
  A reference to the parent state.
28285
29291
 
@@ -28389,20 +29395,24 @@ Ember.State = Ember.Object.extend(Ember.Evented,
28389
29395
 
28390
29396
  setupChild: function(states, name, value) {
28391
29397
  if (!value) { return false; }
29398
+ var instance;
28392
29399
 
28393
- if (value.isState) {
29400
+ if (value instanceof Ember.State) {
28394
29401
  set(value, 'name', name);
29402
+ instance = value;
29403
+ instance.container = this.container;
28395
29404
  } else if (Ember.State.detect(value)) {
28396
- value = value.create({
28397
- name: name
29405
+ instance = value.create({
29406
+ name: name,
29407
+ container: this.container
28398
29408
  });
28399
29409
  }
28400
29410
 
28401
- if (value.isState) {
28402
- set(value, 'parentState', this);
28403
- get(this, 'childStates').pushObject(value);
28404
- states[name] = value;
28405
- return value;
29411
+ if (instance instanceof Ember.State) {
29412
+ set(instance, 'parentState', this);
29413
+ get(this, 'childStates').pushObject(instance);
29414
+ states[name] = instance;
29415
+ return instance;
28406
29416
  }
28407
29417
  },
28408
29418
 
@@ -29631,7 +30641,7 @@ Ember.Test = {
29631
30641
  chained: false
29632
30642
  };
29633
30643
  thenable.then = function(onSuccess, onFailure) {
29634
- var self = this, thenPromise, nextPromise;
30644
+ var thenPromise, nextPromise;
29635
30645
  thenable.chained = true;
29636
30646
  thenPromise = promise.then(onSuccess, onFailure);
29637
30647
  // this is to ensure all downstream fulfillment
@@ -29804,33 +30814,32 @@ Test.QUnitAdapter = Test.Adapter.extend({
29804
30814
 
29805
30815
  (function() {
29806
30816
  var get = Ember.get,
29807
- helper = Ember.Test.registerHelper,
29808
- pendingAjaxRequests = 0,
30817
+ Test = Ember.Test,
30818
+ helper = Test.registerHelper,
29809
30819
  countAsync = 0;
29810
30820
 
30821
+ Test.pendingAjaxRequests = 0;
29811
30822
 
29812
- Ember.Test.onInjectHelpers(function() {
30823
+ Test.onInjectHelpers(function() {
29813
30824
  Ember.$(document).ajaxStart(function() {
29814
- pendingAjaxRequests++;
30825
+ Test.pendingAjaxRequests++;
29815
30826
  });
29816
30827
 
29817
30828
  Ember.$(document).ajaxStop(function() {
29818
- pendingAjaxRequests--;
30829
+ Test.pendingAjaxRequests--;
29819
30830
  });
29820
30831
  });
29821
30832
 
29822
30833
 
29823
30834
  function visit(app, url) {
29824
- Ember.run(app, app.handleURL, url);
29825
30835
  app.__container__.lookup('router:main').location.setURL(url);
30836
+ Ember.run(app, app.handleURL, url);
29826
30837
  return wait(app);
29827
30838
  }
29828
30839
 
29829
30840
  function click(app, selector, context) {
29830
- var $el = find(app, selector, context);
29831
- Ember.run(function() {
29832
- $el.click();
29833
- });
30841
+ var $el = findWithAssert(app, selector, context);
30842
+ Ember.run($el, 'click');
29834
30843
  return wait(app);
29835
30844
  }
29836
30845
 
@@ -29840,42 +30849,49 @@ function fillIn(app, selector, context, text) {
29840
30849
  text = context;
29841
30850
  context = null;
29842
30851
  }
29843
- $el = find(app, selector, context);
30852
+ $el = findWithAssert(app, selector, context);
29844
30853
  Ember.run(function() {
29845
30854
  $el.val(text).change();
29846
30855
  });
29847
30856
  return wait(app);
29848
30857
  }
29849
30858
 
30859
+ function findWithAssert(app, selector, context) {
30860
+ var $el = find(app, selector, context);
30861
+ if ($el.length === 0) {
30862
+ throw("Element " + selector + " not found.");
30863
+ }
30864
+ return $el;
30865
+ }
30866
+
29850
30867
  function find(app, selector, context) {
29851
30868
  var $el;
29852
30869
  context = context || get(app, 'rootElement');
29853
30870
  $el = app.$(selector, context);
29854
- if ($el.length === 0) {
29855
- throw("Element " + selector + " not found.");
29856
- }
30871
+
29857
30872
  return $el;
29858
30873
  }
29859
30874
 
29860
30875
  function wait(app, value) {
29861
- var promise, obj = {}, helperName;
30876
+ var promise;
29862
30877
 
29863
- promise = Ember.Test.promise(function(resolve) {
30878
+ promise = Test.promise(function(resolve) {
29864
30879
  if (++countAsync === 1) {
29865
- Ember.Test.adapter.asyncStart();
30880
+ Test.adapter.asyncStart();
29866
30881
  }
29867
30882
  var watcher = setInterval(function() {
29868
30883
  var routerIsLoading = app.__container__.lookup('router:main').router.isLoading;
29869
30884
  if (routerIsLoading) { return; }
29870
- if (pendingAjaxRequests) { return; }
30885
+ if (Test.pendingAjaxRequests) { return; }
29871
30886
  if (Ember.run.hasScheduledTimers() || Ember.run.currentRunLoop) { return; }
30887
+
29872
30888
  clearInterval(watcher);
30889
+
29873
30890
  if (--countAsync === 0) {
29874
- Ember.Test.adapter.asyncEnd();
30891
+ Test.adapter.asyncEnd();
29875
30892
  }
29876
- Ember.run(function() {
29877
- resolve(value);
29878
- });
30893
+
30894
+ Ember.run(null, resolve, value);
29879
30895
  }, 10);
29880
30896
  });
29881
30897
 
@@ -29930,6 +30946,7 @@ helper('visit', visit);
29930
30946
  helper('click', click);
29931
30947
  helper('fillIn', fillIn);
29932
30948
  helper('find', find);
30949
+ helper('findWithAssert', findWithAssert);
29933
30950
  helper('wait', wait);
29934
30951
 
29935
30952
  })();