ember-rails 0.2.3 → 0.2.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -1608,7 +1608,7 @@ if ('undefined' === typeof Ember) {
1608
1608
  /**
1609
1609
  @namespace
1610
1610
  @name Ember
1611
- @version 0.9.3
1611
+ @version 0.9.4
1612
1612
 
1613
1613
  All Ember methods and functions are defined inside of this namespace.
1614
1614
  You generally should not add new properties to this namespace as it may be
@@ -1640,10 +1640,10 @@ if ('undefined' !== typeof window) {
1640
1640
  /**
1641
1641
  @static
1642
1642
  @type String
1643
- @default '0.9.3'
1643
+ @default '0.9.4'
1644
1644
  @constant
1645
1645
  */
1646
- Ember.VERSION = '0.9.3';
1646
+ Ember.VERSION = '0.9.4';
1647
1647
 
1648
1648
  /**
1649
1649
  @static
@@ -2387,8 +2387,8 @@ function getPath(target, path) {
2387
2387
  }
2388
2388
 
2389
2389
  var TUPLE_RET = [];
2390
- var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$])).*[\.\*]/;
2391
- var IS_GLOBAL_SET = /^([A-Z$]|([0-9][A-Z$])).*[\.\*]?/;
2390
+ var IS_GLOBAL = /^([A-Z$]|([0-9][A-Z$]))/;
2391
+ var IS_GLOBAL_PATH = /^([A-Z$]|([0-9][A-Z$])).*[\.\*]/;
2392
2392
  var HAS_THIS = /^this[\.\*]/;
2393
2393
  var FIRST_KEY = /^([^\.\*]+)/;
2394
2394
 
@@ -2399,7 +2399,7 @@ function firstKey(path) {
2399
2399
  // assumes path is already normalized
2400
2400
  function normalizeTuple(target, path) {
2401
2401
  var hasThis = HAS_THIS.test(path),
2402
- isGlobal = !hasThis && IS_GLOBAL.test(path),
2402
+ isGlobal = !hasThis && IS_GLOBAL_PATH.test(path),
2403
2403
  key;
2404
2404
 
2405
2405
  if (!target || isGlobal) target = window;
@@ -2464,9 +2464,14 @@ Ember.normalizeTuple = function(target, path) {
2464
2464
 
2465
2465
  Ember.normalizeTuple.primitive = normalizeTuple;
2466
2466
 
2467
- Ember.getPath = function(root, path) {
2468
- var hasThis, hasStar, isGlobal;
2467
+ Ember.getPath = function(root, path, _checkGlobal) {
2468
+ var hasThis, hasStar, isGlobal, ret;
2469
2469
 
2470
+ // Helpers that operate with 'this' within an #each
2471
+ if (path === '') {
2472
+ return root;
2473
+ }
2474
+
2470
2475
  if (!path && 'string'===typeof root) {
2471
2476
  path = root;
2472
2477
  root = null;
@@ -2482,15 +2487,26 @@ Ember.getPath = function(root, path) {
2482
2487
  // detect complicated paths and normalize them
2483
2488
  path = normalizePath(path);
2484
2489
  hasThis = HAS_THIS.test(path);
2485
- isGlobal = !hasThis && IS_GLOBAL.test(path);
2486
- if (!root || hasThis || isGlobal || hasStar) {
2490
+
2491
+ if (!root || hasThis || hasStar) {
2492
+ if (root && root !== window && IS_GLOBAL.test(path)) {
2493
+ console.warn("Fetching globals with Ember.getPath is deprecated", root, path);
2494
+ }
2495
+
2487
2496
  var tuple = normalizeTuple(root, path);
2488
2497
  root = tuple[0];
2489
2498
  path = tuple[1];
2490
2499
  tuple.length = 0;
2491
2500
  }
2492
-
2493
- return getPath(root, path);
2501
+
2502
+ ret = getPath(root, path);
2503
+
2504
+ if (ret === undefined && root !== window && !hasThis && IS_GLOBAL.test(path) && _checkGlobal !== false) {
2505
+ console.warn("Fetching globals with Ember.getPath is deprecated", root, path);
2506
+ return Ember.getPath(window, path);
2507
+ } else {
2508
+ return ret;
2509
+ }
2494
2510
  };
2495
2511
 
2496
2512
  Ember.setPath = function(root, path, value, tolerant) {
@@ -2504,6 +2520,10 @@ Ember.setPath = function(root, path, value, tolerant) {
2504
2520
 
2505
2521
  path = normalizePath(path);
2506
2522
  if (path.indexOf('*')>0) {
2523
+ if (root && root !== window && IS_GLOBAL.test(path)) {
2524
+ console.warn("Setting globals with Ember.setPath is deprecated", path);
2525
+ };
2526
+
2507
2527
  var tuple = normalizeTuple(root, path);
2508
2528
  root = tuple[0];
2509
2529
  path = tuple[1];
@@ -2513,14 +2533,17 @@ Ember.setPath = function(root, path, value, tolerant) {
2513
2533
  if (path.indexOf('.') > 0) {
2514
2534
  keyName = path.slice(path.lastIndexOf('.')+1);
2515
2535
  path = path.slice(0, path.length-(keyName.length+1));
2516
- if (!HAS_THIS.test(path) && IS_GLOBAL_SET.test(path) && path.indexOf('.')<0) {
2517
- root = window[path]; // special case only works during set...
2518
- } else if (path !== 'this') {
2519
- root = Ember.getPath(root, path);
2536
+ if (path !== 'this') {
2537
+ // Remove the `false` when we're done with this deprecation
2538
+ root = Ember.getPath(root, path, false);
2539
+ if (!root && IS_GLOBAL.test(path)) {
2540
+ console.warn("Setting globals with Ember.setPath is deprecated", path);
2541
+ root = Ember.getPath(window, path);
2542
+ }
2520
2543
  }
2521
2544
 
2522
2545
  } else {
2523
- if (IS_GLOBAL_SET.test(path)) throw new Error('Invalid Path');
2546
+ if (IS_GLOBAL.test(path)) throw new Error('Invalid Path');
2524
2547
  keyName = path;
2525
2548
  }
2526
2549
 
@@ -2752,7 +2775,9 @@ function xformForArgs(args) {
2752
2775
  return function (target, method, params) {
2753
2776
  var obj = params[0], keyName = changeKey(params[1]), val;
2754
2777
  var copy_args = args.slice();
2755
- if (method.length>2) val = Ember.getPath(obj, keyName);
2778
+ if (method.length>2) {
2779
+ val = Ember.getPath(Ember.isGlobalPath(keyName) ? window : obj, keyName);
2780
+ }
2756
2781
  copy_args.unshift(obj, keyName, val);
2757
2782
  method.apply(target, copy_args);
2758
2783
  };
@@ -3155,9 +3180,10 @@ function hasDesc(descs, keyName) {
3155
3180
  }).property('firstName', 'lastName').cacheable());
3156
3181
  */
3157
3182
  Ember.defineProperty = function(obj, keyName, desc, val) {
3158
- var m = meta(obj, false), descs = m.descs, watching = m.watching[keyName]>0;
3183
+ var m = meta(obj, false), descs = m.descs, watching = m.watching[keyName]>0, override = true;
3159
3184
 
3160
3185
  if (val === undefined) {
3186
+ override = false;
3161
3187
  val = hasDesc(descs, keyName) ? descs[keyName].teardown(obj, keyName) : obj[keyName];
3162
3188
  } else if (hasDesc(descs, keyName)) {
3163
3189
  descs[keyName].teardown(obj, keyName);
@@ -3178,7 +3204,11 @@ Ember.defineProperty = function(obj, keyName, desc, val) {
3178
3204
  if (descs[keyName]) meta(obj).descs[keyName] = null;
3179
3205
  o_defineProperty(obj, keyName, desc);
3180
3206
  }
3181
-
3207
+
3208
+ // if key is being watched, override chains that
3209
+ // were initialized with the prototype
3210
+ if (override && watching) Ember.overrideChains(obj, keyName, m);
3211
+
3182
3212
  return this;
3183
3213
  };
3184
3214
 
@@ -3235,19 +3265,6 @@ Ember.createPrototype = function(obj, props) {
3235
3265
  if (META_KEY in ret) Ember.rewatch(ret); // setup watch chains if needed.
3236
3266
  return ret;
3237
3267
  };
3238
-
3239
-
3240
- /**
3241
- Tears down the meta on an object so that it can be garbage collected.
3242
- Multiple calls will have no effect.
3243
-
3244
- @param {Object} obj the object to destroy
3245
- @returns {void}
3246
- */
3247
- Ember.destroy = function(obj) {
3248
- if (obj[META_KEY]) obj[META_KEY] = null;
3249
- };
3250
-
3251
3268
 
3252
3269
  })({});
3253
3270
 
@@ -3266,6 +3283,7 @@ var normalizeTuple = Ember.normalizeTuple.primitive;
3266
3283
  var normalizePath = Ember.normalizePath;
3267
3284
  var SIMPLE_PROPERTY = Ember.SIMPLE_PROPERTY;
3268
3285
  var GUID_KEY = Ember.GUID_KEY;
3286
+ var META_KEY = Ember.META_KEY;
3269
3287
  var notifyObservers = Ember.notifyObservers;
3270
3288
 
3271
3289
  var FIRST_KEY = /^([^\.\*]+)/;
@@ -3565,7 +3583,7 @@ Wp.chainDidChange = function(chain, path, depth) {
3565
3583
  }
3566
3584
  };
3567
3585
 
3568
- Wp.didChange = function() {
3586
+ Wp.didChange = function(suppressEvent) {
3569
3587
  // invalidate my own value first.
3570
3588
  if (this._watching) {
3571
3589
  var obj = this._parent.value();
@@ -3587,10 +3605,12 @@ Wp.didChange = function() {
3587
3605
  if (chains) {
3588
3606
  for(var key in chains) {
3589
3607
  if (!chains.hasOwnProperty(key)) continue;
3590
- chains[key].didChange();
3608
+ chains[key].didChange(suppressEvent);
3591
3609
  }
3592
3610
  }
3593
3611
 
3612
+ if (suppressEvent) return;
3613
+
3594
3614
  // and finally tell parent about my path changing...
3595
3615
  if (this._parent) this._parent.chainDidChange(this, this._key, 1);
3596
3616
  };
@@ -3610,26 +3630,30 @@ function chainsFor(obj) {
3610
3630
 
3611
3631
 
3612
3632
 
3613
- function notifyChains(obj, keyName, methodName) {
3614
- var m = meta(obj, false);
3633
+ function notifyChains(obj, m, keyName, methodName, arg) {
3615
3634
  var nodes = m.chainWatchers;
3635
+
3616
3636
  if (!nodes || nodes.__emberproto__ !== obj) return; // nothing to do
3617
3637
 
3618
3638
  nodes = nodes[keyName];
3619
3639
  if (!nodes) return;
3620
-
3640
+
3621
3641
  for(var key in nodes) {
3622
3642
  if (!nodes.hasOwnProperty(key)) continue;
3623
- nodes[key][methodName](obj, keyName);
3643
+ nodes[key][methodName](arg);
3624
3644
  }
3625
3645
  }
3626
3646
 
3627
- function chainsWillChange(obj, keyName) {
3628
- notifyChains(obj, keyName, 'willChange');
3647
+ Ember.overrideChains = function(obj, keyName, m) {
3648
+ notifyChains(obj, m, keyName, 'didChange', true);
3649
+ }
3650
+
3651
+ function chainsWillChange(obj, keyName, m) {
3652
+ notifyChains(obj, m, keyName, 'willChange');
3629
3653
  }
3630
3654
 
3631
- function chainsDidChange(obj, keyName) {
3632
- notifyChains(obj, keyName, 'didChange');
3655
+ function chainsDidChange(obj, keyName, m) {
3656
+ notifyChains(obj, m, keyName, 'didChange');
3633
3657
  }
3634
3658
 
3635
3659
  // ..........................................................
@@ -3757,7 +3781,7 @@ var propertyWillChange = Ember.propertyWillChange = function(obj, keyName) {
3757
3781
  if (proto === obj) return ;
3758
3782
  if (desc && desc.willChange) desc.willChange(obj, keyName);
3759
3783
  dependentKeysWillChange(obj, keyName, m);
3760
- chainsWillChange(obj, keyName);
3784
+ chainsWillChange(obj, keyName, m);
3761
3785
  Ember.notifyBeforeObservers(obj, keyName);
3762
3786
  };
3763
3787
 
@@ -3783,10 +3807,51 @@ var propertyDidChange = Ember.propertyDidChange = function(obj, keyName) {
3783
3807
  if (proto === obj) return ;
3784
3808
  if (desc && desc.didChange) desc.didChange(obj, keyName);
3785
3809
  dependentKeysDidChange(obj, keyName, m);
3786
- chainsDidChange(obj, keyName);
3810
+ chainsDidChange(obj, keyName, m);
3787
3811
  Ember.notifyObservers(obj, keyName);
3788
3812
  };
3789
3813
 
3814
+ var NODE_STACK = []
3815
+
3816
+ /**
3817
+ Tears down the meta on an object so that it can be garbage collected.
3818
+ Multiple calls will have no effect.
3819
+
3820
+ @param {Object} obj the object to destroy
3821
+ @returns {void}
3822
+ */
3823
+ Ember.destroy = function (obj) {
3824
+ var meta = obj[META_KEY], node, nodes, key, nodeObject;
3825
+ if (meta) {
3826
+ obj[META_KEY] = null;
3827
+ // remove chainWatchers to remove circular references that would prevent GC
3828
+ node = meta.chains;
3829
+ if (node) {
3830
+ NODE_STACK.push(node);
3831
+ // process tree
3832
+ while (NODE_STACK.length > 0) {
3833
+ node = NODE_STACK.pop();
3834
+ // push children
3835
+ nodes = node._chains;
3836
+ if (nodes) {
3837
+ for (key in nodes) {
3838
+ if (nodes.hasOwnProperty(key)) {
3839
+ NODE_STACK.push(nodes[key]);
3840
+ }
3841
+ }
3842
+ }
3843
+ // remove chainWatcher in node object
3844
+ if (node._watching) {
3845
+ nodeObject = node._object;
3846
+ if (nodeObject) {
3847
+ removeChainWatcher(nodeObject, node._key, node);
3848
+ }
3849
+ }
3850
+ }
3851
+ }
3852
+ }
3853
+ };
3854
+
3790
3855
  })({});
3791
3856
 
3792
3857
 
@@ -3822,8 +3887,20 @@ function invoke(target, method, args, ignore) {
3822
3887
  if (args && ignore>0) {
3823
3888
  args = args.length>ignore ? slice.call(args, ignore) : null;
3824
3889
  }
3825
- // IE8's Function.prototype.apply doesn't accept undefined/null arguments.
3826
- return method.apply(target || this, args || []);
3890
+
3891
+ // Unfortunately in some browsers we lose the backtrace if we rethrow the existing error,
3892
+ // so in the event that we don't have an `onerror` handler we don't wrap in a try/catch
3893
+ if ('function' === typeof Ember.onerror) {
3894
+ try {
3895
+ // IE8's Function.prototype.apply doesn't accept undefined/null arguments.
3896
+ return method.apply(target || this, args || []);
3897
+ } catch (error) {
3898
+ Ember.onerror(error);
3899
+ }
3900
+ } else {
3901
+ // IE8's Function.prototype.apply doesn't accept undefined/null arguments.
3902
+ return method.apply(target || this, args || []);
3903
+ }
3827
3904
  }
3828
3905
 
3829
3906
 
@@ -3968,8 +4045,11 @@ Ember.run = run = function(target, method) {
3968
4045
 
3969
4046
  var ret, loop;
3970
4047
  run.begin();
3971
- if (target || method) ret = invoke(target, method, arguments, 2);
3972
- run.end();
4048
+ try {
4049
+ if (target || method) ret = invoke(target, method, arguments, 2);
4050
+ } finally {
4051
+ run.end();
4052
+ }
3973
4053
  return ret;
3974
4054
  };
3975
4055
 
@@ -4420,7 +4500,8 @@ var NOT = {
4420
4500
  var get = Ember.get,
4421
4501
  getPath = Ember.getPath,
4422
4502
  setPath = Ember.setPath,
4423
- guidFor = Ember.guidFor;
4503
+ guidFor = Ember.guidFor,
4504
+ isGlobalPath = Ember.isGlobalPath;
4424
4505
 
4425
4506
  // Applies a binding's transformations against a value.
4426
4507
  function getTransformedValue(binding, val, obj, dir) {
@@ -4448,9 +4529,18 @@ function empty(val) {
4448
4529
  return val===undefined || val===null || val==='' || (Ember.isArray(val) && get(val, 'length')===0) ;
4449
4530
  }
4450
4531
 
4532
+ function getPathWithGlobals(obj, path) {
4533
+ return getPath(isGlobalPath(path) ? window : obj, path);
4534
+ }
4535
+
4451
4536
  function getTransformedFromValue(obj, binding) {
4452
- var operation = binding._operation;
4453
- var fromValue = operation ? operation(obj, binding._from, binding._operand) : getPath(obj, binding._from);
4537
+ var operation = binding._operation,
4538
+ fromValue;
4539
+ if (operation) {
4540
+ fromValue = operation(obj, binding._from, binding._operand);
4541
+ } else {
4542
+ fromValue = getPathWithGlobals(obj, binding._from);
4543
+ }
4454
4544
  return getTransformedValue(binding, fromValue, obj, 'to');
4455
4545
  }
4456
4546
 
@@ -4460,11 +4550,11 @@ function getTransformedToValue(obj, binding) {
4460
4550
  }
4461
4551
 
4462
4552
  var AND_OPERATION = function(obj, left, right) {
4463
- return getPath(obj, left) && getPath(obj, right);
4553
+ return getPathWithGlobals(obj, left) && getPathWithGlobals(obj, right);
4464
4554
  };
4465
4555
 
4466
4556
  var OR_OPERATION = function(obj, left, right) {
4467
- return getPath(obj, left) || getPath(obj, right);
4557
+ return getPathWithGlobals(obj, left) || getPathWithGlobals(obj, right);
4468
4558
  };
4469
4559
 
4470
4560
  // ..........................................................
@@ -4817,7 +4907,7 @@ Binding.prototype = /** @scope Ember.Binding.prototype */ {
4817
4907
 
4818
4908
  // get the direction of the binding for the object we are
4819
4909
  // synchronizing from
4820
- var guid = guidFor(obj), direction = this[guid], val, transformedValue;
4910
+ var guid = guidFor(obj), direction = this[guid];
4821
4911
 
4822
4912
  var fromPath = this._from, toPath = this._to;
4823
4913
 
@@ -4831,13 +4921,13 @@ Binding.prototype = /** @scope Ember.Binding.prototype */ {
4831
4921
 
4832
4922
  // if we're synchronizing from the remote object...
4833
4923
  if (direction === 'fwd') {
4834
- if (log) { Ember.Logger.log(' ', this.toString(), val, '->', fromValue, obj); }
4835
- Ember.trySetPath(obj, toPath, fromValue);
4924
+ if (log) { Ember.Logger.log(' ', this.toString(), toValue, '->', fromValue, obj); }
4925
+ Ember.trySetPath(Ember.isGlobalPath(toPath) ? window : obj, toPath, fromValue);
4836
4926
 
4837
4927
  // if we're synchronizing *to* the remote object
4838
4928
  } else if (direction === 'back') {// && !this._oneWay) {
4839
- if (log) { Ember.Logger.log(' ', this.toString(), val, '<-', fromValue, obj); }
4840
- Ember.trySetPath(obj, fromPath, toValue);
4929
+ if (log) { Ember.Logger.log(' ', this.toString(), toValue, '<-', fromValue, obj); }
4930
+ Ember.trySetPath(Ember.isGlobalPath(fromPath) ? window : obj, fromPath, toValue);
4841
4931
  }
4842
4932
  }
4843
4933
 
@@ -5071,6 +5161,15 @@ mixinProperties(Binding,
5071
5161
 
5072
5162
  valueBinding: Ember.Binding.oneWay("MyApp.someController.value").notEmpty().notLessThan(10)
5073
5163
 
5164
+ Finally, it's also possible to specify bi-directional transforms. To do this,
5165
+ you can pass a hash to `transform` with `to` and `from`. In the following
5166
+ example, we are expecting a lowercase string that we want to transform to
5167
+ uppercase.
5168
+
5169
+ valueBinding: Ember.Binding.transform({
5170
+ to: function(value, binding) { return value.toUpperCase(); },
5171
+ from: function(value, binding) { return value.toLowerCase(); }
5172
+
5074
5173
  ## How to Manually Adding Binding
5075
5174
 
5076
5175
  All of the examples above show you how to configure a custom binding, but
@@ -6068,6 +6167,9 @@ function findNamespaces() {
6068
6167
  if (Namespace.PROCESSED) { return; }
6069
6168
 
6070
6169
  for (var prop in window) {
6170
+ // get(window.globalStorage, 'isNamespace') would try to read the storage for domain isNamespace and cause exception in Firefox.
6171
+ // globalStorage is a storage obsoleted by the WhatWG storage specification. See https://developer.mozilla.org/en/DOM/Storage#globalStorage
6172
+ if (prop === "globalStorage" && window.StorageList && window.globalStorage instanceof window.StorageList) { continue; }
6071
6173
  // Unfortunately, some versions of IE don't support window.hasOwnProperty
6072
6174
  if (window.hasOwnProperty && !window.hasOwnProperty(prop)) { continue; }
6073
6175
 
@@ -6230,9 +6332,11 @@ function pushCtx(ctx) {
6230
6332
  }
6231
6333
 
6232
6334
  function iter(key, value) {
6335
+ var valueProvided = arguments.length === 2;
6336
+
6233
6337
  function i(item) {
6234
6338
  var cur = get(item, key);
6235
- return value===undefined ? !!cur : value===cur;
6339
+ return valueProvided ? value===cur : !!cur;
6236
6340
  }
6237
6341
  return i ;
6238
6342
  }
@@ -6327,6 +6431,13 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6327
6431
  If your enumerable is empty, this method should return undefined.
6328
6432
 
6329
6433
  @returns {Object} the object or undefined
6434
+
6435
+ @example
6436
+ var arr = ["a", "b", "c"];
6437
+ arr.firstObject(); => "a"
6438
+
6439
+ var arr = [];
6440
+ arr.firstObject(); => undefined
6330
6441
  */
6331
6442
  firstObject: Ember.computed(function() {
6332
6443
  if (get(this, 'length')===0) return undefined ;
@@ -6340,9 +6451,18 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6340
6451
  }).property(),
6341
6452
 
6342
6453
  /**
6343
- Helper method returns the last object from a collection.
6454
+ Helper method returns the last object from a collection. If your enumerable
6455
+ contains only one object, this method should always return that object.
6456
+ If your enumerable is empty, this method should return undefined.
6344
6457
 
6345
- @returns {Object} the object or undefined
6458
+ @returns {Object} the last object or undefined
6459
+
6460
+ @example
6461
+ var arr = ["a", "b", "c"];
6462
+ arr.lastObject(); => "c"
6463
+
6464
+ var arr = [];
6465
+ arr.lastObject(); => undefined
6346
6466
  */
6347
6467
  lastObject: Ember.computed(function() {
6348
6468
  var len = get(this, 'length');
@@ -6358,7 +6478,6 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6358
6478
  pushCtx(context);
6359
6479
  return last;
6360
6480
  }
6361
-
6362
6481
  }).property(),
6363
6482
 
6364
6483
  /**
@@ -6525,7 +6644,7 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6525
6644
  @returns {Array} filtered array
6526
6645
  */
6527
6646
  filterProperty: function(key, value) {
6528
- return this.filter(iter(key, value));
6647
+ return this.filter(iter.apply(this, arguments));
6529
6648
  },
6530
6649
 
6531
6650
  /**
@@ -6580,7 +6699,7 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6580
6699
  @returns {Object} found item or null
6581
6700
  */
6582
6701
  findProperty: function(key, value) {
6583
- return this.find(iter(key, value));
6702
+ return this.find(iter.apply(this, arguments));
6584
6703
  },
6585
6704
 
6586
6705
  /**
@@ -6625,7 +6744,7 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6625
6744
  @returns {Array} filtered array
6626
6745
  */
6627
6746
  everyProperty: function(key, value) {
6628
- return this.every(iter(key, value));
6747
+ return this.every(iter.apply(this, arguments));
6629
6748
  },
6630
6749
 
6631
6750
 
@@ -6671,7 +6790,7 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6671
6790
  @returns {Boolean} true
6672
6791
  */
6673
6792
  someProperty: function(key, value) {
6674
- return this.some(iter(key, value));
6793
+ return this.some(iter.apply(this, arguments));
6675
6794
  },
6676
6795
 
6677
6796
  /**
@@ -6741,7 +6860,7 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6741
6860
 
6742
6861
  /**
6743
6862
  Simply converts the enumerable into a genuine array. The order is not
6744
- gauranteed. Corresponds to the method implemented by Prototype.
6863
+ guaranteed. Corresponds to the method implemented by Prototype.
6745
6864
 
6746
6865
  @returns {Array} the enumerable as an array.
6747
6866
  */
@@ -7068,11 +7187,23 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
7068
7187
  },
7069
7188
 
7070
7189
  /**
7071
- Returns the index for a particular object in the index.
7190
+ Returns the index of the given object's first occurrence.
7191
+ If no startAt argument is given, the starting location to
7192
+ search is 0. If it's negative, will count backward from
7193
+ the end of the array. Returns -1 if no match is found.
7072
7194
 
7073
7195
  @param {Object} object the item to search for
7074
- @param {NUmber} startAt optional starting location to search, default 0
7075
- @returns {Number} index of -1 if not found
7196
+ @param {Number} startAt optional starting location to search, default 0
7197
+ @returns {Number} index or -1 if not found
7198
+
7199
+ @example
7200
+ var arr = ["a", "b", "c", "d", "a"];
7201
+ arr.indexOf("a"); => 0
7202
+ arr.indexOf("z"); => -1
7203
+ arr.indexOf("a", 2); => 4
7204
+ arr.indexOf("a", -1); => 4
7205
+ arr.indexOf("b", 3); => -1
7206
+ arr.indexOf("a", 100); => -1
7076
7207
  */
7077
7208
  indexOf: function(object, startAt) {
7078
7209
  var idx, len = get(this, 'length');
@@ -7087,16 +7218,28 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
7087
7218
  },
7088
7219
 
7089
7220
  /**
7090
- Returns the last index for a particular object in the index.
7221
+ Returns the index of the given object's last occurrence.
7222
+ If no startAt argument is given, the search starts from
7223
+ the last position. If it's negative, will count backward
7224
+ from the end of the array. Returns -1 if no match is found.
7091
7225
 
7092
7226
  @param {Object} object the item to search for
7093
- @param {NUmber} startAt optional starting location to search, default 0
7094
- @returns {Number} index of -1 if not found
7227
+ @param {Number} startAt optional starting location to search, default 0
7228
+ @returns {Number} index or -1 if not found
7229
+
7230
+ @example
7231
+ var arr = ["a", "b", "c", "d", "a"];
7232
+ arr.lastIndexOf("a"); => 4
7233
+ arr.lastIndexOf("z"); => -1
7234
+ arr.lastIndexOf("a", 2); => 0
7235
+ arr.lastIndexOf("a", -1); => 4
7236
+ arr.lastIndexOf("b", 3); => 1
7237
+ arr.lastIndexOf("a", 100); => 4
7095
7238
  */
7096
7239
  lastIndexOf: function(object, startAt) {
7097
7240
  var idx, len = get(this, 'length');
7098
7241
 
7099
- if (startAt === undefined) startAt = len-1;
7242
+ if (startAt === undefined || startAt >= len) startAt = len-1;
7100
7243
  if (startAt < 0) startAt += len;
7101
7244
 
7102
7245
  for(idx=startAt;idx>=0;idx--) {
@@ -7741,11 +7884,12 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
7741
7884
  @returns {Ember.Observable}
7742
7885
  */
7743
7886
  setProperties: function(hash) {
7744
- Ember.beginPropertyChanges(this);
7745
- for(var prop in hash) {
7746
- if (hash.hasOwnProperty(prop)) set(this, prop, hash[prop]);
7747
- }
7748
- Ember.endPropertyChanges(this);
7887
+ var self = this;
7888
+ Ember.changeProperties(function(){
7889
+ for(var prop in hash) {
7890
+ if (hash.hasOwnProperty(prop)) set(self, prop, hash[prop]);
7891
+ }
7892
+ });
7749
7893
  return this;
7750
7894
  },
7751
7895
 
@@ -8057,7 +8201,7 @@ CoreObject.PrototypeMixin = Ember.Mixin.create(
8057
8201
  @private
8058
8202
  */
8059
8203
  _scheduledDestroy: function() {
8060
- this[Ember.META_KEY] = null;
8204
+ Ember.destroy(this);
8061
8205
  },
8062
8206
 
8063
8207
  bind: function(to, from) {
@@ -9725,7 +9869,7 @@ Ember.Comparable = Ember.Mixin.create( /** @scope Ember.Comparable.prototype */{
9725
9869
 
9726
9870
 
9727
9871
  (function(exports) {
9728
- var get = Ember.get, set = Ember.set;
9872
+ var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
9729
9873
 
9730
9874
  Ember.TargetActionSupport = Ember.Mixin.create({
9731
9875
  target: null,
@@ -9735,7 +9879,10 @@ Ember.TargetActionSupport = Ember.Mixin.create({
9735
9879
  var target = get(this, 'target');
9736
9880
 
9737
9881
  if (Ember.typeOf(target) === "string") {
9738
- return Ember.getPath(this, target);
9882
+ // TODO: Remove the false when deprecation is done
9883
+ var value = getPath(this, target, false);
9884
+ if (value === undefined) { value = getPath(window, target); }
9885
+ return value;
9739
9886
  } else {
9740
9887
  return target;
9741
9888
  }
@@ -9746,14 +9893,21 @@ Ember.TargetActionSupport = Ember.Mixin.create({
9746
9893
  target = get(this, 'targetObject');
9747
9894
 
9748
9895
  if (target && action) {
9896
+ var ret;
9897
+
9749
9898
  if (typeof target.send === 'function') {
9750
- target.send(action, this);
9899
+ ret = target.send(action, this);
9751
9900
  } else {
9752
9901
  if (typeof action === 'string') {
9753
9902
  action = target[action];
9754
9903
  }
9755
- action.call(target, this);
9904
+ ret = action.call(target, this);
9756
9905
  }
9906
+ if (ret !== false) ret = true;
9907
+
9908
+ return ret;
9909
+ } else {
9910
+ return false;
9757
9911
  }
9758
9912
  }
9759
9913
  });
@@ -10320,7 +10474,7 @@ Ember._RenderBuffer = Ember.Object.extend(
10320
10474
  set(this ,'elementClasses', Ember.A());
10321
10475
  set(this, 'elementAttributes', {});
10322
10476
  set(this, 'elementStyle', {});
10323
- set(this, 'childBuffers', Ember.A());
10477
+ set(this, 'childBuffers', []);
10324
10478
  set(this, 'elements', {});
10325
10479
  },
10326
10480
 
@@ -10331,7 +10485,7 @@ Ember._RenderBuffer = Ember.Object.extend(
10331
10485
  @returns {Ember.RenderBuffer} this
10332
10486
  */
10333
10487
  push: function(string) {
10334
- get(this, 'childBuffers').pushObject(String(string));
10488
+ get(this, 'childBuffers').push(String(string));
10335
10489
  return this;
10336
10490
  },
10337
10491
 
@@ -10466,7 +10620,7 @@ Ember._RenderBuffer = Ember.Object.extend(
10466
10620
  */
10467
10621
  begin: function(tagName) {
10468
10622
  return this.newBuffer(tagName, this, function(buffer) {
10469
- get(this, 'childBuffers').pushObject(buffer);
10623
+ get(this, 'childBuffers').push(buffer);
10470
10624
  });
10471
10625
  },
10472
10626
 
@@ -10477,7 +10631,7 @@ Ember._RenderBuffer = Ember.Object.extend(
10477
10631
  */
10478
10632
  prepend: function(tagName) {
10479
10633
  return this.newBuffer(tagName, this, function(buffer) {
10480
- get(this, 'childBuffers').insertAt(0, buffer);
10634
+ get(this, 'childBuffers').splice(0, 0, buffer);
10481
10635
  });
10482
10636
  },
10483
10637
 
@@ -10505,7 +10659,7 @@ Ember._RenderBuffer = Ember.Object.extend(
10505
10659
  return this.newBuffer(tagName, parentBuffer, function(buffer) {
10506
10660
  var siblings = get(parentBuffer, 'childBuffers');
10507
10661
  var index = siblings.indexOf(this);
10508
- siblings.insertAt(index + 1, buffer);
10662
+ siblings.splice(index + 1, 0, buffer);
10509
10663
  });
10510
10664
  },
10511
10665
 
@@ -10615,10 +10769,15 @@ Ember.EventDispatcher = Ember.Object.extend(
10615
10769
  The root DOM element to which event listeners should be attached. Event
10616
10770
  listeners will be attached to the document unless this is overridden.
10617
10771
 
10772
+ Can be specified as a DOMElement or a selector string.
10773
+
10774
+ The default body is a string since this may be evaluated before document.body
10775
+ exists in the DOM.
10776
+
10618
10777
  @type DOMElement
10619
- @default document
10778
+ @default 'body'
10620
10779
  */
10621
- rootElement: document,
10780
+ rootElement: 'body',
10622
10781
 
10623
10782
  /**
10624
10783
  @private
@@ -10669,6 +10828,8 @@ Ember.EventDispatcher = Ember.Object.extend(
10669
10828
 
10670
10829
  rootElement.addClass('ember-application');
10671
10830
 
10831
+ ember_assert('Unable to add "ember-application" class to rootElement. Make sure you the body or an element in the body.', rootElement.is('.ember-application'));
10832
+
10672
10833
  for (event in events) {
10673
10834
  if (events.hasOwnProperty(event)) {
10674
10835
  this.setupHandler(rootElement, event, events[event]);
@@ -10749,17 +10910,9 @@ Ember.EventDispatcher = Ember.Object.extend(
10749
10910
 
10750
10911
  /** @private */
10751
10912
  _bubbleEvent: function(view, evt, eventName) {
10752
- var result = true, handler,
10753
- self = this;
10754
-
10755
- Ember.run(function() {
10756
- handler = view[eventName];
10757
- if (Ember.typeOf(handler) === 'function') {
10758
- result = handler.call(view, evt);
10759
- }
10760
- });
10761
-
10762
- return result;
10913
+ return Ember.run(function() {
10914
+ return view.handleEvent(eventName, evt);
10915
+ });
10763
10916
  },
10764
10917
 
10765
10918
  /** @private */
@@ -10813,10 +10966,14 @@ Ember.Application = Ember.Namespace.extend(
10813
10966
  /** @scope Ember.Application.prototype */{
10814
10967
 
10815
10968
  /**
10969
+ The root DOM element of the Application.
10970
+
10971
+ Can be specified as DOMElement or a selector string.
10972
+
10816
10973
  @type DOMElement
10817
- @default document
10974
+ @default 'body'
10818
10975
  */
10819
- rootElement: document,
10976
+ rootElement: 'body',
10820
10977
 
10821
10978
  /**
10822
10979
  @type Ember.EventDispatcher
@@ -10842,21 +10999,35 @@ Ember.Application = Ember.Namespace.extend(
10842
10999
 
10843
11000
  set(this, 'eventDispatcher', eventDispatcher);
10844
11001
 
10845
- var self = this;
10846
- Ember.$(document).ready(function() {
10847
- self.ready();
10848
- });
11002
+ // jQuery 1.7 doesn't call the ready callback if already ready
11003
+ if (Ember.$.isReady) {
11004
+ this.didBecomeReady();
11005
+ } else {
11006
+ var self = this;
11007
+ Ember.$(document).ready(function() {
11008
+ self.didBecomeReady();
11009
+ });
11010
+ }
10849
11011
 
10850
11012
  this._super();
10851
11013
  },
10852
11014
 
10853
- ready: function() {
11015
+ /** @private */
11016
+ didBecomeReady: function() {
10854
11017
  var eventDispatcher = get(this, 'eventDispatcher'),
10855
11018
  customEvents = get(this, 'customEvents');
10856
11019
 
10857
11020
  eventDispatcher.setup(customEvents);
11021
+
11022
+ this.ready();
10858
11023
  },
10859
11024
 
11025
+ /**
11026
+ Called when the Application has become ready.
11027
+ The call will be delayed until the DOM has become ready.
11028
+ */
11029
+ ready: Ember.K,
11030
+
10860
11031
  /** @private */
10861
11032
  destroy: function() {
10862
11033
  get(this, 'eventDispatcher').destroy();
@@ -11044,13 +11215,19 @@ Ember.View = Ember.Object.extend(
11044
11215
  }
11045
11216
  }).property('_parentView'),
11046
11217
 
11218
+ // return the current view, not including virtual views
11219
+ concreteView: Ember.computed(function() {
11220
+ if (!this.isVirtual) { return this; }
11221
+ else { return get(this, 'parentView'); }
11222
+ }).property('_parentView'),
11223
+
11047
11224
  /**
11048
11225
  If false, the view will appear hidden in DOM.
11049
11226
 
11050
11227
  @type Boolean
11051
- @default true
11228
+ @default null
11052
11229
  */
11053
- isVisible: true,
11230
+ isVisible: null,
11054
11231
 
11055
11232
  /**
11056
11233
  Array of child views. You should never edit this array directly.
@@ -11465,6 +11642,9 @@ Ember.View = Ember.Object.extend(
11465
11642
  // Schedule the DOM element to be created and appended to the given
11466
11643
  // element after bindings have synchronized.
11467
11644
  this._insertElementLater(function() {
11645
+ if (get(this, 'isVisible') === null) {
11646
+ set(this, 'isVisible', true);
11647
+ }
11468
11648
  this.$().appendTo(target);
11469
11649
  });
11470
11650
 
@@ -11585,7 +11765,7 @@ Ember.View = Ember.Object.extend(
11585
11765
  */
11586
11766
  renderBuffer: function(tagName) {
11587
11767
  tagName = tagName || get(this, 'tagName');
11588
- if (tagName == null) { tagName = tagName || 'div'; }
11768
+ if (tagName == null) { tagName = 'div'; }
11589
11769
 
11590
11770
  return Ember.RenderBuffer(tagName);
11591
11771
  },
@@ -11774,7 +11954,7 @@ Ember.View = Ember.Object.extend(
11774
11954
  // insert a new buffer after the "parent buffer").
11775
11955
  if (parentBuffer) {
11776
11956
  var tagName = get(this, 'tagName');
11777
- tagName = tagName == null ? 'div' : tagName;
11957
+ if (tagName == null) { tagName = 'div'; }
11778
11958
 
11779
11959
  buffer = parentBuffer[bufferOperation](tagName);
11780
11960
  } else {
@@ -11823,7 +12003,7 @@ Ember.View = Ember.Object.extend(
11823
12003
  buffer.attr('role', role);
11824
12004
  }
11825
12005
 
11826
- if (!get(this, 'isVisible')) {
12006
+ if (get(this, 'isVisible') === false) {
11827
12007
  buffer.style('display', 'none');
11828
12008
  }
11829
12009
  },
@@ -11963,6 +12143,14 @@ Ember.View = Ember.Object.extend(
11963
12143
  set(this, 'domManager', this.domManagerClass.create({ view: this }));
11964
12144
 
11965
12145
  meta(this)["Ember.View"] = {};
12146
+
12147
+ var viewController = get(this, 'viewController');
12148
+ if (viewController) {
12149
+ viewController = Ember.getPath(viewController);
12150
+ if (viewController) {
12151
+ set(viewController, 'view', this);
12152
+ }
12153
+ }
11966
12154
  },
11967
12155
 
11968
12156
  appendChild: function(view, options) {
@@ -12077,7 +12265,10 @@ Ember.View = Ember.Object.extend(
12077
12265
  view = view.create(attrs || {}, { _parentView: this });
12078
12266
 
12079
12267
  var viewName = attrs && attrs.viewName || view.viewName;
12080
- if (viewName) { set(this, viewName, view); }
12268
+
12269
+ // don't set the property on a virtual view, as they are invisible to
12270
+ // consumers of the view API
12271
+ if (viewName) { set(get(this, 'concreteView'), viewName, view); }
12081
12272
  } else {
12082
12273
  ember_assert('must pass instance of View', view instanceof Ember.View);
12083
12274
  set(view, '_parentView', this);
@@ -12085,6 +12276,9 @@ Ember.View = Ember.Object.extend(
12085
12276
  return view;
12086
12277
  },
12087
12278
 
12279
+ becameVisible: Ember.K,
12280
+ becameHidden: Ember.K,
12281
+
12088
12282
  /**
12089
12283
  @private
12090
12284
 
@@ -12092,9 +12286,54 @@ Ember.View = Ember.Object.extend(
12092
12286
  element of the actual DOM element.
12093
12287
  */
12094
12288
  _isVisibleDidChange: Ember.observer(function() {
12095
- this.$().toggle(get(this, 'isVisible'));
12289
+ var isVisible = get(this, 'isVisible');
12290
+
12291
+ this.$().toggle(isVisible);
12292
+
12293
+ if (this._isAncestorHidden()) { return; }
12294
+
12295
+ if (isVisible) {
12296
+ this._notifyBecameVisible();
12297
+ } else {
12298
+ this._notifyBecameHidden();
12299
+ }
12096
12300
  }, 'isVisible'),
12097
12301
 
12302
+ _notifyBecameVisible: function() {
12303
+ this.becameVisible();
12304
+
12305
+ this.forEachChildView(function(view) {
12306
+ var isVisible = get(view, 'isVisible');
12307
+
12308
+ if (isVisible || isVisible === null) {
12309
+ view._notifyBecameVisible();
12310
+ }
12311
+ });
12312
+ },
12313
+
12314
+ _notifyBecameHidden: function() {
12315
+ this.becameHidden();
12316
+ this.forEachChildView(function(view) {
12317
+ var isVisible = get(view, 'isVisible');
12318
+
12319
+ if (isVisible || isVisible === null) {
12320
+ view._notifyBecameHidden();
12321
+ }
12322
+ });
12323
+ },
12324
+
12325
+ _isAncestorHidden: function() {
12326
+ var parent = get(this, 'parentView');
12327
+
12328
+ while (parent) {
12329
+ if (get(parent, 'isVisible') === false) { return true; }
12330
+
12331
+ parent = get(parent, 'parentView');
12332
+ }
12333
+
12334
+ return false;
12335
+ },
12336
+
12098
12337
  clearBuffer: function() {
12099
12338
  this.invokeRecursively(function(view) {
12100
12339
  meta(view)['Ember.View'].buffer = null;
@@ -12109,6 +12348,19 @@ Ember.View = Ember.Object.extend(
12109
12348
  view.transitionTo(state);
12110
12349
  });
12111
12350
  }
12351
+ },
12352
+
12353
+ // .......................................................
12354
+ // EVENT HANDLING
12355
+ //
12356
+
12357
+ /**
12358
+ @private
12359
+
12360
+ Handle events from `Ember.EventDispatcher`
12361
+ */
12362
+ handleEvent: function(eventName, evt) {
12363
+ return this.invokeForState('handleEvent', eventName, evt);
12112
12364
  }
12113
12365
 
12114
12366
  });
@@ -12232,6 +12484,11 @@ Ember.View.states = {
12232
12484
 
12233
12485
  getElement: function() {
12234
12486
  return null;
12487
+ },
12488
+
12489
+ // Handle events from `Ember.EventDispatcher`
12490
+ handleEvent: function() {
12491
+ return true; // continue event propagation
12235
12492
  }
12236
12493
  }
12237
12494
  };
@@ -12428,6 +12685,16 @@ Ember.View.states.hasElement = {
12428
12685
 
12429
12686
  get(view, 'domManager').remove();
12430
12687
  return view;
12688
+ },
12689
+
12690
+ // Handle events from `Ember.EventDispatcher`
12691
+ handleEvent: function(view, eventName, evt) {
12692
+ var handler = view[eventName];
12693
+ if (Ember.typeOf(handler) === 'function') {
12694
+ return handler.call(view, evt);
12695
+ } else {
12696
+ return true; // continue event propagation
12697
+ }
12431
12698
  }
12432
12699
  };
12433
12700
 
@@ -12561,7 +12828,7 @@ Ember.ContainerView = Ember.View.extend({
12561
12828
  },
12562
12829
 
12563
12830
  /**
12564
- When the container view is destroyer, tear down the child views
12831
+ When the container view is destroyed, tear down the child views
12565
12832
  array observer.
12566
12833
 
12567
12834
  @private
@@ -12588,6 +12855,11 @@ Ember.ContainerView = Ember.View.extend({
12588
12855
  @param {Number} removed the number of child views removed
12589
12856
  **/
12590
12857
  childViewsWillChange: function(views, start, removed) {
12858
+ if (removed === 0) { return; }
12859
+
12860
+ var changedViews = views.slice(start, removed);
12861
+ this.setParentView(changedViews, null);
12862
+
12591
12863
  this.invokeForState('childViewsWillChange', views, start, removed);
12592
12864
  },
12593
12865
 
@@ -12612,10 +12884,19 @@ Ember.ContainerView = Ember.View.extend({
12612
12884
  // No new child views were added; bail out.
12613
12885
  if (added === 0) return;
12614
12886
 
12887
+ var changedViews = views.slice(start, added);
12888
+ this.setParentView(changedViews, this);
12889
+
12615
12890
  // Let the current state handle the changes
12616
12891
  this.invokeForState('childViewsDidChange', views, start, added);
12617
12892
  },
12618
12893
 
12894
+ setParentView: function(views, parentView) {
12895
+ views.forEach(function(view) {
12896
+ set(view, '_parentView', parentView);
12897
+ });
12898
+ },
12899
+
12619
12900
  /**
12620
12901
  Schedules a child view to be inserted into the DOM after bindings have
12621
12902
  finished syncing for this run loop.
@@ -12825,6 +13106,10 @@ Ember.CollectionView = Ember.ContainerView.extend(
12825
13106
  childViews = get(this, 'childViews'),
12826
13107
  addedViews = [], view, item, idx, len, itemTagName;
12827
13108
 
13109
+ if ('string' === typeof itemViewClass) {
13110
+ itemViewClass = Ember.getPath(itemViewClass);
13111
+ }
13112
+
12828
13113
  ember_assert(fmt("itemViewClass must be a subclass of Ember.View, not %@", [itemViewClass]), Ember.View.detect(itemViewClass));
12829
13114
 
12830
13115
  len = content ? get(content, 'length') : 0;
@@ -12904,7 +13189,8 @@ Ember.CollectionView.CONTAINER_MAP = {
12904
13189
  // Portions ©2008-2011 Apple Inc. All rights reserved.
12905
13190
  // License: Licensed under MIT license (see license.js)
12906
13191
  // ==========================================================================
12907
- Ember.$ = jQuery;
13192
+ ember_assert("Ember requires jQuery 1.6 or 1.7", window.jQuery && jQuery().jquery.match(/^1\.[67](.\d+)?$/));
13193
+ Ember.$ = window.jQuery;
12908
13194
  })({});
12909
13195
 
12910
13196
  (function(exports) {
@@ -12916,14 +13202,50 @@ Ember.State = Ember.Object.extend({
12916
13202
  start: null,
12917
13203
 
12918
13204
  init: function() {
12919
- Ember.keys(this).forEach(function(name) {
12920
- var value = this[name];
13205
+ var states = get(this, 'states'), foundStates;
13206
+
13207
+ // As a convenience, loop over the properties
13208
+ // of this state and look for any that are other
13209
+ // Ember.State instances or classes, and move them
13210
+ // to the `states` hash. This avoids having to
13211
+ // create an explicit separate hash.
13212
+
13213
+ if (!states) {
13214
+ states = {};
13215
+ for (var name in this) {
13216
+ if (name === "constructor") { continue; }
13217
+ value = this.setupChild(name, this[name]);
13218
+
13219
+ if (value) {
13220
+ foundStates = true;
13221
+ states[name] = value;
13222
+ }
13223
+ }
12921
13224
 
12922
- if (value && value.isState) {
12923
- set(value, 'parentState', this);
12924
- set(value, 'name', (get(this, 'name') || '') + '.' + name);
13225
+ if (foundStates) { set(this, 'states', states); }
13226
+ } else {
13227
+ for (var name in states) {
13228
+ this.setupChild(name, states[name]);
12925
13229
  }
12926
- }, this);
13230
+ }
13231
+
13232
+ set(this, 'routes', {});
13233
+ },
13234
+
13235
+ setupChild: function(name, value) {
13236
+ if (!value) { return false; }
13237
+
13238
+ if (Ember.State.detect(value)) {
13239
+ value = value.create();
13240
+ }
13241
+
13242
+ if (value.isState) {
13243
+ set(value, 'parentState', this);
13244
+ set(value, 'name', (get(this, 'name') || '') + '.' + name);
13245
+ return value;
13246
+ }
13247
+
13248
+ return false;
12927
13249
  },
12928
13250
 
12929
13251
  enter: Ember.K,
@@ -12951,20 +13273,6 @@ Ember.StateManager = Ember.State.extend(
12951
13273
  init: function() {
12952
13274
  this._super();
12953
13275
 
12954
- var states = get(this, 'states');
12955
- if (!states) {
12956
- states = {};
12957
- Ember.keys(this).forEach(function(name) {
12958
- var value = get(this, name);
12959
-
12960
- if (value && value.isState) {
12961
- states[name] = value;
12962
- }
12963
- }, this);
12964
-
12965
- set(this, 'states', states);
12966
- }
12967
-
12968
13276
  var initialState = get(this, 'initialState');
12969
13277
 
12970
13278
  if (!initialState && get(this, 'start')) {
@@ -12985,7 +13293,7 @@ Ember.StateManager = Ember.State.extend(
12985
13293
  this property will be the view associated with it. If there is no
12986
13294
  view state active in this state manager, this value will be null.
12987
13295
  */
12988
- currentView: SC.computed(function() {
13296
+ currentView: Ember.computed(function() {
12989
13297
  var currentState = get(this, 'currentState'),
12990
13298
  view;
12991
13299
 
@@ -13019,32 +13327,72 @@ Ember.StateManager = Ember.State.extend(
13019
13327
  }
13020
13328
  },
13021
13329
 
13330
+ findStatesByRoute: function(state, route) {
13331
+ if (!route || route === "") { return undefined; }
13332
+ var r = route.split('.'), ret = [];
13333
+
13334
+ for (var i=0, len = r.length; i < len; i += 1) {
13335
+ var states = get(state, 'states') ;
13336
+
13337
+ if (!states) { return undefined; }
13338
+
13339
+ var s = get(states, r[i]);
13340
+ if (s) { state = s; ret.push(s); }
13341
+ else { return undefined; }
13342
+ }
13343
+
13344
+ return ret;
13345
+ },
13346
+
13022
13347
  goToState: function(name) {
13023
13348
  if (Ember.empty(name)) { return; }
13024
13349
 
13025
13350
  var currentState = get(this, 'currentState') || this, state, newState;
13026
13351
 
13027
- var exitStates = Ember.A();
13352
+ var exitStates = [], enterStates;
13028
13353
 
13029
- newState = getPath(currentState, name);
13030
13354
  state = currentState;
13031
13355
 
13032
- if (!newState) {
13356
+ if (state.routes[name]) {
13357
+ // cache hit
13358
+ exitStates = state.routes[name].exitStates;
13359
+ enterStates = state.routes[name].enterStates;
13360
+ state = state.routes[name].futureState;
13361
+ } else {
13362
+ // cache miss
13363
+
13364
+ newState = this.findStatesByRoute(currentState, name);
13365
+
13033
13366
  while (state && !newState) {
13034
- exitStates[Ember.guidFor(state)] = state;
13035
- exitStates.push(state);
13367
+ exitStates.unshift(state);
13036
13368
 
13037
13369
  state = get(state, 'parentState');
13038
13370
  if (!state) {
13039
- state = get(this, 'states');
13040
- newState = getPath(state, name);
13371
+ newState = this.findStatesByRoute(this, name);
13041
13372
  if (!newState) { return; }
13042
13373
  }
13043
- newState = getPath(state, name);
13374
+ newState = this.findStatesByRoute(state, name);
13375
+ }
13376
+
13377
+ enterStates = newState.slice(0), exitStates = exitStates.slice(0);
13378
+
13379
+ if (enterStates.length > 0) {
13380
+ state = enterStates[enterStates.length - 1];
13381
+
13382
+ while (enterStates.length > 0 && enterStates[0] === exitStates[0]) {
13383
+ enterStates.shift();
13384
+ exitStates.shift();
13385
+ }
13044
13386
  }
13387
+
13388
+ currentState.routes[name] = {
13389
+ exitStates: exitStates,
13390
+ enterStates: enterStates,
13391
+ futureState: state
13392
+ };
13045
13393
  }
13046
13394
 
13047
- this.enterState(state, name, exitStates);
13395
+ this.enterState(exitStates, enterStates, state);
13048
13396
  },
13049
13397
 
13050
13398
  getState: function(name) {
@@ -13081,24 +13429,9 @@ Ember.StateManager = Ember.State.extend(
13081
13429
  if (!async) { transition.resume(); }
13082
13430
  },
13083
13431
 
13084
- enterState: function(parent, name, exitStates) {
13432
+ enterState: function(exitStates, enterStates, state) {
13085
13433
  var log = Ember.LOG_STATE_TRANSITIONS;
13086
13434
 
13087
- var parts = name.split("."), state = parent, enterStates = Ember.A();
13088
-
13089
- parts.forEach(function(name) {
13090
- state = state[name];
13091
-
13092
- var guid = Ember.guidFor(state);
13093
-
13094
- if (guid in exitStates) {
13095
- exitStates.removeObject(state);
13096
- delete exitStates[guid];
13097
- } else {
13098
- enterStates.push(state);
13099
- }
13100
- });
13101
-
13102
13435
  var stateManager = this;
13103
13436
 
13104
13437
  this.asyncEach(exitStates, function(state, transition) {
@@ -13138,10 +13471,25 @@ Ember.ViewState = Ember.State.extend({
13138
13471
  isViewState: true,
13139
13472
 
13140
13473
  enter: function(stateManager) {
13141
- var view = get(this, 'view');
13474
+ var view = get(this, 'view'), root, childViews;
13142
13475
 
13143
13476
  if (view) {
13144
- view.appendTo(stateManager.get('rootElement') || 'body');
13477
+ if (Ember.View.detect(view)) {
13478
+ view = view.create();
13479
+ set(this, 'view', view);
13480
+ }
13481
+
13482
+ ember_assert('view must be an Ember.View', view instanceof Ember.View);
13483
+
13484
+ root = stateManager.get('rootView');
13485
+
13486
+ if (root) {
13487
+ childViews = get(root, 'childViews');
13488
+ childViews.pushObject(view);
13489
+ } else {
13490
+ root = stateManager.get('rootElement') || 'body';
13491
+ view.appendTo(root);
13492
+ }
13145
13493
  }
13146
13494
  },
13147
13495
 
@@ -13149,12 +13497,21 @@ Ember.ViewState = Ember.State.extend({
13149
13497
  var view = get(this, 'view');
13150
13498
 
13151
13499
  if (view) {
13152
- view.remove();
13500
+ // If the view has a parent view, then it is
13501
+ // part of a view hierarchy and should be removed
13502
+ // from its parent.
13503
+ if (get(view, 'parentView')) {
13504
+ view.removeFromParent();
13505
+ } else {
13506
+
13507
+ // Otherwise, the view is a "root view" and
13508
+ // was appended directly to the DOM.
13509
+ view.remove();
13510
+ }
13153
13511
  }
13154
13512
  }
13155
13513
  });
13156
13514
 
13157
-
13158
13515
  })({});
13159
13516
 
13160
13517
 
@@ -13665,7 +14022,7 @@ Ember.Handlebars.Compiler.prototype.mustache = function(mustache) {
13665
14022
  if (mustache.params.length || mustache.hash) {
13666
14023
  return Handlebars.Compiler.prototype.mustache.call(this, mustache);
13667
14024
  } else {
13668
- var id = new Handlebars.AST.IdNode(['bind']);
14025
+ var id = new Handlebars.AST.IdNode(['_triageMustache']);
13669
14026
 
13670
14027
  // Update the mustache node to include a hash value indicating whether the original node
13671
14028
  // was escaped. This will allow us to properly escape values when the underlying value
@@ -13679,6 +14036,19 @@ Ember.Handlebars.Compiler.prototype.mustache = function(mustache) {
13679
14036
  }
13680
14037
  };
13681
14038
 
14039
+ /**
14040
+ Used for precompilation of Ember Handlebars templates. This will not be used during normal
14041
+ app execution.
14042
+
14043
+ @param {String} string The template to precompile
14044
+ */
14045
+ Ember.Handlebars.precompile = function(string) {
14046
+ var ast = Handlebars.parse(string);
14047
+ var options = { data: true, stringParams: true };
14048
+ var environment = new Ember.Handlebars.Compiler().compile(ast, options);
14049
+ return new Ember.Handlebars.JavaScriptCompiler().compile(environment, options, undefined, true);
14050
+ };
14051
+
13682
14052
  /**
13683
14053
  The entry point for Ember Handlebars. This replaces the default Handlebars.compile and turns on
13684
14054
  template-local data and String parameters.
@@ -13694,6 +14064,21 @@ Ember.Handlebars.compile = function(string) {
13694
14064
  return Handlebars.template(templateSpec);
13695
14065
  };
13696
14066
 
14067
+ /**
14068
+ Lookup both on root and on window
14069
+
14070
+ @param {Object} root The object to look up the property on
14071
+ @param {String} path The path to be lookedup
14072
+ */
14073
+ Ember.Handlebars.getPath = function(root, path) {
14074
+ // TODO: Remove this `false` when the `getPath` globals support is removed
14075
+ var value = Ember.getPath(root, path, false);
14076
+ if (value === undefined && root !== window && Ember.isGlobalPath(path)) {
14077
+ value = Ember.getPath(window, path);
14078
+ }
14079
+ return value;
14080
+ };
14081
+
13697
14082
  /**
13698
14083
  Registers a helper in Handlebars that will be called if no property with the
13699
14084
  given name can be found on the current context object, and no helper with
@@ -13780,17 +14165,14 @@ Ember.TextSupport = Ember.Mixin.create(
13780
14165
 
13781
14166
  focusOut: function(event) {
13782
14167
  this._elementValueDidChange();
13783
- return false;
13784
14168
  },
13785
14169
 
13786
14170
  change: function(event) {
13787
14171
  this._elementValueDidChange();
13788
- return false;
13789
14172
  },
13790
14173
 
13791
14174
  keyUp: function(event) {
13792
14175
  this.interpretKeyEvents(event);
13793
- return false;
13794
14176
  },
13795
14177
 
13796
14178
  /**
@@ -13805,7 +14187,7 @@ Ember.TextSupport = Ember.Mixin.create(
13805
14187
  },
13806
14188
 
13807
14189
  _elementValueDidChange: function() {
13808
- set(this, 'value', this.$().val() || '');
14190
+ set(this, 'value', this.$().val());
13809
14191
  }
13810
14192
 
13811
14193
  });
@@ -13837,14 +14219,7 @@ Ember.TextField = Ember.View.extend(Ember.TextSupport,
13837
14219
 
13838
14220
  tagName: "input",
13839
14221
  attributeBindings: ['type', 'value'],
13840
- type: "text",
13841
-
13842
- /**
13843
- @private
13844
- */
13845
- _updateElementValue: function() {
13846
- this.$().val(get(this, 'value'));
13847
- }
14222
+ type: "text"
13848
14223
 
13849
14224
  });
13850
14225
 
@@ -13864,18 +14239,25 @@ Ember.Button = Ember.View.extend(Ember.TargetActionSupport, {
13864
14239
  classNameBindings: ['isActive'],
13865
14240
 
13866
14241
  tagName: 'button',
13867
- attributeBindings: ['type', 'disabled'],
13868
- type: 'button',
13869
- disabled: false,
14242
+
13870
14243
  propagateEvents: false,
13871
14244
 
13872
- click: function() {
13873
- // Actually invoke the button's target and action.
13874
- // This method comes from the Ember.TargetActionSupport mixin.
13875
- this.triggerAction();
14245
+ attributeBindings: ['type', 'disabled', 'href'],
13876
14246
 
13877
- return get(this, 'propagateEvents');
13878
- },
14247
+ // Defaults to 'button' if tagName is 'input' or 'button'
14248
+ type: Ember.computed(function(key, value) {
14249
+ var tagName = this.get('tagName');
14250
+ if (value !== undefined) { this._type = value; }
14251
+ if (this._type !== undefined) { return this._type; }
14252
+ if (tagName === 'input' || tagName === 'button') { return 'button'; }
14253
+ }).property('tagName').cacheable(),
14254
+
14255
+ disabled: false,
14256
+
14257
+ // Allow 'a' tags to act like buttons
14258
+ href: Ember.computed(function() {
14259
+ return this.get('tagName') === 'a' ? '#' : null;
14260
+ }).property('tagName').cacheable(),
13879
14261
 
13880
14262
  mouseDown: function() {
13881
14263
  if (!get(this, 'disabled')) {
@@ -13902,6 +14284,9 @@ Ember.Button = Ember.View.extend(Ember.TargetActionSupport, {
13902
14284
 
13903
14285
  mouseUp: function(event) {
13904
14286
  if (get(this, 'isActive')) {
14287
+ // Actually invoke the button's target and action.
14288
+ // This method comes from the Ember.TargetActionSupport mixin.
14289
+ this.triggerAction();
13905
14290
  set(this, 'isActive', false);
13906
14291
  }
13907
14292
 
@@ -13910,16 +14295,30 @@ Ember.Button = Ember.View.extend(Ember.TargetActionSupport, {
13910
14295
  return get(this, 'propagateEvents');
13911
14296
  },
13912
14297
 
14298
+ keyDown: function(event) {
14299
+ // Handle space or enter
14300
+ if (event.keyCode === 13 || event.keyCode === 32) {
14301
+ this.mouseDown();
14302
+ }
14303
+ },
14304
+
14305
+ keyUp: function(event) {
14306
+ // Handle space or enter
14307
+ if (event.keyCode === 13 || event.keyCode === 32) {
14308
+ this.mouseUp();
14309
+ }
14310
+ },
14311
+
13913
14312
  // TODO: Handle proper touch behavior. Including should make inactive when
13914
14313
  // finger moves more than 20x outside of the edge of the button (vs mouse
13915
14314
  // which goes inactive as soon as mouse goes out of edges.)
13916
14315
 
13917
14316
  touchStart: function(touch) {
13918
- this.mouseDown(touch);
14317
+ return this.mouseDown(touch);
13919
14318
  },
13920
14319
 
13921
14320
  touchEnd: function(touch) {
13922
- this.mouseUp(touch);
14321
+ return this.mouseUp(touch);
13923
14322
  }
13924
14323
  });
13925
14324
 
@@ -13971,11 +14370,11 @@ Ember.TabContainerView = Ember.View.extend();
13971
14370
  var get = Ember.get, getPath = Ember.getPath;
13972
14371
 
13973
14372
  Ember.TabPaneView = Ember.View.extend({
13974
- tabsContainer: SC.computed(function() {
14373
+ tabsContainer: Ember.computed(function() {
13975
14374
  return this.nearestInstanceOf(Ember.TabContainerView);
13976
14375
  }).property(),
13977
14376
 
13978
- isVisible: SC.computed(function() {
14377
+ isVisible: Ember.computed(function() {
13979
14378
  return get(this, 'viewName') === getPath(this, 'tabsContainer.currentView');
13980
14379
  }).property('tabsContainer.currentView')
13981
14380
  });
@@ -13987,7 +14386,7 @@ Ember.TabPaneView = Ember.View.extend({
13987
14386
  var get = Ember.get, setPath = Ember.setPath;
13988
14387
 
13989
14388
  Ember.TabView = Ember.View.extend({
13990
- tabsContainer: SC.computed(function() {
14389
+ tabsContainer: Ember.computed(function() {
13991
14390
  return this.nearestInstanceOf(Ember.TabContainerView);
13992
14391
  }).property(),
13993
14392
 
@@ -14003,6 +14402,93 @@ Ember.TabView = Ember.View.extend({
14003
14402
  })({});
14004
14403
 
14005
14404
 
14405
+ (function(exports) {
14406
+ var set = Ember.set, get = Ember.get, getPath = Ember.getPath;
14407
+
14408
+ Ember.Select = Ember.View.extend({
14409
+ tagName: 'select',
14410
+ template: Ember.Handlebars.compile(
14411
+ '{{#if prompt}}<option>{{prompt}}</option>{{/if}}' +
14412
+ '{{#each content}}{{view Ember.SelectOption contentBinding="this"}}{{/each}}'
14413
+ ),
14414
+
14415
+ content: null,
14416
+ selection: null,
14417
+ prompt: null,
14418
+
14419
+ optionLabelPath: 'content',
14420
+ optionValuePath: 'content',
14421
+
14422
+
14423
+ didInsertElement: function() {
14424
+ var selection = get(this, 'selection');
14425
+
14426
+ if (selection) { this.selectionDidChange(); }
14427
+
14428
+ this.change();
14429
+ },
14430
+
14431
+ change: function() {
14432
+ var selectedIndex = this.$()[0].selectedIndex,
14433
+ content = get(this, 'content'),
14434
+ prompt = get(this, 'prompt');
14435
+
14436
+ if (!content) { return; }
14437
+ if (prompt && selectedIndex === 0) { set(this, 'selection', null); return; }
14438
+
14439
+ if (prompt) { selectedIndex -= 1; }
14440
+ set(this, 'selection', content.objectAt(selectedIndex));
14441
+ },
14442
+
14443
+ selectionDidChange: Ember.observer(function() {
14444
+ var el = this.$()[0],
14445
+ content = get(this, 'content'),
14446
+ selection = get(this, 'selection'),
14447
+ selectionIndex = content.indexOf(selection),
14448
+ prompt = get(this, 'prompt');
14449
+
14450
+ if (prompt) { selectionIndex += 1; }
14451
+ if (el) { el.selectedIndex = selectionIndex; }
14452
+ }, 'selection')
14453
+ });
14454
+
14455
+ Ember.SelectOption = Ember.View.extend({
14456
+ tagName: 'option',
14457
+ template: Ember.Handlebars.compile("{{label}}"),
14458
+ attributeBindings: ['value'],
14459
+
14460
+ init: function() {
14461
+ this.labelPathDidChange();
14462
+ this.valuePathDidChange();
14463
+
14464
+ this._super();
14465
+ },
14466
+
14467
+ labelPathDidChange: Ember.observer(function() {
14468
+ var labelPath = getPath(this, 'parentView.optionLabelPath');
14469
+
14470
+ if (!labelPath) { return; }
14471
+
14472
+ Ember.defineProperty(this, 'label', Ember.computed(function() {
14473
+ return getPath(this, labelPath);
14474
+ }).property(labelPath).cacheable());
14475
+ }, 'parentView.optionLabelPath'),
14476
+
14477
+ valuePathDidChange: Ember.observer(function() {
14478
+ var valuePath = getPath(this, 'parentView.optionValuePath');
14479
+
14480
+ if (!valuePath) { return; }
14481
+
14482
+ Ember.defineProperty(this, 'value', Ember.computed(function() {
14483
+ return getPath(this, valuePath);
14484
+ }).property(valuePath).cacheable());
14485
+ }, 'parentView.optionValuePath')
14486
+ });
14487
+
14488
+
14489
+ })({});
14490
+
14491
+
14006
14492
  (function(exports) {
14007
14493
  // ==========================================================================
14008
14494
  // Project: Ember Handlebar Views
@@ -14098,7 +14584,7 @@ Ember.Metamorph = Ember.Mixin.create({
14098
14584
  // ==========================================================================
14099
14585
  /*globals Handlebars */
14100
14586
 
14101
- var get = Ember.get, set = Ember.set, getPath = Ember.getPath;
14587
+ var get = Ember.get, set = Ember.set, getPath = Ember.Handlebars.getPath;
14102
14588
  /**
14103
14589
  @ignore
14104
14590
  @private
@@ -14169,6 +14655,29 @@ Ember._BindableSpanView = Ember.View.extend(Ember.Metamorph,
14169
14655
  */
14170
14656
  property: null,
14171
14657
 
14658
+ normalizedValue: Ember.computed(function() {
14659
+ var property = get(this, 'property'),
14660
+ context = get(this, 'previousContext'),
14661
+ valueNormalizer = get(this, 'valueNormalizerFunc'),
14662
+ result;
14663
+
14664
+ // Use the current context as the result if no
14665
+ // property is provided.
14666
+ if (property === '') {
14667
+ result = context;
14668
+ } else {
14669
+ result = getPath(context, property);
14670
+ }
14671
+
14672
+ return valueNormalizer ? valueNormalizer(result) : result;
14673
+ }).property('property', 'previousContext', 'valueNormalizerFunc'),
14674
+
14675
+ rerenderIfNeeded: function() {
14676
+ if (!get(this, 'isDestroyed') && get(this, 'normalizedValue') !== this._lastNormalizedValue) {
14677
+ this.rerender();
14678
+ }
14679
+ },
14680
+
14172
14681
  /**
14173
14682
  Determines which template to invoke, sets up the correct state based on
14174
14683
  that logic, then invokes the default Ember.View `render` implementation.
@@ -14191,23 +14700,14 @@ Ember._BindableSpanView = Ember.View.extend(Ember.Metamorph,
14191
14700
  var escape = get(this, 'isEscaped');
14192
14701
 
14193
14702
  var shouldDisplay = get(this, 'shouldDisplayFunc'),
14194
- property = get(this, 'property'),
14195
14703
  preserveContext = get(this, 'preserveContext'),
14196
14704
  context = get(this, 'previousContext');
14197
14705
 
14198
14706
  var inverseTemplate = get(this, 'inverseTemplate'),
14199
14707
  displayTemplate = get(this, 'displayTemplate');
14200
14708
 
14201
- var result;
14202
-
14203
-
14204
- // Use the current context as the result if no
14205
- // property is provided.
14206
- if (property === '') {
14207
- result = context;
14208
- } else {
14209
- result = getPath(context, property);
14210
- }
14709
+ var result = get(this, 'normalizedValue');
14710
+ this._lastNormalizedValue = result;
14211
14711
 
14212
14712
  // First, test the conditional to see if we should
14213
14713
  // render the template or not.
@@ -14258,12 +14758,15 @@ Ember._BindableSpanView = Ember.View.extend(Ember.Metamorph,
14258
14758
  // License: Licensed under MIT license (see license.js)
14259
14759
  // ==========================================================================
14260
14760
  /*globals Handlebars */
14261
- var get = Ember.get, getPath = Ember.getPath, set = Ember.set, fmt = Ember.String.fmt;
14761
+ var get = Ember.get, getPath = Ember.Handlebars.getPath, set = Ember.set, fmt = Ember.String.fmt;
14762
+
14763
+ var EmberHandlebars = Ember.Handlebars, helpers = EmberHandlebars.helpers;
14764
+ var helpers = EmberHandlebars.helpers;
14262
14765
 
14263
14766
  (function() {
14264
14767
  // Binds a property into the DOM. This will create a hook in DOM that the
14265
- // KVO system will look for and upate if the property changes.
14266
- var bind = function(property, options, preserveContext, shouldDisplay) {
14768
+ // KVO system will look for and update if the property changes.
14769
+ var bind = function(property, options, preserveContext, shouldDisplay, valueNormalizer) {
14267
14770
  var data = options.data,
14268
14771
  fn = options.fn,
14269
14772
  inverse = options.inverse,
@@ -14278,6 +14781,7 @@ var get = Ember.get, getPath = Ember.getPath, set = Ember.set, fmt = Ember.Strin
14278
14781
  var bindView = view.createChildView(Ember._BindableSpanView, {
14279
14782
  preserveContext: preserveContext,
14280
14783
  shouldDisplayFunc: shouldDisplay,
14784
+ valueNormalizerFunc: valueNormalizer,
14281
14785
  displayTemplate: fn,
14282
14786
  inverseTemplate: inverse,
14283
14787
  property: property,
@@ -14287,17 +14791,9 @@ var get = Ember.get, getPath = Ember.getPath, set = Ember.set, fmt = Ember.Strin
14287
14791
 
14288
14792
  view.appendChild(bindView);
14289
14793
 
14290
- var observer, invoker;
14291
-
14292
- /** @private */
14293
- observer = function(){
14294
- // Double check since sometimes the view gets destroyed after this observer is already queued
14295
- if (!get(bindView, 'isDestroyed')) { bindView.rerender(); }
14296
- };
14297
-
14298
14794
  /** @private */
14299
- invoker = function() {
14300
- Ember.run.once(observer);
14795
+ var observer = function() {
14796
+ Ember.run.once(bindView, 'rerenderIfNeeded');
14301
14797
  };
14302
14798
 
14303
14799
  // Observes the given property on the context and
@@ -14305,7 +14801,7 @@ var get = Ember.get, getPath = Ember.getPath, set = Ember.set, fmt = Ember.Strin
14305
14801
  // is an empty string, we are printing the current context
14306
14802
  // object ({{this}}) so updating it is not our responsibility.
14307
14803
  if (property !== '') {
14308
- Ember.addObserver(ctx, property, invoker);
14804
+ Ember.addObserver(ctx, property, observer);
14309
14805
  }
14310
14806
  } else {
14311
14807
  // The object is not observable, so just render it out and
@@ -14314,6 +14810,30 @@ var get = Ember.get, getPath = Ember.getPath, set = Ember.set, fmt = Ember.Strin
14314
14810
  }
14315
14811
  };
14316
14812
 
14813
+ /**
14814
+ '_triageMustache' is used internally select between a binding and helper for
14815
+ the given context. Until this point, it would be hard to determine if the
14816
+ mustache is a property reference or a regular helper reference. This triage
14817
+ helper resolves that.
14818
+
14819
+ This would not be typically invoked by directly.
14820
+
14821
+ @private
14822
+ @name Handlebars.helpers._triageMustache
14823
+ @param {String} property Property/helperID to triage
14824
+ @param {Function} fn Context to provide for rendering
14825
+ @returns {String} HTML string
14826
+ */
14827
+ EmberHandlebars.registerHelper('_triageMustache', function(property, fn) {
14828
+ ember_assert("You cannot pass more than one argument to the _triageMustache helper", arguments.length <= 2);
14829
+ if (helpers[property]) {
14830
+ return helpers[property].call(this, fn);
14831
+ }
14832
+ else {
14833
+ return helpers.bind.apply(this, arguments);
14834
+ }
14835
+ });
14836
+
14317
14837
  /**
14318
14838
  `bind` can be used to display a value, then update that value if it
14319
14839
  changes. For example, if you wanted to print the `title` property of
@@ -14334,7 +14854,7 @@ var get = Ember.get, getPath = Ember.getPath, set = Ember.set, fmt = Ember.Strin
14334
14854
  @param {Function} fn Context to provide for rendering
14335
14855
  @returns {String} HTML string
14336
14856
  */
14337
- Ember.Handlebars.registerHelper('bind', function(property, fn) {
14857
+ EmberHandlebars.registerHelper('bind', function(property, fn) {
14338
14858
  ember_assert("You cannot pass more than one argument to the bind helper", arguments.length <= 2);
14339
14859
 
14340
14860
  var context = (fn.contexts && fn.contexts[0]) || this;
@@ -14358,16 +14878,17 @@ var get = Ember.get, getPath = Ember.getPath, set = Ember.set, fmt = Ember.Strin
14358
14878
  @param {Function} fn Context to provide for rendering
14359
14879
  @returns {String} HTML string
14360
14880
  */
14361
- Ember.Handlebars.registerHelper('boundIf', function(property, fn) {
14881
+ EmberHandlebars.registerHelper('boundIf', function(property, fn) {
14362
14882
  var context = (fn.contexts && fn.contexts[0]) || this;
14363
-
14364
- return bind.call(context, property, fn, true, function(result) {
14883
+ var func = function(result) {
14365
14884
  if (Ember.typeOf(result) === 'array') {
14366
14885
  return get(result, 'length') !== 0;
14367
14886
  } else {
14368
14887
  return !!result;
14369
14888
  }
14370
- } );
14889
+ };
14890
+
14891
+ return bind.call(context, property, fn, true, func, func);
14371
14892
  });
14372
14893
  })();
14373
14894
 
@@ -14377,11 +14898,11 @@ var get = Ember.get, getPath = Ember.getPath, set = Ember.set, fmt = Ember.Strin
14377
14898
  @param {Hash} options
14378
14899
  @returns {String} HTML string
14379
14900
  */
14380
- Ember.Handlebars.registerHelper('with', function(context, options) {
14901
+ EmberHandlebars.registerHelper('with', function(context, options) {
14381
14902
  ember_assert("You must pass exactly one argument to the with helper", arguments.length == 2);
14382
14903
  ember_assert("You must pass a block to the with helper", options.fn && options.fn !== Handlebars.VM.noop);
14383
14904
 
14384
- return Ember.Handlebars.helpers.bind.call(options.contexts[0], context, options);
14905
+ return helpers.bind.call(options.contexts[0], context, options);
14385
14906
  });
14386
14907
 
14387
14908
 
@@ -14391,11 +14912,11 @@ Ember.Handlebars.registerHelper('with', function(context, options) {
14391
14912
  @param {Hash} options
14392
14913
  @returns {String} HTML string
14393
14914
  */
14394
- Ember.Handlebars.registerHelper('if', function(context, options) {
14915
+ EmberHandlebars.registerHelper('if', function(context, options) {
14395
14916
  ember_assert("You must pass exactly one argument to the if helper", arguments.length == 2);
14396
14917
  ember_assert("You must pass a block to the if helper", options.fn && options.fn !== Handlebars.VM.noop);
14397
14918
 
14398
- return Ember.Handlebars.helpers.boundIf.call(options.contexts[0], context, options);
14919
+ return helpers.boundIf.call(options.contexts[0], context, options);
14399
14920
  });
14400
14921
 
14401
14922
  /**
@@ -14404,7 +14925,7 @@ Ember.Handlebars.registerHelper('if', function(context, options) {
14404
14925
  @param {Hash} options
14405
14926
  @returns {String} HTML string
14406
14927
  */
14407
- Ember.Handlebars.registerHelper('unless', function(context, options) {
14928
+ EmberHandlebars.registerHelper('unless', function(context, options) {
14408
14929
  ember_assert("You must pass exactly one argument to the unless helper", arguments.length == 2);
14409
14930
  ember_assert("You must pass a block to the unless helper", options.fn && options.fn !== Handlebars.VM.noop);
14410
14931
 
@@ -14413,7 +14934,7 @@ Ember.Handlebars.registerHelper('unless', function(context, options) {
14413
14934
  options.fn = inverse;
14414
14935
  options.inverse = fn;
14415
14936
 
14416
- return Ember.Handlebars.helpers.boundIf.call(options.contexts[0], context, options);
14937
+ return helpers.boundIf.call(options.contexts[0], context, options);
14417
14938
  });
14418
14939
 
14419
14940
  /**
@@ -14426,7 +14947,7 @@ Ember.Handlebars.registerHelper('unless', function(context, options) {
14426
14947
  @param {Hash} options
14427
14948
  @returns {String} HTML string
14428
14949
  */
14429
- Ember.Handlebars.registerHelper('bindAttr', function(options) {
14950
+ EmberHandlebars.registerHelper('bindAttr', function(options) {
14430
14951
 
14431
14952
  var attrs = options.hash;
14432
14953
 
@@ -14444,7 +14965,7 @@ Ember.Handlebars.registerHelper('bindAttr', function(options) {
14444
14965
  // Handle classes differently, as we can bind multiple classes
14445
14966
  var classBindings = attrs['class'];
14446
14967
  if (classBindings !== null && classBindings !== undefined) {
14447
- var classResults = Ember.Handlebars.bindClasses(this, classBindings, view, dataId);
14968
+ var classResults = EmberHandlebars.bindClasses(this, classBindings, view, dataId);
14448
14969
  ret.push('class="' + classResults.join(' ') + '"');
14449
14970
  delete attrs['class'];
14450
14971
  }
@@ -14506,7 +15027,7 @@ Ember.Handlebars.registerHelper('bindAttr', function(options) {
14506
15027
 
14507
15028
  // Add the unique identifier
14508
15029
  ret.push('data-bindAttr-' + dataId + '="' + dataId + '"');
14509
- return new Ember.Handlebars.SafeString(ret.join(' '));
15030
+ return new EmberHandlebars.SafeString(ret.join(' '));
14510
15031
  });
14511
15032
 
14512
15033
  /**
@@ -14535,7 +15056,7 @@ Ember.Handlebars.registerHelper('bindAttr', function(options) {
14535
15056
 
14536
15057
  @returns {Array} An array of class names to add
14537
15058
  */
14538
- Ember.Handlebars.bindClasses = function(context, classBindings, view, bindAttrId) {
15059
+ EmberHandlebars.bindClasses = function(context, classBindings, view, bindAttrId) {
14539
15060
  var ret = [], newClass, value, elem;
14540
15061
 
14541
15062
  // Helper method to retrieve the property from the context and
@@ -14543,9 +15064,10 @@ Ember.Handlebars.bindClasses = function(context, classBindings, view, bindAttrId
14543
15064
  // a Boolean or not.
14544
15065
  var classStringForProperty = function(property) {
14545
15066
  var split = property.split(':'),
14546
- property = split[0],
14547
15067
  className = split[1];
14548
15068
 
15069
+ property = split[0];
15070
+
14549
15071
  var val = getPath(context, property);
14550
15072
 
14551
15073
  // If value is a Boolean and true, return the dasherized property
@@ -14719,7 +15241,7 @@ Ember.Handlebars.ViewHelper = Ember.Object.create({
14719
15241
  newView;
14720
15242
 
14721
15243
  if ('string' === typeof path) {
14722
- newView = Ember.getPath(thisContext, path);
15244
+ newView = Ember.Handlebars.getPath(thisContext, path);
14723
15245
  ember_assert("Unable to find view at path '" + path + "'", !!newView);
14724
15246
  } else {
14725
15247
  newView = path;
@@ -14771,7 +15293,7 @@ Ember.Handlebars.registerHelper('view', function(path, options) {
14771
15293
  /*globals Handlebars ember_assert */
14772
15294
 
14773
15295
  // TODO: Don't require all of this module
14774
- var get = Ember.get, fmt = Ember.String.fmt;
15296
+ var get = Ember.get, getPath = Ember.Handlebars.getPath, fmt = Ember.String.fmt;
14775
15297
 
14776
15298
  /**
14777
15299
  @name Handlebars.helpers.collection
@@ -14796,7 +15318,7 @@ Ember.Handlebars.registerHelper('collection', function(path, options) {
14796
15318
  // If passed a path string, convert that into an object.
14797
15319
  // Otherwise, just default to the standard class.
14798
15320
  var collectionClass;
14799
- collectionClass = path ? Ember.getPath(this, path) : Ember.CollectionView;
15321
+ collectionClass = path ? getPath(this, path) : Ember.CollectionView;
14800
15322
  ember_assert(fmt("%@ #collection: Could not find %@", data.view, path), !!collectionClass);
14801
15323
 
14802
15324
  var hash = options.hash, itemHash = {}, match;
@@ -14805,7 +15327,7 @@ Ember.Handlebars.registerHelper('collection', function(path, options) {
14805
15327
  var itemViewClass, itemViewPath = hash.itemViewClass;
14806
15328
  var collectionPrototype = get(collectionClass, 'proto');
14807
15329
  delete hash.itemViewClass;
14808
- itemViewClass = itemViewPath ? Ember.getPath(collectionPrototype, itemViewPath) : collectionPrototype.itemViewClass;
15330
+ itemViewClass = itemViewPath ? getPath(collectionPrototype, itemViewPath) : collectionPrototype.itemViewClass;
14809
15331
  ember_assert(fmt("%@ #collection: Could not find %@", data.view, itemViewPath), !!itemViewClass);
14810
15332
 
14811
15333
  // Go through options passed to the {{collection}} helper and extract options
@@ -14863,7 +15385,7 @@ Ember.Handlebars.registerHelper('collection', function(path, options) {
14863
15385
  // License: Licensed under MIT license (see license.js)
14864
15386
  // ==========================================================================
14865
15387
  /*globals Handlebars */
14866
- var getPath = Ember.getPath;
15388
+ var getPath = Ember.Handlebars.getPath;
14867
15389
 
14868
15390
  /**
14869
15391
  `unbound` allows you to output a property without binding. *Important:* The
@@ -14976,6 +15498,64 @@ Ember.Handlebars.registerHelper('template', function(name, options) {
14976
15498
  })({});
14977
15499
 
14978
15500
 
15501
+ (function(exports) {
15502
+ var EmberHandlebars = Ember.Handlebars, getPath = Ember.Handlebars.getPath;
15503
+
15504
+ var ActionHelper = EmberHandlebars.ActionHelper = {};
15505
+
15506
+ ActionHelper.registerAction = function(actionName, eventName, target, view) {
15507
+ var actionId = (++jQuery.uuid).toString(),
15508
+ existingHandler = view[eventName],
15509
+ handler;
15510
+
15511
+ if (existingHandler) {
15512
+ var handler = function(event) {
15513
+ var ret;
15514
+ if ($(event.target).closest('[data-ember-action]').attr('data-ember-action') === actionId) {
15515
+ ret = target[actionName](event);
15516
+ }
15517
+ return ret !== false ? existingHandler.call(view, event) : ret;
15518
+ };
15519
+ } else {
15520
+ var handler = function(event) {
15521
+ if ($(event.target).closest('[data-ember-action]').attr('data-ember-action') === actionId) {
15522
+ return target[actionName](event);
15523
+ }
15524
+ };
15525
+ }
15526
+
15527
+ view[eventName] = handler;
15528
+
15529
+ view.reopen({
15530
+ rerender: function() {
15531
+ if (existingHandler) {
15532
+ view[eventName] = existingHandler;
15533
+ } else {
15534
+ view[eventName] = null;
15535
+ }
15536
+ return this._super();
15537
+ }
15538
+ });
15539
+
15540
+ return actionId;
15541
+ };
15542
+
15543
+ EmberHandlebars.registerHelper('action', function(actionName, options) {
15544
+ var hash = options.hash || {},
15545
+ eventName = options.hash.on || "click",
15546
+ view = options.data.view,
15547
+ target;
15548
+
15549
+ if (view.isVirtual) { view = view.get('parentView'); }
15550
+ target = options.hash.target ? getPath(this, options.hash.target) : view;
15551
+
15552
+ var actionId = ActionHelper.registerAction(actionName, eventName, target, view);
15553
+ return new EmberHandlebars.SafeString('data-ember-action="' + actionId + '"');
15554
+ });
15555
+
15556
+ })({});
15557
+
15558
+
14979
15559
  (function(exports) {
14980
15560
  // ==========================================================================
14981
15561
  // Project: Ember Handlebar Views
@@ -15000,8 +15580,8 @@ Ember.Handlebars.registerHelper('template', function(name, options) {
15000
15580
  // with Ember's Handlebars and are suitable for use as a view's template.
15001
15581
  // Those with type="text/x-raw-handlebars" will be compiled with regular
15002
15582
  // Handlebars and are suitable for use in views' computed properties.
15003
- Ember.Handlebars.bootstrap = function() {
15004
- Ember.$('script[type="text/html"], script[type="text/x-handlebars"], script[type="text/x-raw-handlebars"]')
15583
+ Ember.Handlebars.bootstrap = function(ctx) {
15584
+ Ember.$('script[type="text/html"], script[type="text/x-handlebars"], script[type="text/x-raw-handlebars"]', ctx)
15005
15585
  .each(function() {
15006
15586
  // Get a reference to the script tag
15007
15587
  var script = Ember.$(this),
@@ -15013,7 +15593,7 @@ Ember.Handlebars.bootstrap = function() {
15013
15593
  // id if no name is found.
15014
15594
  templateName = script.attr('data-template-name') || script.attr('id'),
15015
15595
  template = compile(script.html()),
15016
- view, viewPath;
15596
+ view, viewPath, tagName;
15017
15597
 
15018
15598
  if (templateName) {
15019
15599
  // For templates which have a name, we save them and then remove them from the DOM
@@ -15038,8 +15618,13 @@ Ember.Handlebars.bootstrap = function() {
15038
15618
  viewPath = script.attr('data-view');
15039
15619
  view = viewPath ? Ember.getPath(viewPath) : Ember.View;
15040
15620
 
15621
+ // Users can optionally specify a custom tag name to use by setting the
15622
+ // data-tag-name attribute on the script tag.
15623
+ tagName = script.attr('data-tag-name');
15624
+
15041
15625
  view = view.create({
15042
- template: template
15626
+ template: template,
15627
+ tagName: (tagName) ? tagName : undefined
15043
15628
  });
15044
15629
 
15045
15630
  view._insertElementLater(function() {
@@ -15052,7 +15637,11 @@ Ember.Handlebars.bootstrap = function() {
15052
15637
  });
15053
15638
  };
15054
15639
 
15055
- Ember.$(document).ready(Ember.Handlebars.bootstrap);
15640
+ Ember.$(document).ready(
15641
+ function(){
15642
+ Ember.Handlebars.bootstrap( Ember.$(document) );
15643
+ }
15644
+ );
15056
15645
 
15057
15646
  })({});
15058
15647