cadenero 0.0.2.a3 → 0.0.2.b1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
  })();