embient 0.0.5 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -3311,6 +3311,20 @@ var Cp = ComputedProperty.prototype;
3311
3311
  mode the computed property will automatically cache the return value of
3312
3312
  your function until one of the dependent keys changes.
3313
3313
 
3314
+ MyApp.president = Ember.Object.create({
3315
+ fullName: function() {
3316
+ return this.get('firstName') + ' ' + this.get('lastName');
3317
+
3318
+ // After calculating the value of this function, Ember.js will
3319
+ // return that value without re-executing this function until
3320
+ // one of the dependent properties change.
3321
+ }.property('firstName', 'lastName').cacheable()
3322
+ });
3323
+
3324
+ It is common to use `cacheable()` on nearly every computed property
3325
+ you define.
3326
+
3327
+ @name Ember.ComputedProperty.cacheable
3314
3328
  @param {Boolean} aFlag optional set to false to disable cacheing
3315
3329
  @returns {Ember.ComputedProperty} receiver
3316
3330
  */
@@ -3323,6 +3337,16 @@ Cp.cacheable = function(aFlag) {
3323
3337
  Sets the dependent keys on this computed property. Pass any number of
3324
3338
  arguments containing key paths that this computed property depends on.
3325
3339
 
3340
+ MyApp.president = Ember.Object.create({
3341
+ fullName: Ember.computed(function() {
3342
+ return this.get('firstName') + ' ' + this.get('lastName');
3343
+
3344
+ // Tell Ember.js that this computed property depends on firstName
3345
+ // and lastName
3346
+ }).property('firstName', 'lastName')
3347
+ });
3348
+
3349
+ @name Ember.ComputedProperty.property
3326
3350
  @param {String} path... zero or more property paths
3327
3351
  @returns {Ember.ComputedProperty} receiver
3328
3352
  */
@@ -3348,6 +3372,10 @@ Cp.property = function() {
3348
3372
  computed property descriptor under the `_meta` key. Ember runtime
3349
3373
  exposes a public API for retrieving these values from classes,
3350
3374
  via the `metaForProperty()` function.
3375
+
3376
+ @name Ember.ComputedProperty.meta
3377
+ @param {Hash} metadata
3378
+ @returns {Ember.ComputedProperty} property descriptor instance
3351
3379
  */
3352
3380
 
3353
3381
  Cp.meta = function(meta) {
@@ -3458,8 +3486,8 @@ Ember.computed = function(func) {
3458
3486
  var args;
3459
3487
 
3460
3488
  if (arguments.length > 1) {
3461
- args = [].slice.call(arguments, 0, -1);
3462
- func = [].slice.call(arguments, -1)[0];
3489
+ args = a_slice.call(arguments, 0, -1);
3490
+ func = a_slice.call(arguments, -1)[0];
3463
3491
  }
3464
3492
 
3465
3493
  var cp = new ComputedProperty(func);
@@ -3474,7 +3502,11 @@ Ember.computed = function(func) {
3474
3502
  })({});
3475
3503
 
3476
3504
  (function(exports) {
3477
- /*jshint newcap:true*/
3505
+ /*jshint newcap:false*/
3506
+
3507
+ // NOTE: There is a bug in jshint that doesn't recognize `Object()` without `new`
3508
+ // as being ok unless both `newcap:false` and not `use strict`.
3509
+ // https://github.com/jshint/jshint/issues/392
3478
3510
 
3479
3511
  // Testing this is not ideal, but we want ArrayUtils to use native functions
3480
3512
  // if available, but not to use versions created by libraries like Prototype
@@ -3487,7 +3519,7 @@ var isNativeFunc = function(func) {
3487
3519
  // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/map
3488
3520
  /** @private */
3489
3521
  var arrayMap = isNativeFunc(Array.prototype.map) ? Array.prototype.map : function(fun /*, thisp */) {
3490
- "use strict";
3522
+ //"use strict";
3491
3523
 
3492
3524
  if (this === void 0 || this === null) {
3493
3525
  throw new TypeError();
@@ -3513,7 +3545,7 @@ var arrayMap = isNativeFunc(Array.prototype.map) ? Array.prototype.map : functio
3513
3545
  // From: https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/array/foreach
3514
3546
  /** @private */
3515
3547
  var arrayForEach = isNativeFunc(Array.prototype.forEach) ? Array.prototype.forEach : function(fun /*, thisp */) {
3516
- "use strict";
3548
+ //"use strict";
3517
3549
 
3518
3550
  if (this === void 0 || this === null) {
3519
3551
  throw new TypeError();
@@ -3558,6 +3590,11 @@ Ember.ArrayUtils = {
3558
3590
  indexOf: function(obj) {
3559
3591
  var args = Array.prototype.slice.call(arguments, 1);
3560
3592
  return obj.indexOf ? obj.indexOf.apply(obj, args) : arrayIndexOf.apply(obj, args);
3593
+ },
3594
+
3595
+ removeObject: function(array, item) {
3596
+ var index = this.indexOf(array, item);
3597
+ if (index !== -1) { array.splice(index, 1); }
3561
3598
  }
3562
3599
  };
3563
3600
 
@@ -3592,56 +3629,70 @@ var BEFORE_OBSERVERS = ':before';
3592
3629
  var guidFor = Ember.guidFor;
3593
3630
  var normalizePath = Ember.normalizePath;
3594
3631
 
3595
- var suspended = 0;
3632
+ var deferred = 0;
3596
3633
  var array_Slice = Array.prototype.slice;
3597
3634
  var array_ForEach = Ember.ArrayUtils.forEach;
3598
3635
 
3599
3636
  /** @private */
3600
- var ObserverSet = function(iterateable) {
3601
- this.set = {};
3602
- if (iterateable) { this.array = []; }
3637
+ var ObserverSet = function () {
3638
+ this.targetSet = {};
3603
3639
  };
3604
-
3605
- ObserverSet.prototype.add = function(target, name) {
3606
- var set = this.set, guid = Ember.guidFor(target), array;
3607
-
3608
- if (!set[guid]) { set[guid] = {}; }
3609
- set[guid][name] = true;
3610
- if (array = this.array) {
3611
- array.push([target, name]);
3640
+ ObserverSet.prototype.add = function (target, path) {
3641
+ var targetSet = this.targetSet,
3642
+ targetGuid = Ember.guidFor(target),
3643
+ pathSet = targetSet[targetGuid];
3644
+ if (!pathSet) {
3645
+ targetSet[targetGuid] = pathSet = {};
3646
+ }
3647
+ if (pathSet[path]) {
3648
+ return false;
3649
+ } else {
3650
+ return pathSet[path] = true;
3612
3651
  }
3613
3652
  };
3653
+ ObserverSet.prototype.clear = function () {
3654
+ this.targetSet = {};
3655
+ };
3614
3656
 
3615
- ObserverSet.prototype.contains = function(target, name) {
3616
- var set = this.set, guid = Ember.guidFor(target), nameSet = set[guid];
3617
- return nameSet && nameSet[name];
3657
+ /** @private */
3658
+ var DeferredEventQueue = function() {
3659
+ this.targetSet = {};
3660
+ this.queue = [];
3618
3661
  };
3619
3662
 
3620
- ObserverSet.prototype.empty = function() {
3621
- this.set = {};
3622
- this.array = [];
3663
+ DeferredEventQueue.prototype.push = function(target, eventName) {
3664
+ var targetSet = this.targetSet,
3665
+ queue = this.queue,
3666
+ targetGuid = Ember.guidFor(target),
3667
+ eventNameSet = targetSet[targetGuid],
3668
+ index;
3669
+
3670
+ if (!eventNameSet) {
3671
+ targetSet[targetGuid] = eventNameSet = {};
3672
+ }
3673
+ index = eventNameSet[eventName];
3674
+ if (index === undefined) {
3675
+ eventNameSet[eventName] = queue.push(Ember.deferEvent(target, eventName)) - 1;
3676
+ } else {
3677
+ queue[index] = Ember.deferEvent(target, eventName);
3678
+ }
3623
3679
  };
3624
3680
 
3625
- ObserverSet.prototype.forEach = function(fn) {
3626
- var q = this.array;
3627
- this.empty();
3628
- array_ForEach(q, function(item) {
3629
- fn(item[0], item[1]);
3630
- });
3681
+ DeferredEventQueue.prototype.flush = function() {
3682
+ var queue = this.queue;
3683
+ this.queue = [];
3684
+ this.targetSet = {};
3685
+ for (var i=0, len=queue.length; i < len; ++i) {
3686
+ queue[i]();
3687
+ }
3631
3688
  };
3632
3689
 
3633
- var queue = new ObserverSet(true), beforeObserverSet = new ObserverSet();
3690
+ var queue = new DeferredEventQueue(), beforeObserverSet = new ObserverSet();
3634
3691
 
3635
3692
  /** @private */
3636
3693
  function notifyObservers(obj, eventName, forceNotification) {
3637
- if (suspended && !forceNotification) {
3638
-
3639
- // if suspended add to the queue to send event later - but only send
3640
- // event once.
3641
- if (!queue.contains(obj, eventName)) {
3642
- queue.add(obj, eventName);
3643
- }
3644
-
3694
+ if (deferred && !forceNotification) {
3695
+ queue.push(obj, eventName);
3645
3696
  } else {
3646
3697
  Ember.sendEvent(obj, eventName);
3647
3698
  }
@@ -3649,20 +3700,19 @@ function notifyObservers(obj, eventName, forceNotification) {
3649
3700
 
3650
3701
  /** @private */
3651
3702
  function flushObserverQueue() {
3652
- beforeObserverSet.empty();
3703
+ beforeObserverSet.clear();
3653
3704
 
3654
- if (!queue || queue.array.length===0) return ;
3655
- queue.forEach(function(target, event){ Ember.sendEvent(target, event); });
3705
+ queue.flush();
3656
3706
  }
3657
3707
 
3658
3708
  Ember.beginPropertyChanges = function() {
3659
- suspended++;
3709
+ deferred++;
3660
3710
  return this;
3661
3711
  };
3662
3712
 
3663
3713
  Ember.endPropertyChanges = function() {
3664
- suspended--;
3665
- if (suspended<=0) flushObserverQueue();
3714
+ deferred--;
3715
+ if (deferred<=0) flushObserverQueue();
3666
3716
  };
3667
3717
 
3668
3718
  /**
@@ -3674,15 +3724,30 @@ Ember.endPropertyChanges = function() {
3674
3724
  obj2.set('bar', baz);
3675
3725
  });
3676
3726
  */
3677
- Ember.changeProperties = function(cb){
3727
+ Ember.changeProperties = function(cb, binding){
3678
3728
  Ember.beginPropertyChanges();
3679
3729
  try {
3680
- cb();
3730
+ cb.call(binding);
3681
3731
  } finally {
3682
3732
  Ember.endPropertyChanges();
3683
3733
  }
3684
3734
  };
3685
3735
 
3736
+ /**
3737
+ Set a list of properties on an object. These properties are set inside
3738
+ a single `beginPropertyChanges` and `endPropertyChanges` batch, so
3739
+ observers will be buffered.
3740
+ */
3741
+ Ember.setProperties = function(self, hash) {
3742
+ Ember.changeProperties(function(){
3743
+ for(var prop in hash) {
3744
+ if (hash.hasOwnProperty(prop)) Ember.set(self, prop, hash[prop]);
3745
+ }
3746
+ });
3747
+ return self;
3748
+ };
3749
+
3750
+
3686
3751
  /** @private */
3687
3752
  function changeEvent(keyName) {
3688
3753
  return keyName+AFTER_OBSERVERS;
@@ -3759,6 +3824,15 @@ Ember.addBeforeObserver = function(obj, path, target, method) {
3759
3824
  return this;
3760
3825
  };
3761
3826
 
3827
+ // Suspend observer during callback.
3828
+ //
3829
+ // This should only be used by the target of the observer
3830
+ // while it is setting the observed path.
3831
+ /** @private */
3832
+ Ember._suspendObserver = function(obj, path, target, method, callback) {
3833
+ return Ember._suspendListener(obj, changeEvent(path), target, method, callback);
3834
+ };
3835
+
3762
3836
  /** @private */
3763
3837
  Ember.beforeObserversFor = function(obj, path) {
3764
3838
  return Ember.listenersFor(obj, beforeEvent(path));
@@ -3773,16 +3847,19 @@ Ember.removeBeforeObserver = function(obj, path, target, method) {
3773
3847
 
3774
3848
  /** @private */
3775
3849
  Ember.notifyObservers = function(obj, keyName) {
3850
+ if (obj.isDestroying) { return; }
3851
+
3776
3852
  notifyObservers(obj, changeEvent(keyName));
3777
3853
  };
3778
3854
 
3779
3855
  /** @private */
3780
3856
  Ember.notifyBeforeObservers = function(obj, keyName) {
3857
+ if (obj.isDestroying) { return; }
3858
+
3781
3859
  var guid, set, forceNotification = false;
3782
3860
 
3783
- if (suspended) {
3784
- if (!beforeObserverSet.contains(obj, keyName)) {
3785
- beforeObserverSet.add(obj, keyName);
3861
+ if (deferred) {
3862
+ if (beforeObserverSet.add(obj, keyName)) {
3786
3863
  forceNotification = true;
3787
3864
  } else {
3788
3865
  return;
@@ -3857,6 +3934,8 @@ var WILL_SEEN, DID_SEEN;
3857
3934
  // called whenever a property is about to change to clear the cache of any dependent keys (and notify those properties of changes, etc...)
3858
3935
  /** @private */
3859
3936
  function dependentKeysWillChange(obj, depKey, meta) {
3937
+ if (obj.isDestroying) { return; }
3938
+
3860
3939
  var seen = WILL_SEEN, top = !seen;
3861
3940
  if (top) seen = WILL_SEEN = {};
3862
3941
  iterDeps(propertyWillChange, obj, depKey, seen, meta);
@@ -3866,6 +3945,8 @@ function dependentKeysWillChange(obj, depKey, meta) {
3866
3945
  // called whenever a property has just changed to update dependent keys
3867
3946
  /** @private */
3868
3947
  function dependentKeysDidChange(obj, depKey, meta) {
3948
+ if (obj.isDestroying) { return; }
3949
+
3869
3950
  var seen = DID_SEEN, top = !seen;
3870
3951
  if (top) seen = DID_SEEN = {};
3871
3952
  iterDeps(propertyDidChange, obj, depKey, seen, meta);
@@ -4413,7 +4494,7 @@ Ember.destroy = function (obj) {
4413
4494
  var o_create = Ember.platform.create;
4414
4495
  var meta = Ember.meta;
4415
4496
  var guidFor = Ember.guidFor;
4416
- var array_Slice = Array.prototype.slice;
4497
+ var a_slice = Array.prototype.slice;
4417
4498
 
4418
4499
  /**
4419
4500
  The event system uses a series of nested hashes to store listeners on an
@@ -4464,49 +4545,52 @@ function targetSetFor(obj, eventName) {
4464
4545
  // meta system.
4465
4546
  var SKIP_PROPERTIES = { __ember_source__: true };
4466
4547
 
4467
- // For a given target, invokes all of the methods that have
4468
- // been registered as a listener.
4469
4548
  /** @private */
4470
- function invokeEvents(targetSet, params) {
4549
+ function iterateSet(targetSet, callback, params) {
4550
+ if (!targetSet) { return false; }
4471
4551
  // Iterate through all elements of the target set
4472
4552
  for(var targetGuid in targetSet) {
4473
4553
  if (SKIP_PROPERTIES[targetGuid]) { continue; }
4474
4554
 
4475
4555
  var actionSet = targetSet[targetGuid];
4476
-
4477
- // Iterate through the elements of the action set
4478
- for(var methodGuid in actionSet) {
4479
- if (SKIP_PROPERTIES[methodGuid]) { continue; }
4480
-
4481
- var action = actionSet[methodGuid];
4482
- if (!action) { continue; }
4483
-
4484
- // Extract target and method for each action
4485
- var method = action.method;
4486
- var target = action.target;
4487
-
4488
- // If there is no target, the target is the object
4489
- // on which the event was fired.
4490
- if (!target) { target = params[0]; }
4491
- if ('string' === typeof method) { method = target[method]; }
4492
-
4493
- // Listeners can provide an `xform` function, which can perform
4494
- // arbitrary transformations, such as changing the order of
4495
- // parameters.
4496
- //
4497
- // This is primarily used by ember-runtime's observer system, which
4498
- // provides a higher level abstraction on top of events, including
4499
- // dynamically looking up current values and passing them into the
4500
- // registered listener.
4501
- var xform = action.xform;
4502
-
4503
- if (xform) {
4504
- xform(target, method, params);
4505
- } else {
4506
- method.apply(target, params);
4556
+ if (actionSet) {
4557
+ // Iterate through the elements of the action set
4558
+ for(var methodGuid in actionSet) {
4559
+ if (SKIP_PROPERTIES[methodGuid]) { continue; }
4560
+
4561
+ var action = actionSet[methodGuid];
4562
+ if (action) {
4563
+ if (callback(action, params) === true) {
4564
+ return true;
4565
+ }
4566
+ }
4507
4567
  }
4508
4568
  }
4509
4569
  }
4570
+ return false;
4571
+ }
4572
+
4573
+ /** @private */
4574
+ function invokeAction(action, params) {
4575
+ var method = action.method, target = action.target, xform = action.xform;
4576
+ // If there is no target, the target is the object
4577
+ // on which the event was fired.
4578
+ if (!target) { target = params[0]; }
4579
+ if ('string' === typeof method) { method = target[method]; }
4580
+
4581
+ // Listeners can provide an `xform` function, which can perform
4582
+ // arbitrary transformations, such as changing the order of
4583
+ // parameters.
4584
+ //
4585
+ // This is primarily used by ember-runtime's observer system, which
4586
+ // provides a higher level abstraction on top of events, including
4587
+ // dynamically looking up current values and passing them into the
4588
+ // registered listener.
4589
+ if (xform) {
4590
+ xform(target, method, params);
4591
+ } else {
4592
+ method.apply(target, params);
4593
+ }
4510
4594
  }
4511
4595
 
4512
4596
  /**
@@ -4560,6 +4644,31 @@ function removeListener(obj, eventName, target, method) {
4560
4644
  }
4561
4645
  }
4562
4646
 
4647
+ // Suspend listener during callback.
4648
+ //
4649
+ // This should only be used by the target of the event listener
4650
+ // when it is taking an action that would cause the event, e.g.
4651
+ // an object might suspend its property change listener while it is
4652
+ // setting that property.
4653
+ /** @private */
4654
+ function suspendListener(obj, eventName, target, method, callback) {
4655
+ if (!method && 'function' === typeof target) {
4656
+ method = target;
4657
+ target = null;
4658
+ }
4659
+
4660
+ var actionSet = actionSetFor(obj, eventName, target, true),
4661
+ methodGuid = guidFor(method),
4662
+ action = actionSet && actionSet[methodGuid];
4663
+
4664
+ actionSet[methodGuid] = null;
4665
+ try {
4666
+ return callback.call(target);
4667
+ } finally {
4668
+ actionSet[methodGuid] = action;
4669
+ }
4670
+ }
4671
+
4563
4672
  // returns a list of currently watched events
4564
4673
  /** @memberOf Ember */
4565
4674
  function watchedEvents(obj) {
@@ -4580,30 +4689,37 @@ function sendEvent(obj, eventName) {
4580
4689
 
4581
4690
  // first give object a chance to handle it
4582
4691
  if (obj !== Ember && 'function' === typeof obj.sendEvent) {
4583
- obj.sendEvent.apply(obj, array_Slice.call(arguments, 1));
4692
+ obj.sendEvent.apply(obj, a_slice.call(arguments, 1));
4584
4693
  }
4585
4694
 
4586
4695
  var targetSet = targetSetFor(obj, eventName);
4587
- if (!targetSet) { return false; }
4696
+ iterateSet(targetSet, invokeAction, arguments);
4588
4697
 
4589
- invokeEvents(targetSet, arguments);
4590
4698
  return true;
4591
4699
  }
4592
4700
 
4593
- /** @memberOf Ember */
4594
- function hasListeners(obj, eventName) {
4595
- var targetSet = targetSetFor(obj, eventName);
4596
- if (!targetSet) { return false; }
4597
-
4598
- for(var targetGuid in targetSet) {
4599
- if (SKIP_PROPERTIES[targetGuid] || !targetSet[targetGuid]) { continue; }
4701
+ function deferEvent(obj, eventName) {
4702
+ var targetSet = targetSetFor(obj, eventName), actions = [], params = arguments;
4703
+ iterateSet(targetSet, function (action) {
4704
+ actions.push(action);
4705
+ });
4600
4706
 
4601
- var actionSet = targetSet[targetGuid];
4707
+ return function() {
4708
+ if (obj !== Ember && 'function' === typeof obj.sendEvent) {
4709
+ obj.sendEvent.apply(obj, a_slice.call(params, 1));
4710
+ }
4602
4711
 
4603
- for(var methodGuid in actionSet) {
4604
- if (SKIP_PROPERTIES[methodGuid] || !actionSet[methodGuid]) { continue; }
4605
- return true; // stop as soon as we find a valid listener
4712
+ for (var i=0, len=actions.length; i < len; ++i) {
4713
+ invokeAction(actions[i], params);
4606
4714
  }
4715
+ };
4716
+ }
4717
+
4718
+ /** @memberOf Ember */
4719
+ function hasListeners(obj, eventName) {
4720
+ var targetSet = targetSetFor(obj, eventName);
4721
+ if (iterateSet(targetSet, function () {return true;})) {
4722
+ return true;
4607
4723
  }
4608
4724
 
4609
4725
  // no listeners! might as well clean this up so it is faster later.
@@ -4616,31 +4732,20 @@ function hasListeners(obj, eventName) {
4616
4732
  /** @memberOf Ember */
4617
4733
  function listenersFor(obj, eventName) {
4618
4734
  var targetSet = targetSetFor(obj, eventName), ret = [];
4619
- if (!targetSet) { return ret; }
4620
-
4621
- var info;
4622
- for(var targetGuid in targetSet) {
4623
- if (SKIP_PROPERTIES[targetGuid] || !targetSet[targetGuid]) { continue; }
4624
-
4625
- var actionSet = targetSet[targetGuid];
4626
-
4627
- for(var methodGuid in actionSet) {
4628
- if (SKIP_PROPERTIES[methodGuid] || !actionSet[methodGuid]) { continue; }
4629
- info = actionSet[methodGuid];
4630
- ret.push([info.target, info.method]);
4631
- }
4632
- }
4633
-
4735
+ iterateSet(targetSet, function (action) {
4736
+ ret.push([action.target, action.method]);
4737
+ });
4634
4738
  return ret;
4635
4739
  }
4636
4740
 
4637
4741
  Ember.addListener = addListener;
4638
4742
  Ember.removeListener = removeListener;
4743
+ Ember._suspendListener = suspendListener;
4639
4744
  Ember.sendEvent = sendEvent;
4640
4745
  Ember.hasListeners = hasListeners;
4641
4746
  Ember.watchedEvents = watchedEvents;
4642
4747
  Ember.listenersFor = listenersFor;
4643
-
4748
+ Ember.deferEvent = deferEvent;
4644
4749
  })({});
4645
4750
 
4646
4751
  (function(exports) {
@@ -4808,6 +4913,12 @@ function applyMixin(obj, mixins, partial) {
4808
4913
 
4809
4914
  var mixinBindings = Ember._mixinBindings;
4810
4915
 
4916
+ // Go through all mixins and hashes passed in, and:
4917
+ //
4918
+ // * Handle concatenated properties
4919
+ // * Set up _super wrapping if necessary
4920
+ // * Set up descriptors (simple, watched or computed properties)
4921
+ // * Copying `toString` in broken browsers
4811
4922
  mergeMixins(mixins, meta(obj), descs, values, obj);
4812
4923
 
4813
4924
  if (MixinDelegate.detect(obj)) {
@@ -5314,17 +5425,24 @@ RunLoop.prototype = {
5314
5425
  while (this._queues && (queue = this._queues[queueName])) {
5315
5426
  this._queues[queueName] = null;
5316
5427
 
5317
- log = Ember.LOG_BINDINGS && queueName==='sync';
5318
- if (log) Ember.Logger.log('Begin: Flush Sync Queue');
5319
-
5320
5428
  // the sync phase is to allow property changes to propogate. don't
5321
5429
  // invoke observers until that is finished.
5322
- if (queueName === 'sync') Ember.beginPropertyChanges();
5323
- forEach(queue, iter);
5324
- if (queueName === 'sync') Ember.endPropertyChanges();
5430
+ if (queueName === 'sync') {
5431
+ log = Ember.LOG_BINDINGS;
5432
+ if (log) Ember.Logger.log('Begin: Flush Sync Queue');
5325
5433
 
5326
- if (log) Ember.Logger.log('End: Flush Sync Queue');
5434
+ Ember.beginPropertyChanges();
5435
+ try {
5436
+ forEach(queue, iter);
5437
+ } finally {
5438
+ Ember.endPropertyChanges();
5439
+ }
5327
5440
 
5441
+ if (log) Ember.Logger.log('End: Flush Sync Queue');
5442
+
5443
+ } else {
5444
+ forEach(queue, iter);
5445
+ }
5328
5446
  }
5329
5447
 
5330
5448
  } else {
@@ -5336,17 +5454,26 @@ RunLoop.prototype = {
5336
5454
  queueName = queueNames[idx];
5337
5455
  queue = queues[queueName];
5338
5456
 
5339
- log = Ember.LOG_BINDINGS && queueName==='sync';
5340
- if (log) Ember.Logger.log('Begin: Flush Sync Queue');
5341
-
5342
- if (queueName === 'sync') Ember.beginPropertyChanges();
5343
- if (queue) forEach(queue, iter);
5344
- if (queueName === 'sync') Ember.endPropertyChanges();
5345
-
5346
- if (log) Ember.Logger.log('End: Flush Sync Queue');
5457
+ if (queue) {
5458
+ // the sync phase is to allow property changes to propogate. don't
5459
+ // invoke observers until that is finished.
5460
+ if (queueName === 'sync') {
5461
+ log = Ember.LOG_BINDINGS;
5462
+ if (log) Ember.Logger.log('Begin: Flush Sync Queue');
5463
+
5464
+ Ember.beginPropertyChanges();
5465
+ try {
5466
+ forEach(queue, iter);
5467
+ } finally {
5468
+ Ember.endPropertyChanges();
5469
+ }
5347
5470
 
5471
+ if (log) Ember.Logger.log('End: Flush Sync Queue');
5472
+ } else {
5473
+ forEach(queue, iter);
5474
+ }
5475
+ }
5348
5476
  }
5349
-
5350
5477
  } while (queues = this._queues); // go until queues stay clean
5351
5478
  }
5352
5479
 
@@ -5891,19 +6018,21 @@ function getPathWithGlobals(obj, path) {
5891
6018
  }
5892
6019
 
5893
6020
  /** @private */
5894
- function getFromValue(obj, binding) {
5895
- var operation = binding._operation;
5896
-
6021
+ function getTransformedFromValue(obj, binding) {
6022
+ var operation = binding._operation,
6023
+ fromValue;
5897
6024
  if (operation) {
5898
- return operation(obj, binding._from, binding._operand);
6025
+ fromValue = operation(obj, binding._from, binding._operand);
5899
6026
  } else {
5900
- return getPathWithGlobals(obj, binding._from);
6027
+ fromValue = getPathWithGlobals(obj, binding._from);
5901
6028
  }
6029
+ return getTransformedValue(binding, fromValue, obj, 'to');
5902
6030
  }
5903
6031
 
5904
6032
  /** @private */
5905
- function getToValue(obj, binding) {
5906
- return getPath(obj, binding._to);
6033
+ function getTransformedToValue(obj, binding) {
6034
+ var toValue = getPath(obj, binding._to);
6035
+ return getTransformedValue(binding, toValue, obj, 'from');
5907
6036
  }
5908
6037
 
5909
6038
  /** @private */
@@ -5919,7 +6048,6 @@ var OR_OPERATION = function(obj, left, right) {
5919
6048
  // ..........................................................
5920
6049
  // BINDING
5921
6050
  //
5922
-
5923
6051
  /** @private */
5924
6052
  var K = function() {};
5925
6053
 
@@ -5940,9 +6068,6 @@ var Binding = function(toPath, fromPath) {
5940
6068
  self._from = fromPath;
5941
6069
  self._to = toPath;
5942
6070
 
5943
- /** @private */
5944
- self._cache = {};
5945
-
5946
6071
  return self;
5947
6072
  };
5948
6073
 
@@ -6275,55 +6400,32 @@ Binding.prototype = /** @scope Ember.Binding.prototype */ {
6275
6400
  // synchronizing from
6276
6401
  var guid = guidFor(obj), direction = this[guid];
6277
6402
 
6278
- var fromPath = this._from, toPath = this._to, lastSet;
6403
+ var fromPath = this._from, toPath = this._to;
6279
6404
 
6280
6405
  delete this[guid];
6281
6406
 
6282
- if (direction === 'fwd') {
6283
- lastSet = this._cache.back;
6284
- } else if (direction === 'back') {
6285
- lastSet = this._cache.fwd;
6286
- }
6287
-
6288
- var fromValue, toValue;
6289
-
6290
- // There's a bit of duplicate logic here, but the order is important.
6291
- //
6292
- // We want to avoid ping-pong bindings. To do this, we store off the
6293
- // guid of the item we are setting. Later, we avoid synchronizing
6294
- // bindings in the other direction if the raw value we are copying
6295
- // is the same as the guid of the last thing we set.
6296
- //
6297
- // Use guids here to avoid unnecessarily holding hard references
6298
- // to objects.
6299
- if (direction === 'fwd') {
6300
- fromValue = getFromValue(obj, this);
6301
- if (this._cache.back === guidFor(fromValue)) { return; }
6302
- this._cache.fwd = guidFor(fromValue);
6303
-
6304
- toValue = getToValue(obj, this);
6305
- } else if (direction === 'back') {
6306
- toValue = getToValue(obj, this);
6307
- if (this._cache.fwd === guidFor(toValue)) { return; }
6308
- this._cache.back = guidFor(toValue);
6309
-
6310
- fromValue = getFromValue(obj, this);
6311
- }
6312
-
6313
- fromValue = getTransformedValue(this, fromValue, obj, 'to');
6314
- toValue = getTransformedValue(this, toValue, obj, 'from');
6315
-
6316
- if (toValue === fromValue) { return; }
6317
-
6318
6407
  // if we're synchronizing from the remote object...
6319
6408
  if (direction === 'fwd') {
6320
- if (log) { Ember.Logger.log(' ', this.toString(), toValue, '->', fromValue, obj); }
6321
- Ember.trySetPath(Ember.isGlobalPath(toPath) ? window : obj, toPath, fromValue);
6322
-
6409
+ var fromValue = getTransformedFromValue(obj, this);
6410
+ if (log) {
6411
+ Ember.Logger.log(' ', this.toString(), '->', fromValue, obj);
6412
+ }
6413
+ if (this._oneWay) {
6414
+ Ember.trySetPath(Ember.isGlobalPath(toPath) ? window : obj, toPath, fromValue);
6415
+ } else {
6416
+ Ember._suspendObserver(obj, toPath, this, this.toDidChange, function () {
6417
+ Ember.trySetPath(Ember.isGlobalPath(toPath) ? window : obj, toPath, fromValue);
6418
+ });
6419
+ }
6323
6420
  // if we're synchronizing *to* the remote object
6324
6421
  } else if (direction === 'back') {// && !this._oneWay) {
6325
- if (log) { Ember.Logger.log(' ', this.toString(), toValue, '<-', fromValue, obj); }
6326
- Ember.trySetPath(Ember.isGlobalPath(fromPath) ? window : obj, fromPath, toValue);
6422
+ var toValue = getTransformedToValue(obj, this);
6423
+ if (log) {
6424
+ Ember.Logger.log(' ', this.toString(), '<-', toValue, obj);
6425
+ }
6426
+ Ember._suspendObserver(obj, fromPath, this, this.fromDidChange, function () {
6427
+ Ember.trySetPath(Ember.isGlobalPath(fromPath) ? window : obj, fromPath, toValue);
6428
+ });
6327
6429
  }
6328
6430
  }
6329
6431
 
@@ -6971,9 +7073,9 @@ Ember.inspect = function(obj) {
6971
7073
 
6972
7074
  /**
6973
7075
  Compares two objects, returning true if they are logically equal. This is
6974
- a deeper comparison than a simple triple equal. For arrays and enumerables
6975
- it will compare the internal objects. For any other object that implements
6976
- `isEqual()` it will respect that method.
7076
+ a deeper comparison than a simple triple equal. For sets it will compare the
7077
+ internal objects. For any other object that implements `isEqual()` it will
7078
+ respect that method.
6977
7079
 
6978
7080
  @param {Object} a first object to compare
6979
7081
  @param {Object} b second object to compare
@@ -7321,16 +7423,95 @@ var a_slice = Array.prototype.slice;
7321
7423
 
7322
7424
  if (Ember.EXTEND_PROTOTYPES) {
7323
7425
 
7426
+ /**
7427
+ The `property` extension of Javascript's Function prototype is available
7428
+ when Ember.EXTEND_PROTOTYPES is true, which is the default.
7429
+
7430
+ Computed properties allow you to treat a function like a property:
7431
+
7432
+ MyApp.president = Ember.Object.create({
7433
+ firstName: "Barack",
7434
+ lastName: "Obama",
7435
+
7436
+ fullName: function() {
7437
+ return this.get('firstName') + ' ' + this.get('lastName');
7438
+
7439
+ // Call this flag to mark the function as a property
7440
+ }.property()
7441
+ });
7442
+
7443
+ MyApp.president.get('fullName'); => "Barack Obama"
7444
+
7445
+ Treating a function like a property is useful because they can work with
7446
+ bindings, just like any other property.
7447
+
7448
+ Many computed properties have dependencies on other properties. For
7449
+ example, in the above example, the `fullName` property depends on
7450
+ `firstName` and `lastName` to determine its value. You can tell Ember.js
7451
+ about these dependencies like this:
7452
+
7453
+ MyApp.president = Ember.Object.create({
7454
+ firstName: "Barack",
7455
+ lastName: "Obama",
7456
+
7457
+ fullName: function() {
7458
+ return this.get('firstName') + ' ' + this.get('lastName');
7459
+
7460
+ // Tell Ember.js that this computed property depends on firstName
7461
+ // and lastName
7462
+ }.property('firstName', 'lastName')
7463
+ });
7464
+
7465
+ Make sure you list these dependencies so Ember.js knows when to update
7466
+ bindings that connect to a computed property.
7467
+
7468
+ Note: you will usually want to use `property(...)` with `cacheable()`.
7469
+
7470
+ @see Ember.ComputedProperty
7471
+ @see Ember.computed
7472
+ */
7324
7473
  Function.prototype.property = function() {
7325
7474
  var ret = Ember.computed(this);
7326
7475
  return ret.property.apply(ret, arguments);
7327
7476
  };
7328
7477
 
7478
+ /**
7479
+ The `observes` extension of Javascript's Function prototype is available
7480
+ when Ember.EXTEND_PROTOTYPES is true, which is the default.
7481
+
7482
+ You can observe property changes simply by adding the `observes`
7483
+ call to the end of your method declarations in classes that you write.
7484
+ For example:
7485
+
7486
+ Ember.Object.create({
7487
+ valueObserver: function() {
7488
+ // Executes whenever the "value" property changes
7489
+ }.observes('value')
7490
+ });
7491
+
7492
+ @see Ember.Observable
7493
+ */
7329
7494
  Function.prototype.observes = function() {
7330
7495
  this.__ember_observes__ = a_slice.call(arguments);
7331
7496
  return this;
7332
7497
  };
7333
7498
 
7499
+ /**
7500
+ The `observesBefore` extension of Javascript's Function prototype is
7501
+ available when Ember.EXTEND_PROTOTYPES is true, which is the default.
7502
+
7503
+ You can get notified when a property changes is about to happen by
7504
+ by adding the `observesBefore` call to the end of your method
7505
+ declarations in classes that you write. For example:
7506
+
7507
+ Ember.Object.create({
7508
+ valueObserver: function() {
7509
+ // Executes whenever the "value" property is about to change
7510
+ }.observesBefore('value')
7511
+ });
7512
+
7513
+ @see Ember.Observable
7514
+ */
7334
7515
  Function.prototype.observesBefore = function() {
7335
7516
  this.__ember_observesBefore__ = a_slice.call(arguments);
7336
7517
  return this;
@@ -9265,13 +9446,7 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
9265
9446
  @returns {Ember.Observable}
9266
9447
  */
9267
9448
  setProperties: function(hash) {
9268
- var self = this;
9269
- Ember.changeProperties(function(){
9270
- for(var prop in hash) {
9271
- if (hash.hasOwnProperty(prop)) set(self, prop, hash[prop]);
9272
- }
9273
- });
9274
- return this;
9449
+ return Ember.setProperties(this, hash);
9275
9450
  },
9276
9451
 
9277
9452
  /**
@@ -9280,9 +9455,9 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
9280
9455
  You can use this method to group property changes so that notifications
9281
9456
  will not be sent until the changes are finished. If you plan to make a
9282
9457
  large number of changes to an object at one time, you should call this
9283
- method at the beginning of the changes to suspend change notifications.
9284
- When you are done making changes, call endPropertyChanges() to allow
9285
- notification to resume.
9458
+ method at the beginning of the changes to begin deferring change
9459
+ notifications. When you are done making changes, call endPropertyChanges()
9460
+ to deliver the deferred change notifications and end deferring.
9286
9461
 
9287
9462
  @returns {Ember.Observable}
9288
9463
  */
@@ -9297,9 +9472,9 @@ Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
9297
9472
  You can use this method to group property changes so that notifications
9298
9473
  will not be sent until the changes are finished. If you plan to make a
9299
9474
  large number of changes to an object at one time, you should call
9300
- beginPropertyChanges() at the beginning of the changes to suspend change
9301
- notifications. When you are done making changes, call this method to allow
9302
- notification to resume.
9475
+ beginPropertyChanges() at the beginning of the changes to defer change
9476
+ notifications. When you are done making changes, call this method to
9477
+ deliver the deferred change notifications and end deferring.
9303
9478
 
9304
9479
  @returns {Ember.Observable}
9305
9480
  */
@@ -9614,11 +9789,11 @@ Ember.TargetActionSupport = Ember.Mixin.create({
9614
9789
  })({});
9615
9790
 
9616
9791
  (function(exports) {
9617
- var get = Ember.get, set = Ember.set;
9792
+ var get = Ember.get, set = Ember.set, a_slice = Array.prototype.slice;
9618
9793
 
9619
9794
  /** @private */
9620
9795
  function xform(target, method, params) {
9621
- var args = [].slice.call(params, 2);
9796
+ var args = a_slice.call(params, 2);
9622
9797
  method.apply(target, args);
9623
9798
  }
9624
9799
 
@@ -9633,7 +9808,7 @@ Ember.Evented = Ember.Mixin.create({
9633
9808
  },
9634
9809
 
9635
9810
  fire: function(name) {
9636
- Ember.sendEvent.apply(null, [this, name].concat([].slice.call(arguments, 1)));
9811
+ Ember.sendEvent.apply(null, [this, name].concat(a_slice.call(arguments, 1)));
9637
9812
  },
9638
9813
 
9639
9814
  off: function(name, target, method) {
@@ -9671,6 +9846,7 @@ var classToString = Ember.Mixin.prototype.toString;
9671
9846
  var set = Ember.set, get = Ember.get;
9672
9847
  var o_create = Ember.platform.create,
9673
9848
  o_defineProperty = Ember.platform.defineProperty,
9849
+ a_slice = Array.prototype.slice,
9674
9850
  meta = Ember.meta;
9675
9851
 
9676
9852
  /** @private */
@@ -9680,14 +9856,23 @@ function makeCtor() {
9680
9856
  // method a lot faster. This is glue code so we want it to be as fast as
9681
9857
  // possible.
9682
9858
 
9683
- var isPrepared = false, initMixins, init = false, hasChains = false;
9859
+ var wasApplied = false, initMixins, defaults, init = false, hasChains = false;
9684
9860
 
9685
9861
  var Class = function() {
9686
- if (!isPrepared) { get(Class, 'proto'); } // prepare prototype...
9862
+ if (defaults) {
9863
+ for (var prop in defaults) {
9864
+ if (!defaults.hasOwnProperty(prop)) { continue; }
9865
+ Ember.defineProperty(this, prop, undefined, defaults[prop]);
9866
+ }
9867
+
9868
+ defaults = null;
9869
+ }
9870
+
9871
+ if (!wasApplied) { Class.proto(); } // prepare prototype...
9687
9872
  if (initMixins) {
9688
9873
  this.reopen.apply(this, initMixins);
9689
9874
  initMixins = null;
9690
- rewatch(this); // ålways rewatch just in case
9875
+ rewatch(this); // always rewatch just in case
9691
9876
  this.init.apply(this, arguments);
9692
9877
  } else {
9693
9878
  if (hasChains) {
@@ -9704,20 +9889,29 @@ function makeCtor() {
9704
9889
  };
9705
9890
 
9706
9891
  Class.toString = classToString;
9707
- Class._prototypeMixinDidChange = function() {
9708
- ember_assert("Reopening already instantiated classes is not supported. We plan to support this in the future.", isPrepared === false);
9709
- isPrepared = false;
9892
+ Class.willReopen = function() {
9893
+ if (wasApplied) {
9894
+ Class.PrototypeMixin = Ember.Mixin.create(Class.PrototypeMixin);
9895
+ }
9896
+
9897
+ wasApplied = false;
9710
9898
  };
9711
9899
  Class._initMixins = function(args) { initMixins = args; };
9900
+ Class._setDefaults = function(arg) { defaults = arg; };
9712
9901
 
9713
- Ember.defineProperty(Class, 'proto', Ember.computed(function() {
9714
- if (!isPrepared) {
9715
- isPrepared = true;
9902
+ Class.proto = function() {
9903
+ var superclass = Class.superclass;
9904
+ if (superclass) { superclass.proto(); }
9905
+
9906
+ if (!wasApplied) {
9907
+ wasApplied = true;
9716
9908
  Class.PrototypeMixin.applyPartial(Class.prototype);
9909
+ Ember.rewatch(Class.prototype); // setup watch chains if needed.
9717
9910
  hasChains = !!meta(Class.prototype, false).chains; // avoid rewatch
9718
9911
  }
9912
+
9719
9913
  return this.prototype;
9720
- }));
9914
+ };
9721
9915
 
9722
9916
  return Class;
9723
9917
 
@@ -9738,6 +9932,7 @@ CoreObject.PrototypeMixin = Ember.Mixin.create(
9738
9932
  init: function() {},
9739
9933
 
9740
9934
  isDestroyed: false,
9935
+ isDestroying: false,
9741
9936
 
9742
9937
  /**
9743
9938
  Destroys an object by setting the isDestroyed flag and removing its
@@ -9752,6 +9947,12 @@ CoreObject.PrototypeMixin = Ember.Mixin.create(
9752
9947
  @returns {Ember.Object} receiver
9753
9948
  */
9754
9949
  destroy: function() {
9950
+ if (this.isDestroying) { return; }
9951
+
9952
+ this.isDestroying = true;
9953
+
9954
+ if (this.willDestroy) { this.willDestroy(); }
9955
+
9755
9956
  set(this, 'isDestroyed', true);
9756
9957
  Ember.run.schedule('destroy', this, this._scheduledDestroy);
9757
9958
  return this;
@@ -9765,6 +9966,7 @@ CoreObject.PrototypeMixin = Ember.Mixin.create(
9765
9966
  */
9766
9967
  _scheduledDestroy: function() {
9767
9968
  Ember.destroy(this);
9969
+ if (this.didDestroy) { this.didDestroy(); }
9768
9970
  },
9769
9971
 
9770
9972
  bind: function(to, from) {
@@ -9808,7 +10010,6 @@ var ClassMixin = Ember.Mixin.create({
9808
10010
  proto.constructor = Class;
9809
10011
  Ember.generateGuid(proto, 'ember');
9810
10012
  meta(proto).proto = proto; // this will disable observers on prototype
9811
- Ember.rewatch(proto); // setup watch chains if needed.
9812
10013
 
9813
10014
 
9814
10015
  Class.subclasses = Ember.Set ? new Ember.Set() : null;
@@ -9824,10 +10025,34 @@ var ClassMixin = Ember.Mixin.create({
9824
10025
  return new C();
9825
10026
  },
9826
10027
 
10028
+ /**
10029
+ @private
10030
+
10031
+ Right now, when a key is passed in `create` that is not already
10032
+ present in the superclass, we need to create a mixin object and
10033
+ apply the mixin to the object we're creating. This is
10034
+ unnecessarily expensive. Because Ember views are created a lot,
10035
+ this is a temporary convenience that will allow us to create
10036
+ a new object and set properties before `init` time.
10037
+
10038
+ The correct solution is for the default init code to detect
10039
+ properties that do not need special handling and call
10040
+ `setProperties` on them when `create` occurs. This will
10041
+ massively speed up `create` calls that do not need any special
10042
+ Ember features (like bindings, observers or computed properties)
10043
+ and are not overriding a computed property with a regular value.
10044
+ */
10045
+ createWith: function(defaults) {
10046
+ var C = this;
10047
+ if (arguments.length>0) { this._initMixins(a_slice.call(arguments, 1)); }
10048
+ if (defaults) { this._setDefaults(defaults); }
10049
+ return new C();
10050
+ },
10051
+
9827
10052
  reopen: function() {
10053
+ this.willReopen();
9828
10054
  var PrototypeMixin = this.PrototypeMixin;
9829
10055
  PrototypeMixin.reopen.apply(PrototypeMixin, arguments);
9830
- this._prototypeMixinDidChange();
9831
10056
  return this;
9832
10057
  },
9833
10058
 
@@ -9848,7 +10073,7 @@ var ClassMixin = Ember.Mixin.create({
9848
10073
  },
9849
10074
 
9850
10075
  detectInstance: function(obj) {
9851
- return this.PrototypeMixin.detect(obj);
10076
+ return obj instanceof this;
9852
10077
  },
9853
10078
 
9854
10079
  /**
@@ -9872,7 +10097,7 @@ var ClassMixin = Ember.Mixin.create({
9872
10097
  This will return the original hash that was passed to `meta()`.
9873
10098
  */
9874
10099
  metaForProperty: function(key) {
9875
- var desc = meta(get(this, 'proto'), false).descs[key];
10100
+ var desc = meta(this.proto(), false).descs[key];
9876
10101
 
9877
10102
  ember_assert("metaForProperty() could not find a computed property with key '"+key+"'.", !!desc && desc instanceof Ember.ComputedProperty);
9878
10103
  return desc._meta || {};
@@ -9883,7 +10108,7 @@ var ClassMixin = Ember.Mixin.create({
9883
10108
  and any associated metadata (see `metaForProperty`) to the callback.
9884
10109
  */
9885
10110
  eachComputedProperty: function(callback, binding) {
9886
- var proto = get(this, 'proto'),
10111
+ var proto = this.proto(),
9887
10112
  descs = meta(proto).descs,
9888
10113
  empty = {},
9889
10114
  property;
@@ -11905,8 +12130,6 @@ Ember.Application = Ember.Namespace.extend(
11905
12130
  self.didBecomeReady();
11906
12131
  });
11907
12132
  }
11908
-
11909
- this._super();
11910
12133
  },
11911
12134
 
11912
12135
  /** @private */
@@ -11988,7 +12211,7 @@ var childViewsProperty = Ember.computed(function() {
11988
12211
  });
11989
12212
 
11990
12213
  return ret;
11991
- }).property('_childViews.@each').cacheable();
12214
+ }).property().cacheable();
11992
12215
 
11993
12216
  /**
11994
12217
  @static
@@ -12176,7 +12399,7 @@ Ember.View = Ember.Object.extend(Ember.Evented,
12176
12399
  */
12177
12400
  childViews: childViewsProperty,
12178
12401
 
12179
- _childViews: Ember.A(),
12402
+ _childViews: [],
12180
12403
 
12181
12404
  /**
12182
12405
  Return the nearest ancestor that is an instance of the provided
@@ -12261,6 +12484,8 @@ Ember.View = Ember.Object.extend(Ember.Evented,
12261
12484
  collectionView, itemView, and contentView
12262
12485
  */
12263
12486
  _parentViewDidChange: Ember.observer(function() {
12487
+ if (this.isDestroying) { return; }
12488
+
12264
12489
  this.invokeRecursively(function(view) {
12265
12490
  view.propertyDidChange('collectionView');
12266
12491
  view.propertyDidChange('itemView');
@@ -13111,15 +13336,13 @@ Ember.View = Ember.Object.extend(Ember.Evented,
13111
13336
  dispatch
13112
13337
  */
13113
13338
  init: function() {
13114
- var parentView = get(this, '_parentView');
13115
-
13116
13339
  this._super();
13117
13340
 
13118
13341
  // Register the view for event handling. This hash is used by
13119
13342
  // Ember.RootResponder to dispatch incoming events.
13120
13343
  Ember.View.views[get(this, 'elementId')] = this;
13121
13344
 
13122
- var childViews = Ember.A(get(this, '_childViews').slice());
13345
+ var childViews = get(this, '_childViews').slice();
13123
13346
 
13124
13347
  // setup child views. be sure to clone the child views array first
13125
13348
  set(this, '_childViews', childViews);
@@ -13150,12 +13373,19 @@ Ember.View = Ember.Object.extend(Ember.Evented,
13150
13373
  @returns {Ember.View} receiver
13151
13374
  */
13152
13375
  removeChild: function(view) {
13376
+ // If we're destroying, the entire subtree will be
13377
+ // freed, and the DOM will be handled separately,
13378
+ // so no need to mess with childViews.
13379
+ if (this.isDestroying) { return; }
13380
+
13153
13381
  // update parent node
13154
13382
  set(view, '_parentView', null);
13155
13383
 
13156
13384
  // remove view from childViews array.
13157
13385
  var childViews = get(this, '_childViews');
13158
- childViews.removeObject(view);
13386
+ Ember.ArrayUtils.removeObject(childViews, view);
13387
+
13388
+ this.propertyDidChange('childViews');
13159
13389
 
13160
13390
  return this;
13161
13391
  },
@@ -13194,14 +13424,12 @@ Ember.View = Ember.Object.extend(Ember.Evented,
13194
13424
  },
13195
13425
 
13196
13426
  /**
13197
- You must call this method on a view to destroy the view (and all of its
13427
+ You must call `destroy` on a view to destroy the view (and all of its
13198
13428
  child views). This will remove the view from any parent node, then make
13199
13429
  sure that the DOM element managed by the view can be released by the
13200
13430
  memory manager.
13201
13431
  */
13202
- destroy: function() {
13203
- if (get(this, 'isDestroyed')) { return; }
13204
-
13432
+ willDestroy: function() {
13205
13433
  // calling this._super() will nuke computed properties and observers,
13206
13434
  // so collect any information we need before calling super.
13207
13435
  var childViews = get(this, '_childViews'),
@@ -13226,9 +13454,7 @@ Ember.View = Ember.Object.extend(Ember.Evented,
13226
13454
  // the DOM again.
13227
13455
  if (parent) { parent.removeChild(this); }
13228
13456
 
13229
- Ember.Descriptor.setup(this, 'state', 'destroyed');
13230
-
13231
- this._super();
13457
+ this.state = 'destroyed';
13232
13458
 
13233
13459
  childLen = get(childViews, 'length');
13234
13460
  for (var i=childLen-1; i>=0; i--) {
@@ -13238,8 +13464,6 @@ Ember.View = Ember.Object.extend(Ember.Evented,
13238
13464
 
13239
13465
  // next remove view from global hash
13240
13466
  delete Ember.View.views[get(this, 'elementId')];
13241
-
13242
- return this; // done with cleanup
13243
13467
  },
13244
13468
 
13245
13469
  /**
@@ -13256,7 +13480,11 @@ Ember.View = Ember.Object.extend(Ember.Evented,
13256
13480
  */
13257
13481
  createChildView: function(view, attrs) {
13258
13482
  if (Ember.View.detect(view)) {
13259
- view = view.create(attrs || {}, { _parentView: this });
13483
+ if (attrs) {
13484
+ view = view.createWith({ _parentView: this }, attrs);
13485
+ } else {
13486
+ view = view.createWith({ _parentView: this });
13487
+ }
13260
13488
 
13261
13489
  var viewName = view.viewName;
13262
13490
 
@@ -13427,6 +13655,10 @@ var DOMManager = {
13427
13655
  set(view, 'element', null);
13428
13656
 
13429
13657
  Ember.$(elem).remove();
13658
+ },
13659
+
13660
+ empty: function(view) {
13661
+ view.$().empty();
13430
13662
  }
13431
13663
  };
13432
13664
 
@@ -13527,6 +13759,8 @@ Ember.View.states.preRender = {
13527
13759
  return Ember.$();
13528
13760
  },
13529
13761
 
13762
+ empty: Ember.K,
13763
+
13530
13764
  // This exists for the removal warning, remove later
13531
13765
  getElement: function(view){
13532
13766
  if (view._willInsertElementAccessUnsupported) {
@@ -13590,8 +13824,12 @@ Ember.View.states.inBuffer = {
13590
13824
  var buffer = view.buffer;
13591
13825
 
13592
13826
  childView = this.createChildView(childView, options);
13593
- get(view, '_childViews').pushObject(childView);
13827
+ get(view, '_childViews').push(childView);
13828
+
13594
13829
  childView.renderToBuffer(buffer);
13830
+
13831
+ view.propertyDidChange('childViews');
13832
+
13595
13833
  return childView;
13596
13834
  },
13597
13835
 
@@ -13606,6 +13844,10 @@ Ember.View.states.inBuffer = {
13606
13844
  return view;
13607
13845
  },
13608
13846
 
13847
+ empty: function() {
13848
+ throw "EWOT";
13849
+ },
13850
+
13609
13851
  // It should be impossible for a rendered view to be scheduled for
13610
13852
  // insertion.
13611
13853
  insertElement: function() {
@@ -13685,6 +13927,10 @@ Ember.View.states.hasElement = {
13685
13927
  return view;
13686
13928
  },
13687
13929
 
13930
+ empty: function(view) {
13931
+ view.domManager.empty(view);
13932
+ },
13933
+
13688
13934
  // Handle events from `Ember.EventDispatcher`
13689
13935
  handleEvent: function(view, eventName, evt) {
13690
13936
  var handler = view[eventName];
@@ -13727,6 +13973,9 @@ Ember.View.states.destroyed = {
13727
13973
  destroyElement: function() {
13728
13974
  throw fmt(destroyedError, ['destroyElement']);
13729
13975
  },
13976
+ empty: function() {
13977
+ throw fmt(destroyedError, ['empty']);
13978
+ },
13730
13979
 
13731
13980
  setElement: function() {
13732
13981
  throw fmt(destroyedError, ["set('element', ...)"]);
@@ -13788,6 +14037,9 @@ Ember.ContainerView = Ember.View.extend({
13788
14037
  _childViews[idx] = view;
13789
14038
  }, this);
13790
14039
 
14040
+ // Make the _childViews array observable
14041
+ Ember.A(_childViews);
14042
+
13791
14043
  // Sets up an array observer on the child views array. This
13792
14044
  // observer will detect when child views are added or removed
13793
14045
  // and update the DOM to reflect the mutation.
@@ -13815,7 +14067,7 @@ Ember.ContainerView = Ember.View.extend({
13815
14067
 
13816
14068
  @private
13817
14069
  */
13818
- destroy: function() {
14070
+ willDestroy: function() {
13819
14071
  get(this, 'childViews').removeArrayObserver(this, {
13820
14072
  willChange: 'childViewsWillChange',
13821
14073
  didChange: 'childViewsDidChange'
@@ -14037,13 +14289,11 @@ Ember.CollectionView = Ember.ContainerView.extend(
14037
14289
  this.arrayDidChange(content, 0, null, len);
14038
14290
  }, 'content'),
14039
14291
 
14040
- destroy: function() {
14292
+ willDestroy: function() {
14041
14293
  var content = get(this, 'content');
14042
14294
  if (content) { content.removeArrayObserver(this); }
14043
14295
 
14044
14296
  this._super();
14045
-
14046
- return this;
14047
14297
  },
14048
14298
 
14049
14299
  arrayWillChange: function(content, start, removedCount) {
@@ -14060,8 +14310,17 @@ Ember.CollectionView = Ember.ContainerView.extend(
14060
14310
  var childViews = get(this, 'childViews'), childView, idx, len;
14061
14311
 
14062
14312
  len = get(childViews, 'length');
14313
+
14314
+ var removingAll = removedCount === len;
14315
+
14316
+ if (removingAll) {
14317
+ this.invokeForState('empty');
14318
+ }
14319
+
14063
14320
  for (idx = start + removedCount - 1; idx >= start; idx--) {
14064
- childViews[idx].destroy();
14321
+ childView = childViews[idx];
14322
+ if (removingAll) { childView.removedFromDOM = true; }
14323
+ childView.destroy();
14065
14324
  }
14066
14325
  },
14067
14326
 
@@ -15145,6 +15404,10 @@ var DOMManager = {
15145
15404
  view.transitionTo('inDOM');
15146
15405
  view._notifyDidInsertElement();
15147
15406
  });
15407
+ },
15408
+
15409
+ empty: function(view) {
15410
+ view.morph.html("");
15148
15411
  }
15149
15412
  };
15150
15413
 
@@ -15369,7 +15632,6 @@ var get = Ember.get, getPath = Ember.Handlebars.getPath, set = Ember.set, fmt =
15369
15632
  var forEach = Ember.ArrayUtils.forEach;
15370
15633
 
15371
15634
  var EmberHandlebars = Ember.Handlebars, helpers = EmberHandlebars.helpers;
15372
- var helpers = EmberHandlebars.helpers;
15373
15635
 
15374
15636
  (function() {
15375
15637
  // Binds a property into the DOM. This will create a hook in DOM that the
@@ -15947,7 +16209,7 @@ Ember.Handlebars.registerHelper('collection', function(path, options) {
15947
16209
 
15948
16210
  // Extract item view class if provided else default to the standard class
15949
16211
  var itemViewClass, itemViewPath = hash.itemViewClass;
15950
- var collectionPrototype = get(collectionClass, 'proto');
16212
+ var collectionPrototype = collectionClass.proto();
15951
16213
  delete hash.itemViewClass;
15952
16214
  itemViewClass = itemViewPath ? getPath(collectionPrototype, itemViewPath) : collectionPrototype.itemViewClass;
15953
16215
  ember_assert(fmt("%@ #collection: Could not find %@", data.view, itemViewPath), !!itemViewClass);
@@ -15968,7 +16230,7 @@ Ember.Handlebars.registerHelper('collection', function(path, options) {
15968
16230
  }
15969
16231
  }
15970
16232
 
15971
- var tagName = hash.tagName || get(collectionClass, 'proto').tagName;
16233
+ var tagName = hash.tagName || collectionPrototype.tagName;
15972
16234
 
15973
16235
  if (fn) {
15974
16236
  itemHash.template = fn;
@@ -16641,6 +16903,9 @@ Ember.Handlebars.bootstrap = function(ctx) {
16641
16903
  var compile = (script.attr('type') === 'text/x-raw-handlebars') ?
16642
16904
  Ember.$.proxy(Handlebars.compile, Handlebars) :
16643
16905
  Ember.$.proxy(Ember.Handlebars.compile, Ember.Handlebars),
16906
+ // Get the id of the script, used by Ember.View's elementId property,
16907
+ // Look for data-element-id attribute.
16908
+ elementId = script.attr('data-element-id'),
16644
16909
  // Get the name of the script, used by Ember.View's templateName property.
16645
16910
  // First look for data-template-name attribute, then fall back to its
16646
16911
  // id if no name is found.
@@ -16676,6 +16941,7 @@ Ember.Handlebars.bootstrap = function(ctx) {
16676
16941
  tagName = script.attr('data-tag-name');
16677
16942
 
16678
16943
  view = view.create({
16944
+ elementId: elementId,
16679
16945
  template: template,
16680
16946
  tagName: (tagName) ? tagName : undefined
16681
16947
  });