ember-rails 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -10,7 +10,7 @@ if ('undefined' === typeof Ember) {
10
10
  /**
11
11
  @namespace
12
12
  @name Ember
13
- @version 0.9.7.1
13
+ @version 0.9.8.1
14
14
 
15
15
  All Ember methods and functions are defined inside of this namespace.
16
16
  You generally should not add new properties to this namespace as it may be
@@ -30,7 +30,7 @@ if ('undefined' === typeof Ember) {
30
30
 
31
31
  // Create core object. Make it act like an instance of Ember.Namespace so that
32
32
  // objects assigned to it are given a sane string representation.
33
- Ember = { isNamespace: true, toString: function() { return "Ember"; } };
33
+ Ember = {};
34
34
 
35
35
  // aliases needed to keep minifiers from removing the global context
36
36
  if ('undefined' !== typeof window) {
@@ -39,13 +39,20 @@ if ('undefined' !== typeof window) {
39
39
 
40
40
  }
41
41
 
42
+ // Make sure these are set whether Ember was already defined or not
43
+
44
+ Ember.isNamespace = true;
45
+
46
+ Ember.toString = function() { return "Ember"; };
47
+
48
+
42
49
  /**
43
50
  @static
44
51
  @type String
45
- @default '0.9.7.1'
52
+ @default '0.9.8.1'
46
53
  @constant
47
54
  */
48
- Ember.VERSION = '0.9.7.1';
55
+ Ember.VERSION = '0.9.8.1';
49
56
 
50
57
  /**
51
58
  @static
@@ -92,6 +99,56 @@ Ember.EXTEND_PROTOTYPES = (Ember.ENV.EXTEND_PROTOTYPES !== false);
92
99
  Ember.SHIM_ES5 = (Ember.ENV.SHIM_ES5 === false) ? false : Ember.EXTEND_PROTOTYPES;
93
100
 
94
101
 
102
+ /**
103
+ @static
104
+ @type Boolean
105
+ @default false
106
+ @constant
107
+
108
+ Determines whether computed properties are cacheable by default.
109
+ In future releases this will default to `true`. For the 1.0 release,
110
+ the option to turn off caching by default will be removed entirely.
111
+
112
+ When caching is enabled by default, you can use `volatile()` to disable
113
+ caching on individual computed properties.
114
+ */
115
+ Ember.CP_DEFAULT_CACHEABLE = !!Ember.ENV.CP_DEFAULT_CACHEABLE;
116
+
117
+ /**
118
+ @static
119
+ @type Boolean
120
+ @default false
121
+ @constant
122
+
123
+ Determines whether views render their templates using themselves
124
+ as the context, or whether it is inherited from the parent. In
125
+ future releases, this will default to `true`. For the 1.0 release,
126
+ the option to have views change context by default will be removed entirely.
127
+
128
+ If you need to update your application to use the new context rules, simply
129
+ prefix property access with `view.`:
130
+
131
+ // Before:
132
+ {{#each App.photosController}}
133
+ Photo Title: {{title}}
134
+ {{#view App.InfoView contentBinding="this"}}
135
+ {{content.date}}
136
+ {{content.cameraType}}
137
+ {{otherViewProperty}}
138
+ {{/view}}
139
+ {{/each}}
140
+
141
+ // After:
142
+ {{#each App.photosController}}
143
+ Photo Title: {{title}}
144
+ {{#view App.InfoView}}
145
+ {{date}}
146
+ {{cameraType}}
147
+ {{view.otherViewProperty}}
148
+ {{/view}}
149
+ {{/each}}
150
+ */
151
+ Ember.VIEW_PRESERVES_CONTEXT = !!Ember.ENV.VIEW_PRESERVES_CONTEXT;
95
152
 
96
153
  /**
97
154
  Empty function. Useful for some operations.
@@ -110,18 +167,24 @@ Ember.K = function() { return this; };
110
167
 
111
168
  // Stub out the methods defined by the ember-debug package in case it's not loaded
112
169
 
113
- if ('undefined' === typeof ember_assert) {
114
- window.ember_assert = Ember.K;
170
+ if ('undefined' === typeof Ember.assert) { Ember.assert = Ember.K; }
171
+ if ('undefined' === typeof Ember.warn) { Ember.warn = Ember.K; }
172
+ if ('undefined' === typeof Ember.deprecate) { Ember.deprecate = Ember.K; }
173
+ if ('undefined' === typeof Ember.deprecateFunc) {
174
+ Ember.deprecateFunc = function(_, func) { return func; };
115
175
  }
116
176
 
117
- if ('undefined' === typeof ember_warn) { window.ember_warn = Ember.K; }
177
+ // These are deprecated but still supported
118
178
 
179
+ if ('undefined' === typeof ember_assert) { window.ember_assert = Ember.K; }
180
+ if ('undefined' === typeof ember_warn) { window.ember_warn = Ember.K; }
119
181
  if ('undefined' === typeof ember_deprecate) { window.ember_deprecate = Ember.K; }
120
-
121
182
  if ('undefined' === typeof ember_deprecateFunc) {
183
+ /** @private */
122
184
  window.ember_deprecateFunc = function(_, func) { return func; };
123
185
  }
124
186
 
187
+
125
188
  // ..........................................................
126
189
  // LOGGER
127
190
  //
@@ -655,7 +718,6 @@ Ember.makeArray = function(obj) {
655
718
  // Copyright: ©2011 Strobe Inc. and contributors.
656
719
  // License: Licensed under MIT license (see license.js)
657
720
  // ==========================================================================
658
- /*globals ember_assert */
659
721
  var USE_ACCESSORS = Ember.platform.hasPropertyAccessors && Ember.ENV.USE_ACCESSORS;
660
722
  Ember.USE_ACCESSORS = !!USE_ACCESSORS;
661
723
 
@@ -793,14 +855,24 @@ Ember.set = set;
793
855
  // PATHS
794
856
  //
795
857
 
858
+ /** @private */
859
+ function cleanupStars(path) {
860
+ if (path.indexOf('*') === -1 || path === '*') return path;
861
+
862
+
863
+ return path.replace(/(^|.)\*/, function(match, char){
864
+ return (char === '.') ? match : (char + '.');
865
+ });
866
+ }
867
+
796
868
  /** @private */
797
869
  function normalizePath(path) {
798
870
 
871
+ path = cleanupStars(path);
799
872
 
800
873
  if (path==='*') return path; //special case...
801
874
  var first = path.charAt(0);
802
875
  if(first==='.') return 'this'+path;
803
- if (first==='*' && path.charAt(1)!=='.') return 'this.'+path.slice(1);
804
876
  return path;
805
877
  }
806
878
 
@@ -809,10 +881,7 @@ function normalizePath(path) {
809
881
  function getPath(target, path) {
810
882
  var len = path.length, idx, next, key;
811
883
 
812
- idx = path.indexOf('*');
813
- if (idx>0 && path.charAt(idx-1)!=='.') {
814
- return getPath(getPath(target, path.slice(0, idx)), path.slice(idx+1));
815
- }
884
+ path = cleanupStars(path);
816
885
 
817
886
  idx = 0;
818
887
  while(target && idx<len) {
@@ -849,19 +918,9 @@ function normalizeTuple(target, path) {
849
918
  if (!target || isGlobal) target = window;
850
919
  if (hasThis) path = path.slice(5);
851
920
 
852
- var idx = path.indexOf('*');
853
- if (idx>0 && path.charAt(idx-1)!=='.') {
854
-
855
- // should not do lookup on a prototype object because the object isn't
856
- // really live yet.
857
- if (target && meta(target,false).proto!==target) {
858
- target = getPath(target, path.slice(0, idx));
859
- } else {
860
- target = null;
861
- }
862
- path = path.slice(idx+1);
921
+ path = cleanupStars(path);
863
922
 
864
- } else if (target === window) {
923
+ if (target === window) {
865
924
  key = firstKey(path);
866
925
  target = get(target, key);
867
926
  path = path.slice(key.length+1);
@@ -915,8 +974,8 @@ Ember.getWithDefault = function(root, key, defaultValue) {
915
974
  return value;
916
975
  };
917
976
 
918
- Ember.getPath = function(root, path, _checkGlobal) {
919
- var pathOnly, hasThis, hasStar, isGlobal, ret;
977
+ Ember.getPath = function(root, path) {
978
+ var hasThis, isGlobal, ret;
920
979
 
921
980
  // Helpers that operate with 'this' within an #each
922
981
  if (path === '') {
@@ -926,37 +985,27 @@ Ember.getPath = function(root, path, _checkGlobal) {
926
985
  if (!path && 'string'===typeof root) {
927
986
  path = root;
928
987
  root = null;
929
- pathOnly = true;
930
988
  }
931
989
 
932
- hasStar = path.indexOf('*') > -1;
990
+ path = cleanupStars(path);
933
991
 
934
992
  // If there is no root and path is a key name, return that
935
993
  // property from the global object.
936
994
  // E.g. getPath('Ember') -> Ember
937
- if (root === null && !hasStar && path.indexOf('.') < 0) { return get(window, path); }
995
+ if (root === null && path.indexOf('.') < 0) { return get(window, path); }
938
996
 
939
997
  // detect complicated paths and normalize them
940
998
  path = normalizePath(path);
941
999
  hasThis = HAS_THIS.test(path);
942
1000
 
943
- if (!root || hasThis || hasStar) {
944
-
945
-
1001
+ if (!root || hasThis) {
946
1002
  var tuple = normalizeTuple(root, path);
947
1003
  root = tuple[0];
948
1004
  path = tuple[1];
949
1005
  tuple.length = 0;
950
1006
  }
951
1007
 
952
- ret = getPath(root, path);
953
-
954
- if (ret === undefined && !pathOnly && !hasThis && root !== window && IS_GLOBAL.test(path) && _checkGlobal !== false) {
955
-
956
- return Ember.getPath(window, path);
957
- } else {
958
- return ret;
959
- }
1008
+ return getPath(root, path);
960
1009
  };
961
1010
 
962
1011
  Ember.setPath = function(root, path, value, tolerant) {
@@ -969,25 +1018,12 @@ Ember.setPath = function(root, path, value, tolerant) {
969
1018
  }
970
1019
 
971
1020
  path = normalizePath(path);
972
- if (path.indexOf('*')>0) {
973
-
974
-
975
- var tuple = normalizeTuple(root, path);
976
- root = tuple[0];
977
- path = tuple[1];
978
- tuple.length = 0;
979
- }
980
1021
 
981
1022
  if (path.indexOf('.') > 0) {
982
1023
  keyName = path.slice(path.lastIndexOf('.')+1);
983
1024
  path = path.slice(0, path.length-(keyName.length+1));
984
1025
  if (path !== 'this') {
985
- // Remove the `false` when we're done with this deprecation
986
- root = Ember.getPath(root, path, false);
987
- if (!root && IS_GLOBAL.test(path)) {
988
-
989
- root = Ember.getPath(window, path);
990
- }
1026
+ root = Ember.getPath(root, path);
991
1027
  }
992
1028
 
993
1029
  } else {
@@ -1045,7 +1081,6 @@ Ember.isGlobalPath = function(path) {
1045
1081
  // Copyright: ©2011 Strobe Inc. and contributors.
1046
1082
  // License: Licensed under MIT license (see license.js)
1047
1083
  // ==========================================================================
1048
- /*globals ember_assert */
1049
1084
  var USE_ACCESSORS = Ember.USE_ACCESSORS;
1050
1085
  var GUID_KEY = Ember.GUID_KEY;
1051
1086
  var META_KEY = Ember.META_KEY;
@@ -1458,7 +1493,9 @@ Ember.createPrototype = function(obj, props) {
1458
1493
  // Copyright: ©2011 Strobe Inc. and contributors.
1459
1494
  // License: Licensed under MIT license (see license.js)
1460
1495
  // ==========================================================================
1461
- /*globals ember_assert */
1496
+
1497
+
1498
+
1462
1499
  var meta = Ember.meta;
1463
1500
  var guidFor = Ember.guidFor;
1464
1501
  var USE_ACCESSORS = Ember.USE_ACCESSORS;
@@ -1529,7 +1566,7 @@ function addDependentKeys(desc, obj, keyName) {
1529
1566
  /** @private */
1530
1567
  function ComputedProperty(func, opts) {
1531
1568
  this.func = func;
1532
- this._cacheable = opts && opts.cacheable;
1569
+ this._cacheable = (opts && opts.cacheable !== undefined) ? opts.cacheable : Ember.CP_DEFAULT_CACHEABLE;
1533
1570
  this._dependentKeys = opts && opts.dependentKeys;
1534
1571
  }
1535
1572
 
@@ -1614,11 +1651,10 @@ var Cp = ComputedProperty.prototype;
1614
1651
  }.property('firstName', 'lastName').cacheable()
1615
1652
  });
1616
1653
 
1617
- It is common to use `cacheable()` on nearly every computed property
1618
- you define.
1654
+ Properties are cacheable by default.
1619
1655
 
1620
1656
  @name Ember.ComputedProperty.cacheable
1621
- @param {Boolean} aFlag optional set to false to disable cacheing
1657
+ @param {Boolean} aFlag optional set to false to disable caching
1622
1658
  @returns {Ember.ComputedProperty} receiver
1623
1659
  */
1624
1660
  Cp.cacheable = function(aFlag) {
@@ -1626,6 +1662,23 @@ Cp.cacheable = function(aFlag) {
1626
1662
  return this;
1627
1663
  };
1628
1664
 
1665
+ /**
1666
+ Call on a computed property to set it into non-cached mode. When in this
1667
+ mode the computed property will not automatically cache the return value.
1668
+
1669
+ MyApp.outsideService = Ember.Object.create({
1670
+ value: function() {
1671
+ return OutsideService.getValue();
1672
+ }.property().volatile()
1673
+ });
1674
+
1675
+ @name Ember.ComputedProperty.volatile
1676
+ @returns {Ember.ComputedProperty} receiver
1677
+ */
1678
+ Cp.volatile = function() {
1679
+ return this.cacheable(false);
1680
+ };
1681
+
1629
1682
  /**
1630
1683
  Sets the dependent keys on this computed property. Pass any number of
1631
1684
  arguments containing key paths that this computed property depends on.
@@ -1806,7 +1859,7 @@ Ember.computed = function(func) {
1806
1859
  Ember.cacheFor = function(obj, key) {
1807
1860
  var cache = meta(obj, false).cache;
1808
1861
 
1809
- if (cache && cache[key]) {
1862
+ if (cache && key in cache) {
1810
1863
  return cache[key];
1811
1864
  }
1812
1865
  };
@@ -2203,7 +2256,6 @@ Ember.notifyBeforeObservers = function(obj, keyName) {
2203
2256
  // Copyright: ©2011 Strobe Inc. and contributors.
2204
2257
  // License: Licensed under MIT license (see license.js)
2205
2258
  // ==========================================================================
2206
- /*globals ember_assert */
2207
2259
  var guidFor = Ember.guidFor;
2208
2260
  var meta = Ember.meta;
2209
2261
  var get = Ember.get, set = Ember.set;
@@ -2312,16 +2364,14 @@ var pendingQueue = [];
2312
2364
  // back in the queue and reschedule is true, schedules a timeout to try
2313
2365
  // again.
2314
2366
  /** @private */
2315
- function flushPendingChains(reschedule) {
2367
+ function flushPendingChains() {
2316
2368
  if (pendingQueue.length===0) return ; // nothing to do
2317
2369
 
2318
2370
  var queue = pendingQueue;
2319
2371
  pendingQueue = [];
2320
2372
 
2321
2373
  forEach(queue, function(q) { q[0].add(q[1]); });
2322
- if (reschedule!==false && pendingQueue.length>0) {
2323
- setTimeout(flushPendingChains, 1);
2324
- }
2374
+
2325
2375
  }
2326
2376
 
2327
2377
  /** @private */
@@ -2691,14 +2741,6 @@ Ember.rewatch = function(obj) {
2691
2741
  // make sure any chained watchers update.
2692
2742
  if (chains && chains.value() !== obj) chainsFor(obj);
2693
2743
 
2694
- // if the object has bindings then sync them..
2695
- if (bindings && m.proto!==obj) {
2696
- for (key in bindings) {
2697
- b = !DEP_SKIP[key] && obj[key];
2698
- if (b && b instanceof Ember.Binding) b.fromDidChange(obj);
2699
- }
2700
- }
2701
-
2702
2744
  return this;
2703
2745
  };
2704
2746
 
@@ -2817,7 +2859,6 @@ Ember.destroy = function (obj) {
2817
2859
  // Copyright: ©2011 Strobe Inc. and contributors.
2818
2860
  // License: Licensed under MIT license (see license.js)
2819
2861
  // ==========================================================================
2820
- /*globals ember_assert */
2821
2862
  var o_create = Ember.platform.create;
2822
2863
  var meta = Ember.meta;
2823
2864
  var guidFor = Ember.guidFor;
@@ -2937,7 +2978,7 @@ function addListener(obj, eventName, target, method, xform) {
2937
2978
  }
2938
2979
 
2939
2980
  var actionSet = actionSetFor(obj, eventName, target, true),
2940
- methodGuid = guidFor(method), ret;
2981
+ methodGuid = guidFor(method);
2941
2982
 
2942
2983
  if (!actionSet[methodGuid]) {
2943
2984
  actionSet[methodGuid] = { target: target, method: method, xform: xform };
@@ -2948,8 +2989,6 @@ function addListener(obj, eventName, target, method, xform) {
2948
2989
  if ('function' === typeof obj.didAddListener) {
2949
2990
  obj.didAddListener(eventName, target, method);
2950
2991
  }
2951
-
2952
- return ret; // return true if this is the first listener.
2953
2992
  }
2954
2993
 
2955
2994
  /** @memberOf Ember */
@@ -3081,562 +3120,597 @@ Ember.deferEvent = deferEvent;
3081
3120
  (function() {
3082
3121
  // ==========================================================================
3083
3122
  // Project: Ember Runtime
3084
- // Copyright: ©2011 Strobe Inc. and contributors.
3123
+ // Copyright: ©2006-2011 Strobe Inc. and contributors.
3124
+ // Portions ©2008-2010 Apple Inc. All rights reserved.
3085
3125
  // License: Licensed under MIT license (see license.js)
3086
3126
  // ==========================================================================
3087
- var Mixin, MixinDelegate, REQUIRED, Alias;
3088
- var classToString, superClassString;
3127
+ // Ember.Logger
3128
+ // Ember.watch.flushPending
3129
+ // Ember.beginPropertyChanges, Ember.endPropertyChanges
3130
+ // Ember.guidFor
3131
+ // Ember.ArrayUtils
3089
3132
 
3090
- var a_map = Ember.ArrayUtils.map;
3091
- var a_indexOf = Ember.ArrayUtils.indexOf;
3092
- var a_forEach = Ember.ArrayUtils.forEach;
3093
- var a_slice = Array.prototype.slice;
3094
- var EMPTY_META = {}; // dummy for non-writable meta
3095
- var META_SKIP = { __emberproto__: true, __ember_count__: true };
3133
+ // ..........................................................
3134
+ // HELPERS
3135
+ //
3096
3136
 
3097
- var o_create = Ember.platform.create;
3137
+ var slice = Array.prototype.slice;
3138
+ var forEach = Ember.ArrayUtils.forEach;
3098
3139
 
3140
+ // invokes passed params - normalizing so you can pass target/func,
3141
+ // target/string or just func
3099
3142
  /** @private */
3100
- function meta(obj, writable) {
3101
- var m = Ember.meta(obj, writable!==false), ret = m.mixins;
3102
- if (writable===false) return ret || EMPTY_META;
3143
+ function invoke(target, method, args, ignore) {
3103
3144
 
3104
- if (!ret) {
3105
- ret = m.mixins = { __emberproto__: obj };
3106
- } else if (ret.__emberproto__ !== obj) {
3107
- ret = m.mixins = o_create(ret);
3108
- ret.__emberproto__ = obj;
3145
+ if (method===undefined) {
3146
+ method = target;
3147
+ target = undefined;
3109
3148
  }
3110
- return ret;
3111
- }
3112
3149
 
3113
- /** @private */
3114
- function initMixin(mixin, args) {
3115
- if (args && args.length > 0) {
3116
- mixin.mixins = a_map(args, function(x) {
3117
- if (x instanceof Mixin) return x;
3150
+ if ('string'===typeof method) method = target[method];
3151
+ if (args && ignore>0) {
3152
+ args = args.length>ignore ? slice.call(args, ignore) : null;
3153
+ }
3118
3154
 
3119
- // Note: Manually setup a primitive mixin here. This is the only
3120
- // way to actually get a primitive mixin. This way normal creation
3121
- // of mixins will give you combined mixins...
3122
- var mixin = new Mixin();
3123
- mixin.properties = x;
3124
- return mixin;
3125
- });
3155
+ // Unfortunately in some browsers we lose the backtrace if we rethrow the existing error,
3156
+ // so in the event that we don't have an `onerror` handler we don't wrap in a try/catch
3157
+ if ('function' === typeof Ember.onerror) {
3158
+ try {
3159
+ // IE8's Function.prototype.apply doesn't accept undefined/null arguments.
3160
+ return method.apply(target || this, args || []);
3161
+ } catch (error) {
3162
+ Ember.onerror(error);
3163
+ }
3164
+ } else {
3165
+ // IE8's Function.prototype.apply doesn't accept undefined/null arguments.
3166
+ return method.apply(target || this, args || []);
3126
3167
  }
3127
- return mixin;
3128
3168
  }
3129
3169
 
3130
- var NATIVES = [Boolean, Object, Number, Array, Date, String];
3170
+
3171
+ // ..........................................................
3172
+ // RUNLOOP
3173
+ //
3174
+
3175
+ var timerMark; // used by timers...
3176
+
3131
3177
  /** @private */
3132
- function isMethod(obj) {
3133
- if ('function' !== typeof obj || obj.isMethod===false) return false;
3134
- return a_indexOf(NATIVES, obj)<0;
3135
- }
3178
+ var K = function() {};
3136
3179
 
3137
3180
  /** @private */
3138
- function mergeMixins(mixins, m, descs, values, base) {
3139
- var len = mixins.length, idx, mixin, guid, props, value, key, ovalue, concats;
3181
+ var RunLoop = function(prev) {
3182
+ var self;
3140
3183
 
3141
- /** @private */
3142
- function removeKeys(keyName) {
3143
- delete descs[keyName];
3144
- delete values[keyName];
3184
+ if (this instanceof RunLoop) {
3185
+ self = this;
3186
+ } else {
3187
+ self = new K();
3145
3188
  }
3146
3189
 
3147
- for(idx=0;idx<len;idx++) {
3190
+ self._prev = prev || null;
3191
+ self.onceTimers = {};
3148
3192
 
3149
- mixin = mixins[idx];
3150
- if (!mixin) throw new Error('Null value found in Ember.mixin()');
3193
+ return self;
3194
+ };
3151
3195
 
3152
- if (mixin instanceof Mixin) {
3153
- guid = Ember.guidFor(mixin);
3154
- if (m[guid]) continue;
3155
- m[guid] = mixin;
3156
- props = mixin.properties;
3157
- } else {
3158
- props = mixin; // apply anonymous mixin properties
3159
- }
3196
+ K.prototype = RunLoop.prototype;
3160
3197
 
3161
- if (props) {
3198
+ RunLoop.prototype = {
3199
+ end: function() {
3200
+ this.flush();
3201
+ },
3162
3202
 
3163
- // reset before adding each new mixin to pickup concats from previous
3164
- concats = values.concatenatedProperties || base.concatenatedProperties;
3165
- if (props.concatenatedProperties) {
3166
- concats = concats ? concats.concat(props.concatenatedProperties) : props.concatenatedProperties;
3167
- }
3203
+ prev: function() {
3204
+ return this._prev;
3205
+ },
3168
3206
 
3169
- for (key in props) {
3170
- if (!props.hasOwnProperty(key)) continue;
3171
- value = props[key];
3172
- if (value instanceof Ember.Descriptor) {
3173
- if (value === REQUIRED && descs[key]) { continue; }
3207
+ // ..........................................................
3208
+ // Delayed Actions
3209
+ //
3174
3210
 
3175
- descs[key] = value;
3176
- values[key] = undefined;
3177
- } else {
3211
+ schedule: function(queueName, target, method) {
3212
+ var queues = this._queues, queue;
3213
+ if (!queues) queues = this._queues = {};
3214
+ queue = queues[queueName];
3215
+ if (!queue) queue = queues[queueName] = [];
3178
3216
 
3179
- // impl super if needed...
3180
- if (isMethod(value)) {
3181
- ovalue = (descs[key] === Ember.SIMPLE_PROPERTY) && values[key];
3182
- if (!ovalue) ovalue = base[key];
3183
- if ('function' !== typeof ovalue) ovalue = null;
3184
- if (ovalue) {
3185
- var o = value.__ember_observes__, ob = value.__ember_observesBefore__;
3186
- value = Ember.wrap(value, ovalue);
3187
- value.__ember_observes__ = o;
3188
- value.__ember_observesBefore__ = ob;
3189
- }
3190
- } else if ((concats && a_indexOf(concats, key)>=0) || key === 'concatenatedProperties') {
3191
- var baseValue = values[key] || base[key];
3192
- value = baseValue ? baseValue.concat(value) : Ember.makeArray(value);
3193
- }
3217
+ var args = arguments.length>3 ? slice.call(arguments, 3) : null;
3218
+ queue.push({ target: target, method: method, args: args });
3219
+ return this;
3220
+ },
3194
3221
 
3195
- descs[key] = Ember.SIMPLE_PROPERTY;
3196
- values[key] = value;
3197
- }
3198
- }
3222
+ flush: function(queueName) {
3223
+ var queues = this._queues, queueNames, idx, len, queue, log;
3199
3224
 
3200
- // manually copy toString() because some JS engines do not enumerate it
3201
- if (props.hasOwnProperty('toString')) {
3202
- base.toString = props.toString;
3203
- }
3225
+ if (!queues) return this; // nothing to do
3204
3226
 
3205
- } else if (mixin.mixins) {
3206
- mergeMixins(mixin.mixins, m, descs, values, base);
3207
- if (mixin._without) a_forEach(mixin._without, removeKeys);
3227
+ function iter(item) {
3228
+ invoke(item.target, item.method, item.args);
3208
3229
  }
3209
- }
3210
- }
3211
3230
 
3212
- /** @private */
3213
- var defineProperty = Ember.defineProperty;
3231
+ Ember.watch.flushPending(); // make sure all chained watchers are setup
3214
3232
 
3215
- /** @private */
3216
- function writableReq(obj) {
3217
- var m = Ember.meta(obj), req = m.required;
3218
- if (!req || (req.__emberproto__ !== obj)) {
3219
- req = m.required = req ? o_create(req) : { __ember_count__: 0 };
3220
- req.__emberproto__ = obj;
3221
- }
3222
- return req;
3223
- }
3224
-
3225
- /** @private */
3226
- function getObserverPaths(value) {
3227
- return ('function' === typeof value) && value.__ember_observes__;
3228
- }
3229
-
3230
- /** @private */
3231
- function getBeforeObserverPaths(value) {
3232
- return ('function' === typeof value) && value.__ember_observesBefore__;
3233
- }
3234
-
3235
- Ember._mixinBindings = function(obj, key, value, m) {
3236
- return value;
3237
- };
3238
-
3239
- /** @private */
3240
- function applyMixin(obj, mixins, partial) {
3241
- var descs = {}, values = {}, m = Ember.meta(obj), req = m.required;
3242
- var key, willApply, didApply, value, desc;
3243
-
3244
- var mixinBindings = Ember._mixinBindings;
3245
-
3246
- // Go through all mixins and hashes passed in, and:
3247
- //
3248
- // * Handle concatenated properties
3249
- // * Set up _super wrapping if necessary
3250
- // * Set up descriptors (simple, watched or computed properties)
3251
- // * Copying `toString` in broken browsers
3252
- mergeMixins(mixins, meta(obj), descs, values, obj);
3253
-
3254
- if (MixinDelegate.detect(obj)) {
3255
- willApply = values.willApplyProperty || obj.willApplyProperty;
3256
- didApply = values.didApplyProperty || obj.didApplyProperty;
3257
- }
3233
+ if (queueName) {
3234
+ while (this._queues && (queue = this._queues[queueName])) {
3235
+ this._queues[queueName] = null;
3258
3236
 
3259
- for(key in descs) {
3260
- if (!descs.hasOwnProperty(key)) continue;
3237
+ // the sync phase is to allow property changes to propagate. don't
3238
+ // invoke observers until that is finished.
3239
+ if (queueName === 'sync') {
3240
+ log = Ember.LOG_BINDINGS;
3241
+ if (log) Ember.Logger.log('Begin: Flush Sync Queue');
3261
3242
 
3262
- desc = descs[key];
3263
- value = values[key];
3243
+ Ember.beginPropertyChanges();
3244
+ try {
3245
+ forEach(queue, iter);
3246
+ } finally {
3247
+ Ember.endPropertyChanges();
3248
+ }
3264
3249
 
3265
- if (desc === REQUIRED) {
3266
- if (!(key in obj)) {
3267
- if (!partial) throw new Error('Required property not defined: '+key);
3250
+ if (log) Ember.Logger.log('End: Flush Sync Queue');
3268
3251
 
3269
- // for partial applies add to hash of required keys
3270
- req = writableReq(obj);
3271
- req.__ember_count__++;
3272
- req[key] = true;
3252
+ } else {
3253
+ forEach(queue, iter);
3254
+ }
3273
3255
  }
3274
3256
 
3275
3257
  } else {
3258
+ queueNames = Ember.run.queues;
3259
+ len = queueNames.length;
3260
+ do {
3261
+ this._queues = null;
3262
+ for(idx=0;idx<len;idx++) {
3263
+ queueName = queueNames[idx];
3264
+ queue = queues[queueName];
3276
3265
 
3277
- while (desc instanceof Alias) {
3266
+ if (queue) {
3267
+ // the sync phase is to allow property changes to propagate. don't
3268
+ // invoke observers until that is finished.
3269
+ if (queueName === 'sync') {
3270
+ log = Ember.LOG_BINDINGS;
3271
+ if (log) Ember.Logger.log('Begin: Flush Sync Queue');
3278
3272
 
3279
- var altKey = desc.methodName;
3280
- if (descs[altKey]) {
3281
- value = values[altKey];
3282
- desc = descs[altKey];
3283
- } else if (m.descs[altKey]) {
3284
- desc = m.descs[altKey];
3285
- value = desc.val(obj, altKey);
3286
- } else {
3287
- value = obj[altKey];
3288
- desc = Ember.SIMPLE_PROPERTY;
3273
+ Ember.beginPropertyChanges();
3274
+ try {
3275
+ forEach(queue, iter);
3276
+ } finally {
3277
+ Ember.endPropertyChanges();
3278
+ }
3279
+
3280
+ if (log) Ember.Logger.log('End: Flush Sync Queue');
3281
+ } else {
3282
+ forEach(queue, iter);
3283
+ }
3284
+ }
3289
3285
  }
3290
- }
3286
+ } while (queues = this._queues); // go until queues stay clean
3287
+ }
3291
3288
 
3292
- if (willApply) willApply.call(obj, key);
3289
+ timerMark = null;
3293
3290
 
3294
- var observerPaths = getObserverPaths(value),
3295
- curObserverPaths = observerPaths && getObserverPaths(obj[key]),
3296
- beforeObserverPaths = getBeforeObserverPaths(value),
3297
- curBeforeObserverPaths = beforeObserverPaths && getBeforeObserverPaths(obj[key]),
3298
- len, idx;
3291
+ return this;
3292
+ }
3299
3293
 
3300
- if (curObserverPaths) {
3301
- len = curObserverPaths.length;
3302
- for(idx=0;idx<len;idx++) {
3303
- Ember.removeObserver(obj, curObserverPaths[idx], null, key);
3304
- }
3305
- }
3294
+ };
3306
3295
 
3307
- if (curBeforeObserverPaths) {
3308
- len = curBeforeObserverPaths.length;
3309
- for(idx=0;idx<len;idx++) {
3310
- Ember.removeBeforeObserver(obj, curBeforeObserverPaths[idx], null,key);
3311
- }
3312
- }
3296
+ Ember.RunLoop = RunLoop;
3297
+
3298
+ // ..........................................................
3299
+ // Ember.run - this is ideally the only public API the dev sees
3300
+ //
3301
+ /**
3302
+ * @namespace Ember.run is both a function and a namespace for
3303
+ * RunLoop-related functions.
3304
+ * @name Ember.run
3305
+ */
3313
3306
 
3314
- // TODO: less hacky way for ember-runtime to add bindings.
3315
- value = mixinBindings(obj, key, value, m);
3307
+ /**
3308
+ Runs the passed target and method inside of a RunLoop, ensuring any
3309
+ deferred actions including bindings and views updates are flushed at the
3310
+ end.
3316
3311
 
3317
- defineProperty(obj, key, desc, value);
3312
+ Normally you should not need to invoke this method yourself. However if
3313
+ you are implementing raw event handlers when interfacing with other
3314
+ libraries or plugins, you should probably wrap all of your code inside this
3315
+ call.
3318
3316
 
3319
- if (observerPaths) {
3320
- len = observerPaths.length;
3321
- for(idx=0;idx<len;idx++) {
3322
- Ember.addObserver(obj, observerPaths[idx], null, key);
3323
- }
3324
- }
3317
+ Ember.run(function(){
3318
+ // code to be execute within a RunLoop
3319
+ });
3325
3320
 
3326
- if (beforeObserverPaths) {
3327
- len = beforeObserverPaths.length;
3328
- for(idx=0;idx<len;idx++) {
3329
- Ember.addBeforeObserver(obj, beforeObserverPaths[idx], null, key);
3330
- }
3331
- }
3321
+ @name run^2
3322
+ @methodOf Ember.run
3323
+ @param {Object} target
3324
+ (Optional) target of method to call
3332
3325
 
3333
- if (req && req[key]) {
3334
- req = writableReq(obj);
3335
- req.__ember_count__--;
3336
- req[key] = false;
3337
- }
3326
+ @param {Function|String} method
3327
+ Method to invoke. May be a function or a string. If you pass a string
3328
+ then it will be looked up on the passed target.
3338
3329
 
3339
- if (didApply) didApply.call(obj, key);
3330
+ @param {Object...} args
3331
+ Any additional arguments you wish to pass to the method.
3340
3332
 
3341
- }
3342
- }
3333
+ @returns {Object} return value from invoking the passed function.
3334
+ */
3335
+ Ember.run = function(target, method) {
3343
3336
 
3344
- // Make sure no required attrs remain
3345
- if (!partial && req && req.__ember_count__>0) {
3346
- var keys = [];
3347
- for(key in req) {
3348
- if (META_SKIP[key]) continue;
3349
- keys.push(key);
3350
- }
3351
- throw new Error('Required properties not defined: '+keys.join(','));
3337
+ var ret, loop;
3338
+ run.begin();
3339
+ try {
3340
+ if (target || method) ret = invoke(target, method, arguments, 2);
3341
+ } finally {
3342
+ run.end();
3352
3343
  }
3353
- return obj;
3354
- }
3355
-
3356
- Ember.mixin = function(obj) {
3357
- var args = a_slice.call(arguments, 1);
3358
- return applyMixin(obj, args, false);
3344
+ return ret;
3359
3345
  };
3360
3346
 
3347
+ /** @private */
3348
+ var run = Ember.run;
3349
+
3361
3350
 
3362
3351
  /**
3363
- @constructor
3364
- */
3365
- Ember.Mixin = function() { return initMixin(this, arguments); };
3352
+ Begins a new RunLoop. Any deferred actions invoked after the begin will
3353
+ be buffered until you invoke a matching call to Ember.run.end(). This is
3354
+ an lower-level way to use a RunLoop instead of using Ember.run().
3366
3355
 
3367
- /** @private */
3368
- Mixin = Ember.Mixin;
3356
+ Ember.run.begin();
3357
+ // code to be execute within a RunLoop
3358
+ Ember.run.end();
3369
3359
 
3370
- /** @private */
3371
- Mixin._apply = applyMixin;
3372
3360
 
3373
- Mixin.applyPartial = function(obj) {
3374
- var args = a_slice.call(arguments, 1);
3375
- return applyMixin(obj, args, true);
3361
+ @returns {void}
3362
+ */
3363
+ Ember.run.begin = function() {
3364
+ run.currentRunLoop = new RunLoop(run.currentRunLoop);
3376
3365
  };
3377
3366
 
3378
- Mixin.create = function() {
3379
- classToString.processed = false;
3380
- var M = this;
3381
- return initMixin(new M(), arguments);
3382
- };
3367
+ /**
3368
+ Ends a RunLoop. This must be called sometime after you call Ember.run.begin()
3369
+ to flush any deferred actions. This is a lower-level way to use a RunLoop
3370
+ instead of using Ember.run().
3383
3371
 
3384
- Mixin.prototype.reopen = function() {
3372
+ Ember.run.begin();
3373
+ // code to be execute within a RunLoop
3374
+ Ember.run.end();
3385
3375
 
3386
- var mixin, tmp;
3376
+ @returns {void}
3377
+ */
3378
+ Ember.run.end = function() {
3387
3379
 
3388
- if (this.properties) {
3389
- mixin = Mixin.create();
3390
- mixin.properties = this.properties;
3391
- delete this.properties;
3392
- this.mixins = [mixin];
3380
+ try {
3381
+ run.currentRunLoop.end();
3393
3382
  }
3383
+ finally {
3384
+ run.currentRunLoop = run.currentRunLoop.prev();
3385
+ }
3386
+ };
3394
3387
 
3395
- var len = arguments.length, mixins = this.mixins, idx;
3396
-
3397
- for(idx=0;idx<len;idx++) {
3398
- mixin = arguments[idx];
3399
- if (mixin instanceof Mixin) {
3400
- mixins.push(mixin);
3401
- } else {
3402
- tmp = Mixin.create();
3403
- tmp.properties = mixin;
3404
- mixins.push(tmp);
3405
- }
3406
- }
3388
+ /**
3389
+ Array of named queues. This array determines the order in which queues
3390
+ are flushed at the end of the RunLoop. You can define your own queues by
3391
+ simply adding the queue name to this array. Normally you should not need
3392
+ to inspect or modify this property.
3407
3393
 
3408
- return this;
3409
- };
3394
+ @property {String}
3395
+ @default ['sync', 'actions', 'destroy', 'timers']
3396
+ */
3397
+ Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];
3410
3398
 
3411
- var TMP_ARRAY = [];
3412
- Mixin.prototype.apply = function(obj) {
3413
- TMP_ARRAY[0] = this;
3414
- var ret = applyMixin(obj, TMP_ARRAY, false);
3415
- TMP_ARRAY.length=0;
3416
- return ret;
3417
- };
3399
+ /**
3400
+ Adds the passed target/method and any optional arguments to the named
3401
+ queue to be executed at the end of the RunLoop. If you have not already
3402
+ started a RunLoop when calling this method one will be started for you
3403
+ automatically.
3418
3404
 
3419
- Mixin.prototype.applyPartial = function(obj) {
3420
- TMP_ARRAY[0] = this;
3421
- var ret = applyMixin(obj, TMP_ARRAY, true);
3422
- TMP_ARRAY.length=0;
3423
- return ret;
3424
- };
3405
+ At the end of a RunLoop, any methods scheduled in this way will be invoked.
3406
+ Methods will be invoked in an order matching the named queues defined in
3407
+ the run.queues property.
3425
3408
 
3426
- /** @private */
3427
- function _detect(curMixin, targetMixin, seen) {
3428
- var guid = Ember.guidFor(curMixin);
3409
+ Ember.run.schedule('timers', this, function(){
3410
+ // this will be executed at the end of the RunLoop, when timers are run
3411
+ console.log("scheduled on timers queue");
3412
+ });
3413
+ Ember.run.schedule('sync', this, function(){
3414
+ // this will be executed at the end of the RunLoop, when bindings are synced
3415
+ console.log("scheduled on sync queue");
3416
+ });
3417
+ // Note the functions will be run in order based on the run queues order. Output would be:
3418
+ // scheduled on sync queue
3419
+ // scheduled on timers queue
3429
3420
 
3430
- if (seen[guid]) return false;
3431
- seen[guid] = true;
3421
+ @param {String} queue
3422
+ The name of the queue to schedule against. Default queues are 'sync' and
3423
+ 'actions'
3432
3424
 
3433
- if (curMixin === targetMixin) return true;
3434
- var mixins = curMixin.mixins, loc = mixins ? mixins.length : 0;
3435
- while(--loc >= 0) {
3436
- if (_detect(mixins[loc], targetMixin, seen)) return true;
3437
- }
3438
- return false;
3439
- }
3425
+ @param {Object} target
3426
+ (Optional) target object to use as the context when invoking a method.
3440
3427
 
3441
- Mixin.prototype.detect = function(obj) {
3442
- if (!obj) return false;
3443
- if (obj instanceof Mixin) return _detect(obj, this, {});
3444
- return !!meta(obj, false)[Ember.guidFor(this)];
3445
- };
3428
+ @param {String|Function} method
3429
+ The method to invoke. If you pass a string it will be resolved on the
3430
+ target object at the time the scheduled item is invoked allowing you to
3431
+ change the target function.
3446
3432
 
3447
- Mixin.prototype.without = function() {
3448
- var ret = new Mixin(this);
3449
- ret._without = a_slice.call(arguments);
3450
- return ret;
3433
+ @param {Object} arguments...
3434
+ Optional arguments to be passed to the queued method.
3435
+
3436
+ @returns {void}
3437
+ */
3438
+ Ember.run.schedule = function(queue, target, method) {
3439
+ var loop = run.autorun();
3440
+ loop.schedule.apply(loop, arguments);
3451
3441
  };
3452
3442
 
3443
+ var autorunTimer;
3444
+
3453
3445
  /** @private */
3454
- function _keys(ret, mixin, seen) {
3455
- if (seen[Ember.guidFor(mixin)]) return;
3456
- seen[Ember.guidFor(mixin)] = true;
3446
+ function autorun() {
3447
+ autorunTimer = null;
3448
+ if (run.currentRunLoop) run.end();
3449
+ }
3457
3450
 
3458
- if (mixin.properties) {
3459
- var props = mixin.properties;
3460
- for(var key in props) {
3461
- if (props.hasOwnProperty(key)) ret[key] = true;
3451
+ /**
3452
+ Begins a new RunLoop if necessary and schedules a timer to flush the
3453
+ RunLoop at a later time. This method is used by parts of Ember to
3454
+ ensure the RunLoop always finishes. You normally do not need to call this
3455
+ method directly. Instead use Ember.run().
3456
+
3457
+ Ember.run.autorun();
3458
+
3459
+ @returns {Ember.RunLoop} the new current RunLoop
3460
+ */
3461
+ Ember.run.autorun = function() {
3462
+
3463
+ if (!run.currentRunLoop) {
3464
+ run.begin();
3465
+
3466
+ // TODO: throw during tests
3467
+ if (Ember.testing) {
3468
+ run.end();
3469
+ } else if (!autorunTimer) {
3470
+ autorunTimer = setTimeout(autorun, 1);
3462
3471
  }
3463
- } else if (mixin.mixins) {
3464
- a_forEach(mixin.mixins, function(x) { _keys(ret, x, seen); });
3465
3472
  }
3466
- }
3467
3473
 
3468
- Mixin.prototype.keys = function() {
3469
- var keys = {}, seen = {}, ret = [];
3470
- _keys(keys, this, seen);
3471
- for(var key in keys) {
3472
- if (keys.hasOwnProperty(key)) ret.push(key);
3473
- }
3474
- return ret;
3474
+ return run.currentRunLoop;
3475
3475
  };
3476
3476
 
3477
- /** @private - make Mixin's have nice displayNames */
3477
+ /**
3478
+ Immediately flushes any events scheduled in the 'sync' queue. Bindings
3479
+ use this queue so this method is a useful way to immediately force all
3480
+ bindings in the application to sync.
3478
3481
 
3479
- var NAME_KEY = Ember.GUID_KEY+'_name';
3480
- var get = Ember.get;
3482
+ You should call this method anytime you need any changed state to propagate
3483
+ throughout the app immediately without repainting the UI.
3481
3484
 
3482
- /** @private */
3483
- function processNames(paths, root, seen) {
3484
- var idx = paths.length;
3485
- for(var key in root) {
3486
- if (!root.hasOwnProperty || !root.hasOwnProperty(key)) continue;
3487
- var obj = root[key];
3488
- paths[idx] = key;
3485
+ Ember.run.sync();
3489
3486
 
3490
- if (obj && obj.toString === classToString) {
3491
- obj[NAME_KEY] = paths.join('.');
3492
- } else if (obj && get(obj, 'isNamespace')) {
3493
- if (seen[Ember.guidFor(obj)]) continue;
3494
- seen[Ember.guidFor(obj)] = true;
3495
- processNames(paths, obj, seen);
3496
- }
3487
+ @returns {void}
3488
+ */
3489
+ Ember.run.sync = function() {
3490
+ run.autorun();
3491
+ run.currentRunLoop.flush('sync');
3492
+ };
3493
+
3494
+ // ..........................................................
3495
+ // TIMERS
3496
+ //
3497
+
3498
+ var timers = {}; // active timers...
3497
3499
 
3500
+ var laterScheduled = false;
3501
+ /** @private */
3502
+ function invokeLaterTimers() {
3503
+ var now = (+ new Date()), earliest = -1;
3504
+ for(var key in timers) {
3505
+ if (!timers.hasOwnProperty(key)) continue;
3506
+ var timer = timers[key];
3507
+ if (timer && timer.expires) {
3508
+ if (now >= timer.expires) {
3509
+ delete timers[key];
3510
+ invoke(timer.target, timer.method, timer.args, 2);
3511
+ } else {
3512
+ if (earliest<0 || (timer.expires < earliest)) earliest=timer.expires;
3513
+ }
3514
+ }
3498
3515
  }
3499
- paths.length = idx; // cut out last item
3516
+
3517
+ // schedule next timeout to fire...
3518
+ if (earliest>0) setTimeout(invokeLaterTimers, earliest-(+ new Date()));
3500
3519
  }
3501
3520
 
3502
- /** @private */
3503
- function findNamespaces() {
3504
- var Namespace = Ember.Namespace, obj;
3521
+ /**
3522
+ Invokes the passed target/method and optional arguments after a specified
3523
+ period if time. The last parameter of this method must always be a number
3524
+ of milliseconds.
3505
3525
 
3506
- if (Namespace.PROCESSED) { return; }
3526
+ You should use this method whenever you need to run some action after a
3527
+ period of time instead of using setTimeout(). This method will ensure that
3528
+ items that expire during the same script execution cycle all execute
3529
+ together, which is often more efficient than using a real setTimeout.
3507
3530
 
3508
- for (var prop in window) {
3509
- // get(window.globalStorage, 'isNamespace') would try to read the storage for domain isNamespace and cause exception in Firefox.
3510
- // globalStorage is a storage obsoleted by the WhatWG storage specification. See https://developer.mozilla.org/en/DOM/Storage#globalStorage
3511
- if (prop === "globalStorage" && window.StorageList && window.globalStorage instanceof window.StorageList) { continue; }
3512
- // Unfortunately, some versions of IE don't support window.hasOwnProperty
3513
- if (window.hasOwnProperty && !window.hasOwnProperty(prop)) { continue; }
3531
+ Ember.run.later(myContext, function(){
3532
+ // code here will execute within a RunLoop in about 500ms with this == myContext
3533
+ }, 500);
3514
3534
 
3515
- try {
3516
- obj = window[prop];
3517
- } catch (e) {
3518
- continue;
3519
- }
3535
+ @param {Object} target
3536
+ (optional) target of method to invoke
3520
3537
 
3521
- if (obj && get(obj, 'isNamespace')) {
3538
+ @param {Function|String} method
3539
+ The method to invoke. If you pass a string it will be resolved on the
3540
+ target at the time the method is invoked.
3522
3541
 
3523
- obj[NAME_KEY] = prop;
3524
- }
3525
- }
3526
- }
3542
+ @param {Object...} args
3543
+ Optional arguments to pass to the timeout.
3527
3544
 
3528
- Ember.identifyNamespaces = findNamespaces;
3545
+ @param {Number} wait
3546
+ Number of milliseconds to wait.
3547
+
3548
+ @returns {Timer} an object you can use to cancel a timer at a later time.
3549
+ */
3550
+ Ember.run.later = function(target, method) {
3551
+ var args, expires, timer, guid, wait;
3552
+
3553
+ // setTimeout compatibility...
3554
+ if (arguments.length===2 && 'function' === typeof target) {
3555
+ wait = method;
3556
+ method = target;
3557
+ target = undefined;
3558
+ args = [target, method];
3529
3559
 
3530
- /** @private */
3531
- superClassString = function(mixin) {
3532
- var superclass = mixin.superclass;
3533
- if (superclass) {
3534
- if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; }
3535
- else { return superClassString(superclass); }
3536
3560
  } else {
3537
- return;
3561
+ args = slice.call(arguments);
3562
+ wait = args.pop();
3538
3563
  }
3564
+
3565
+ expires = (+ new Date())+wait;
3566
+ timer = { target: target, method: method, expires: expires, args: args };
3567
+ guid = Ember.guidFor(timer);
3568
+ timers[guid] = timer;
3569
+ run.once(timers, invokeLaterTimers);
3570
+ return guid;
3539
3571
  };
3540
3572
 
3541
3573
  /** @private */
3542
- classToString = function() {
3543
- var Namespace = Ember.Namespace, namespace;
3574
+ function invokeOnceTimer(guid, onceTimers) {
3575
+ if (onceTimers[this.tguid]) delete onceTimers[this.tguid][this.mguid];
3576
+ if (timers[guid]) invoke(this.target, this.method, this.args, 2);
3577
+ delete timers[guid];
3578
+ }
3544
3579
 
3545
- // TODO: Namespace should really be in Metal
3546
- if (Namespace) {
3547
- if (!this[NAME_KEY] && !classToString.processed) {
3548
- if (!Namespace.PROCESSED) {
3549
- findNamespaces();
3550
- Namespace.PROCESSED = true;
3551
- }
3580
+ /**
3581
+ Schedules an item to run one time during the current RunLoop. Calling
3582
+ this method with the same target/method combination will have no effect.
3552
3583
 
3553
- classToString.processed = true;
3584
+ Note that although you can pass optional arguments these will not be
3585
+ considered when looking for duplicates. New arguments will replace previous
3586
+ calls.
3554
3587
 
3555
- var namespaces = Namespace.NAMESPACES;
3556
- for (var i=0, l=namespaces.length; i<l; i++) {
3557
- namespace = namespaces[i];
3558
- processNames([namespace.toString()], namespace, {});
3559
- }
3560
- }
3561
- }
3588
+ Ember.run(function(){
3589
+ var doFoo = function() { foo(); }
3590
+ Ember.run.once(myContext, doFoo);
3591
+ Ember.run.once(myContext, doFoo);
3592
+ // doFoo will only be executed once at the end of the RunLoop
3593
+ });
3562
3594
 
3563
- if (this[NAME_KEY]) {
3564
- return this[NAME_KEY];
3565
- } else {
3566
- var str = superClassString(this);
3567
- if (str) {
3568
- return "(subclass of " + str + ")";
3569
- } else {
3570
- return "(unknown mixin)";
3571
- }
3572
- }
3573
- };
3595
+ @param {Object} target
3596
+ (optional) target of method to invoke
3574
3597
 
3575
- Mixin.prototype.toString = classToString;
3598
+ @param {Function|String} method
3599
+ The method to invoke. If you pass a string it will be resolved on the
3600
+ target at the time the method is invoked.
3576
3601
 
3577
- // returns the mixins currently applied to the specified object
3578
- // TODO: Make Ember.mixin
3579
- Mixin.mixins = function(obj) {
3580
- var ret = [], mixins = meta(obj, false), key, mixin;
3581
- for(key in mixins) {
3582
- if (META_SKIP[key]) continue;
3583
- mixin = mixins[key];
3602
+ @param {Object...} args
3603
+ Optional arguments to pass to the timeout.
3584
3604
 
3585
- // skip primitive mixins since these are always anonymous
3586
- if (!mixin.properties) ret.push(mixins[key]);
3587
- }
3588
- return ret;
3589
- };
3590
3605
 
3591
- REQUIRED = new Ember.Descriptor();
3592
- REQUIRED.toString = function() { return '(Required Property)'; };
3606
+ @returns {Object} timer
3607
+ */
3608
+ Ember.run.once = function(target, method) {
3609
+ var tguid = Ember.guidFor(target), mguid = Ember.guidFor(method), guid, timer;
3593
3610
 
3594
- Ember.required = function() {
3595
- return REQUIRED;
3611
+ var onceTimers = run.autorun().onceTimers;
3612
+ guid = onceTimers[tguid] && onceTimers[tguid][mguid];
3613
+ if (guid && timers[guid]) {
3614
+ timers[guid].args = slice.call(arguments); // replace args
3615
+
3616
+ } else {
3617
+ timer = {
3618
+ target: target,
3619
+ method: method,
3620
+ args: slice.call(arguments),
3621
+ tguid: tguid,
3622
+ mguid: mguid
3623
+ };
3624
+
3625
+ guid = Ember.guidFor(timer);
3626
+ timers[guid] = timer;
3627
+ if (!onceTimers[tguid]) onceTimers[tguid] = {};
3628
+ onceTimers[tguid][mguid] = guid; // so it isn't scheduled more than once
3629
+
3630
+ run.schedule('actions', timer, invokeOnceTimer, guid, onceTimers);
3631
+ }
3632
+
3633
+ return guid;
3596
3634
  };
3597
3635
 
3636
+ var scheduledNext = false;
3598
3637
  /** @private */
3599
- Alias = function(methodName) {
3600
- this.methodName = methodName;
3601
- };
3602
- Alias.prototype = new Ember.Descriptor();
3638
+ function invokeNextTimers() {
3639
+ scheduledNext = null;
3640
+ for(var key in timers) {
3641
+ if (!timers.hasOwnProperty(key)) continue;
3642
+ var timer = timers[key];
3643
+ if (timer.next) {
3644
+ delete timers[key];
3645
+ invoke(timer.target, timer.method, timer.args, 2);
3646
+ }
3647
+ }
3648
+ }
3603
3649
 
3604
- Ember.alias = function(methodName) {
3605
- return new Alias(methodName);
3606
- };
3650
+ /**
3651
+ Schedules an item to run after control has been returned to the system.
3652
+ This is often equivalent to calling setTimeout(function...,1).
3607
3653
 
3608
- Ember.MixinDelegate = Mixin.create({
3654
+ Ember.run.next(myContext, function(){
3655
+ // code to be executed in the next RunLoop, which will be scheduled after the current one
3656
+ });
3609
3657
 
3610
- willApplyProperty: Ember.required(),
3611
- didApplyProperty: Ember.required()
3658
+ @param {Object} target
3659
+ (optional) target of method to invoke
3612
3660
 
3613
- });
3661
+ @param {Function|String} method
3662
+ The method to invoke. If you pass a string it will be resolved on the
3663
+ target at the time the method is invoked.
3614
3664
 
3615
- /** @private */
3616
- MixinDelegate = Ember.MixinDelegate;
3665
+ @param {Object...} args
3666
+ Optional arguments to pass to the timeout.
3617
3667
 
3668
+ @returns {Object} timer
3669
+ */
3670
+ Ember.run.next = function(target, method) {
3671
+ var timer, guid;
3618
3672
 
3619
- // ..........................................................
3620
- // OBSERVER HELPER
3621
- //
3673
+ timer = {
3674
+ target: target,
3675
+ method: method,
3676
+ args: slice.call(arguments),
3677
+ next: true
3678
+ };
3622
3679
 
3623
- Ember.observer = function(func) {
3624
- var paths = a_slice.call(arguments, 1);
3625
- func.__ember_observes__ = paths;
3626
- return func;
3627
- };
3680
+ guid = Ember.guidFor(timer);
3681
+ timers[guid] = timer;
3628
3682
 
3629
- Ember.beforeObserver = function(func) {
3630
- var paths = a_slice.call(arguments, 1);
3631
- func.__ember_observesBefore__ = paths;
3632
- return func;
3683
+ if (!scheduledNext) scheduledNext = setTimeout(invokeNextTimers, 1);
3684
+ return guid;
3633
3685
  };
3634
3686
 
3687
+ /**
3688
+ Cancels a scheduled item. Must be a value returned by `Ember.run.later()`,
3689
+ `Ember.run.once()`, or `Ember.run.next()`.
3635
3690
 
3691
+ var runNext = Ember.run.next(myContext, function(){
3692
+ // will not be executed
3693
+ });
3694
+ Ember.run.cancel(runNext);
3636
3695
 
3696
+ var runLater = Ember.run.next(myContext, function(){
3697
+ // will not be executed
3698
+ }, 500);
3699
+ Ember.run.cancel(runLater);
3637
3700
 
3701
+ var runOnce = Ember.run.once(myContext, function(){
3702
+ // will not be executed
3703
+ });
3704
+ Ember.run.cancel(runOnce);
3638
3705
 
3706
+ @param {Object} timer
3707
+ Timer object to cancel
3639
3708
 
3709
+ @returns {void}
3710
+ */
3711
+ Ember.run.cancel = function(timer) {
3712
+ delete timers[timer];
3713
+ };
3640
3714
 
3641
3715
  })();
3642
3716
 
@@ -3645,1539 +3719,1552 @@ Ember.beforeObserver = function(func) {
3645
3719
  (function() {
3646
3720
  // ==========================================================================
3647
3721
  // Project: Ember Runtime
3648
- // Copyright: ©2006-2011 Strobe Inc. and contributors.
3649
- // Portions ©2008-2010 Apple Inc. All rights reserved.
3722
+ // Copyright: ©2011 Strobe Inc. and contributors.
3650
3723
  // License: Licensed under MIT license (see license.js)
3651
3724
  // ==========================================================================
3652
- /*globals ember_assert */
3653
3725
  // Ember.Logger
3654
- // Ember.watch.flushPending
3655
- // Ember.beginPropertyChanges, Ember.endPropertyChanges
3656
- // Ember.guidFor
3657
- // Ember.ArrayUtils
3726
+ // get, getPath, setPath, trySetPath
3727
+ // guidFor, isArray, meta
3728
+ // addObserver, removeObserver
3729
+ // Ember.run.schedule
3658
3730
 
3659
3731
  // ..........................................................
3660
- // HELPERS
3732
+ // CONSTANTS
3661
3733
  //
3662
3734
 
3663
- var slice = Array.prototype.slice;
3664
- var forEach = Ember.ArrayUtils.forEach;
3665
3735
 
3666
- // invokes passed params - normalizing so you can pass target/func,
3667
- // target/string or just func
3668
- /** @private */
3669
- function invoke(target, method, args, ignore) {
3736
+ /**
3737
+ @static
3670
3738
 
3671
- if (method===undefined) {
3672
- method = target;
3673
- target = undefined;
3674
- }
3739
+ Debug parameter you can turn on. This will log all bindings that fire to
3740
+ the console. This should be disabled in production code. Note that you
3741
+ can also enable this from the console or temporarily.
3675
3742
 
3676
- if ('string'===typeof method) method = target[method];
3677
- if (args && ignore>0) {
3678
- args = args.length>ignore ? slice.call(args, ignore) : null;
3679
- }
3743
+ @type Boolean
3744
+ @default false
3745
+ */
3746
+ Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS;
3680
3747
 
3681
- // Unfortunately in some browsers we lose the backtrace if we rethrow the existing error,
3682
- // so in the event that we don't have an `onerror` handler we don't wrap in a try/catch
3683
- if ('function' === typeof Ember.onerror) {
3684
- try {
3685
- // IE8's Function.prototype.apply doesn't accept undefined/null arguments.
3686
- return method.apply(target || this, args || []);
3687
- } catch (error) {
3688
- Ember.onerror(error);
3689
- }
3690
- } else {
3691
- // IE8's Function.prototype.apply doesn't accept undefined/null arguments.
3692
- return method.apply(target || this, args || []);
3693
- }
3694
- }
3748
+ /**
3749
+ @static
3695
3750
 
3751
+ Performance paramter. This will benchmark the time spent firing each
3752
+ binding.
3696
3753
 
3697
- // ..........................................................
3698
- // RUNLOOP
3699
- //
3754
+ @type Boolean
3755
+ */
3756
+ Ember.BENCHMARK_BINDING_NOTIFICATIONS = !!Ember.ENV.BENCHMARK_BINDING_NOTIFICATIONS;
3700
3757
 
3701
- var timerMark; // used by timers...
3758
+ /**
3759
+ @static
3702
3760
 
3703
- /** @private */
3704
- var K = function() {};
3761
+ Performance parameter. This will benchmark the time spend configuring each
3762
+ binding.
3705
3763
 
3706
- /** @private */
3707
- var RunLoop = function(prev) {
3708
- var self;
3764
+ @type Boolean
3765
+ */
3766
+ Ember.BENCHMARK_BINDING_SETUP = !!Ember.ENV.BENCHMARK_BINDING_SETUP;
3709
3767
 
3710
- if (this instanceof RunLoop) {
3711
- self = this;
3712
- } else {
3713
- self = new K();
3714
- }
3715
-
3716
- self._prev = prev || null;
3717
- self.onceTimers = {};
3718
-
3719
- return self;
3720
- };
3721
3768
 
3722
- K.prototype = RunLoop.prototype;
3769
+ /**
3770
+ @static
3723
3771
 
3724
- RunLoop.prototype = {
3725
- end: function() {
3726
- this.flush();
3727
- },
3772
+ Default placeholder for multiple values in bindings.
3728
3773
 
3729
- prev: function() {
3730
- return this._prev;
3731
- },
3774
+ @type String
3775
+ @default '@@MULT@@'
3776
+ */
3777
+ Ember.MULTIPLE_PLACEHOLDER = '@@MULT@@';
3732
3778
 
3733
- // ..........................................................
3734
- // Delayed Actions
3735
- //
3779
+ /**
3780
+ @static
3736
3781
 
3737
- schedule: function(queueName, target, method) {
3738
- var queues = this._queues, queue;
3739
- if (!queues) queues = this._queues = {};
3740
- queue = queues[queueName];
3741
- if (!queue) queue = queues[queueName] = [];
3782
+ Default placeholder for empty values in bindings. Used by notEmpty()
3783
+ helper unless you specify an alternative.
3742
3784
 
3743
- var args = arguments.length>3 ? slice.call(arguments, 3) : null;
3744
- queue.push({ target: target, method: method, args: args });
3745
- return this;
3746
- },
3785
+ @type String
3786
+ @default '@@EMPTY@@'
3787
+ */
3788
+ Ember.EMPTY_PLACEHOLDER = '@@EMPTY@@';
3747
3789
 
3748
- flush: function(queueName) {
3749
- var queues = this._queues, queueNames, idx, len, queue, log;
3790
+ // ..........................................................
3791
+ // TYPE COERCION HELPERS
3792
+ //
3750
3793
 
3751
- if (!queues) return this; // nothing to do
3794
+ // Coerces a non-array value into an array.
3795
+ /** @private */
3796
+ function MULTIPLE(val) {
3797
+ if (val instanceof Array) return val;
3798
+ if (val === undefined || val === null) return [];
3799
+ return [val];
3800
+ }
3752
3801
 
3753
- function iter(item) {
3754
- invoke(item.target, item.method, item.args);
3755
- }
3802
+ // Treats a single-element array as the element. Otherwise
3803
+ // returns a placeholder.
3804
+ /** @private */
3805
+ function SINGLE(val, placeholder) {
3806
+ if (val instanceof Array) {
3807
+ if (val.length>1) return placeholder;
3808
+ else return val[0];
3809
+ }
3810
+ return val;
3811
+ }
3756
3812
 
3757
- Ember.watch.flushPending(); // make sure all chained watchers are setup
3813
+ // Coerces the binding value into a Boolean.
3758
3814
 
3759
- if (queueName) {
3760
- while (this._queues && (queue = this._queues[queueName])) {
3761
- this._queues[queueName] = null;
3815
+ var BOOL = {
3816
+ to: function (val) {
3817
+ return !!val;
3818
+ }
3819
+ };
3762
3820
 
3763
- // the sync phase is to allow property changes to propagate. don't
3764
- // invoke observers until that is finished.
3765
- if (queueName === 'sync') {
3766
- log = Ember.LOG_BINDINGS;
3767
- if (log) Ember.Logger.log('Begin: Flush Sync Queue');
3821
+ // Returns the Boolean inverse of the value.
3822
+ var NOT = {
3823
+ to: function NOT(val) {
3824
+ return !val;
3825
+ }
3826
+ };
3768
3827
 
3769
- Ember.beginPropertyChanges();
3770
- try {
3771
- forEach(queue, iter);
3772
- } finally {
3773
- Ember.endPropertyChanges();
3774
- }
3828
+ var get = Ember.get,
3829
+ getPath = Ember.getPath,
3830
+ setPath = Ember.setPath,
3831
+ guidFor = Ember.guidFor,
3832
+ isGlobalPath = Ember.isGlobalPath;
3775
3833
 
3776
- if (log) Ember.Logger.log('End: Flush Sync Queue');
3834
+ // Applies a binding's transformations against a value.
3835
+ /** @private */
3836
+ function getTransformedValue(binding, val, obj, dir) {
3777
3837
 
3778
- } else {
3779
- forEach(queue, iter);
3780
- }
3781
- }
3838
+ // First run a type transform, if it exists, that changes the fundamental
3839
+ // type of the value. For example, some transforms convert an array to a
3840
+ // single object.
3782
3841
 
3783
- } else {
3784
- queueNames = Ember.run.queues;
3785
- len = queueNames.length;
3786
- do {
3787
- this._queues = null;
3788
- for(idx=0;idx<len;idx++) {
3789
- queueName = queueNames[idx];
3790
- queue = queues[queueName];
3842
+ var typeTransform = binding._typeTransform;
3843
+ if (typeTransform) { val = typeTransform(val, binding._placeholder); }
3791
3844
 
3792
- if (queue) {
3793
- // the sync phase is to allow property changes to propagate. don't
3794
- // invoke observers until that is finished.
3795
- if (queueName === 'sync') {
3796
- log = Ember.LOG_BINDINGS;
3797
- if (log) Ember.Logger.log('Begin: Flush Sync Queue');
3845
+ // handle transforms
3846
+ var transforms = binding._transforms,
3847
+ len = transforms ? transforms.length : 0,
3848
+ idx;
3798
3849
 
3799
- Ember.beginPropertyChanges();
3800
- try {
3801
- forEach(queue, iter);
3802
- } finally {
3803
- Ember.endPropertyChanges();
3804
- }
3850
+ for(idx=0;idx<len;idx++) {
3851
+ var transform = transforms[idx][dir];
3852
+ if (transform) { val = transform.call(this, val, obj); }
3853
+ }
3854
+ return val;
3855
+ }
3805
3856
 
3806
- if (log) Ember.Logger.log('End: Flush Sync Queue');
3807
- } else {
3808
- forEach(queue, iter);
3809
- }
3810
- }
3811
- }
3812
- } while (queues = this._queues); // go until queues stay clean
3813
- }
3857
+ /** @private */
3858
+ function empty(val) {
3859
+ return val===undefined || val===null || val==='' || (Ember.isArray(val) && get(val, 'length')===0) ;
3860
+ }
3814
3861
 
3815
- timerMark = null;
3862
+ /** @private */
3863
+ function getPathWithGlobals(obj, path) {
3864
+ return getPath(isGlobalPath(path) ? window : obj, path);
3865
+ }
3816
3866
 
3817
- return this;
3867
+ /** @private */
3868
+ function getTransformedFromValue(obj, binding) {
3869
+ var operation = binding._operation,
3870
+ fromValue;
3871
+ if (operation) {
3872
+ fromValue = operation(obj, binding._from, binding._operand);
3873
+ } else {
3874
+ fromValue = getPathWithGlobals(obj, binding._from);
3818
3875
  }
3876
+ return getTransformedValue(binding, fromValue, obj, 'to');
3877
+ }
3878
+
3879
+ /** @private */
3880
+ function getTransformedToValue(obj, binding) {
3881
+ var toValue = getPath(obj, binding._to);
3882
+ return getTransformedValue(binding, toValue, obj, 'from');
3883
+ }
3819
3884
 
3885
+ /** @private */
3886
+ var AND_OPERATION = function(obj, left, right) {
3887
+ return getPathWithGlobals(obj, left) && getPathWithGlobals(obj, right);
3820
3888
  };
3821
3889
 
3822
- Ember.RunLoop = RunLoop;
3890
+ /** @private */
3891
+ var OR_OPERATION = function(obj, left, right) {
3892
+ return getPathWithGlobals(obj, left) || getPathWithGlobals(obj, right);
3893
+ };
3823
3894
 
3824
3895
  // ..........................................................
3825
- // Ember.run - this is ideally the only public API the dev sees
3896
+ // BINDING
3826
3897
  //
3827
- /**
3828
- * @namespace Ember.run is both a function and a namespace for
3829
- * RunLoop-related functions.
3830
- * @name Ember.run
3831
- */
3832
-
3833
- /**
3834
- Runs the passed target and method inside of a RunLoop, ensuring any
3835
- deferred actions including bindings and views updates are flushed at the
3836
- end.
3837
-
3838
- Normally you should not need to invoke this method yourself. However if
3839
- you are implementing raw event handlers when interfacing with other
3840
- libraries or plugins, you should probably wrap all of your code inside this
3841
- call.
3842
-
3843
- Ember.run(function(){
3844
- // code to be execute within a RunLoop
3845
- });
3898
+ /** @private */
3899
+ var K = function() {};
3846
3900
 
3847
- @name run^2
3848
- @methodOf Ember.run
3849
- @param {Object} target
3850
- (Optional) target of method to call
3901
+ /** @private */
3902
+ var Binding = function(toPath, fromPath) {
3903
+ var self;
3851
3904
 
3852
- @param {Function|String} method
3853
- Method to invoke. May be a function or a string. If you pass a string
3854
- then it will be looked up on the passed target.
3905
+ if (this instanceof Binding) {
3906
+ self = this;
3907
+ } else {
3908
+ self = new K();
3909
+ }
3855
3910
 
3856
- @param {Object...} args
3857
- Any additional arguments you wish to pass to the method.
3911
+ /** @private */
3912
+ self._direction = 'fwd';
3858
3913
 
3859
- @returns {Object} return value from invoking the passed function.
3860
- */
3861
- Ember.run = function(target, method) {
3914
+ /** @private */
3915
+ self._from = fromPath;
3916
+ self._to = toPath;
3862
3917
 
3863
- var ret, loop;
3864
- run.begin();
3865
- try {
3866
- if (target || method) ret = invoke(target, method, arguments, 2);
3867
- } finally {
3868
- run.end();
3869
- }
3870
- return ret;
3918
+ return self;
3871
3919
  };
3872
3920
 
3873
- /** @private */
3874
- var run = Ember.run;
3921
+ K.prototype = Binding.prototype;
3875
3922
 
3923
+ Binding.prototype = /** @scope Ember.Binding.prototype */ {
3924
+ /**
3925
+ This copies the Binding so it can be connected to another object.
3926
+ @returns {Ember.Binding}
3927
+ */
3928
+ copy: function () {
3929
+ var copy = new Binding(this._to, this._from);
3930
+ if (this._oneWay) {
3931
+ copy._oneWay = true;
3932
+ }
3933
+ if (this._transforms) {
3934
+ copy._transforms = this._transforms.slice(0);
3935
+ }
3936
+ if (this._typeTransform) {
3937
+ copy._typeTransform = this._typeTransform;
3938
+ copy._placeholder = this._placeholder;
3939
+ }
3940
+ if (this._operand) {
3941
+ copy._operand = this._operand;
3942
+ copy._operation = this._operation;
3943
+ }
3944
+ return copy;
3945
+ },
3876
3946
 
3877
- /**
3878
- Begins a new RunLoop. Any deferred actions invoked after the begin will
3879
- be buffered until you invoke a matching call to Ember.run.end(). This is
3880
- an lower-level way to use a RunLoop instead of using Ember.run().
3947
+ // ..........................................................
3948
+ // CONFIG
3949
+ //
3881
3950
 
3882
- Ember.run.begin();
3883
- // code to be execute within a RunLoop
3884
- Ember.run.end();
3951
+ /**
3952
+ This will set "from" property path to the specified value. It will not
3953
+ attempt to resolve this property path to an actual object until you
3954
+ connect the binding.
3885
3955
 
3956
+ The binding will search for the property path starting at the root object
3957
+ you pass when you connect() the binding. It follows the same rules as
3958
+ `getPath()` - see that method for more information.
3886
3959
 
3887
- @returns {void}
3888
- */
3889
- Ember.run.begin = function() {
3890
- run.currentRunLoop = new RunLoop(run.currentRunLoop);
3891
- };
3960
+ @param {String} propertyPath the property path to connect to
3961
+ @returns {Ember.Binding} receiver
3962
+ */
3963
+ from: function(path) {
3964
+ this._from = path;
3965
+ return this;
3966
+ },
3892
3967
 
3893
- /**
3894
- Ends a RunLoop. This must be called sometime after you call Ember.run.begin()
3895
- to flush any deferred actions. This is a lower-level way to use a RunLoop
3896
- instead of using Ember.run().
3968
+ /**
3969
+ This will set the "to" property path to the specified value. It will not
3970
+ attempt to resolve this property path to an actual object until you
3971
+ connect the binding.
3897
3972
 
3898
- Ember.run.begin();
3899
- // code to be execute within a RunLoop
3900
- Ember.run.end();
3973
+ The binding will search for the property path starting at the root object
3974
+ you pass when you connect() the binding. It follows the same rules as
3975
+ `getPath()` - see that method for more information.
3901
3976
 
3902
- @returns {void}
3903
- */
3904
- Ember.run.end = function() {
3977
+ @param {String|Tuple} propertyPath A property path or tuple
3978
+ @param {Object} [root] Root object to use when resolving the path.
3979
+ @returns {Ember.Binding} this
3980
+ */
3981
+ to: function(path) {
3982
+ this._to = path;
3983
+ return this;
3984
+ },
3905
3985
 
3906
- try {
3907
- run.currentRunLoop.end();
3908
- }
3909
- finally {
3910
- run.currentRunLoop = run.currentRunLoop.prev();
3911
- }
3912
- };
3986
+ /**
3987
+ Configures the binding as one way. A one-way binding will relay changes
3988
+ on the "from" side to the "to" side, but not the other way around. This
3989
+ means that if you change the "to" side directly, the "from" side may have
3990
+ a different value.
3913
3991
 
3914
- /**
3915
- Array of named queues. This array determines the order in which queues
3916
- are flushed at the end of the RunLoop. You can define your own queues by
3917
- simply adding the queue name to this array. Normally you should not need
3918
- to inspect or modify this property.
3992
+ @param {Boolean} flag
3993
+ (Optional) passing nothing here will make the binding oneWay. You can
3994
+ instead pass false to disable oneWay, making the binding two way again.
3919
3995
 
3920
- @property {String}
3921
- @default ['sync', 'actions', 'destroy', 'timers']
3922
- */
3923
- Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];
3996
+ @returns {Ember.Binding} receiver
3997
+ */
3998
+ oneWay: function(flag) {
3999
+ this._oneWay = flag===undefined ? true : !!flag;
4000
+ return this;
4001
+ },
3924
4002
 
3925
- /**
3926
- Adds the passed target/method and any optional arguments to the named
3927
- queue to be executed at the end of the RunLoop. If you have not already
3928
- started a RunLoop when calling this method one will be started for you
3929
- automatically.
4003
+ /**
4004
+ Adds the specified transform to the array of transform functions.
3930
4005
 
3931
- At the end of a RunLoop, any methods scheduled in this way will be invoked.
3932
- Methods will be invoked in an order matching the named queues defined in
3933
- the run.queues property.
4006
+ A transform is a hash with `to` and `from` properties. Each property
4007
+ should be a function that performs a transformation in either the
4008
+ forward or back direction.
3934
4009
 
3935
- Ember.run.schedule('timers', this, function(){
3936
- // this will be executed at the end of the RunLoop, when timers are run
3937
- console.log("scheduled on timers queue");
3938
- });
3939
- Ember.run.schedule('sync', this, function(){
3940
- // this will be executed at the end of the RunLoop, when bindings are synced
3941
- console.log("scheduled on sync queue");
3942
- });
3943
- // Note the functions will be run in order based on the run queues order. Output would be:
3944
- // scheduled on sync queue
3945
- // scheduled on timers queue
4010
+ The functions you pass must have the following signature:
3946
4011
 
3947
- @param {String} queue
3948
- The name of the queue to schedule against. Default queues are 'sync' and
3949
- 'actions'
4012
+ function(value) {};
3950
4013
 
3951
- @param {Object} target
3952
- (Optional) target object to use as the context when invoking a method.
4014
+ They must also return the transformed value.
3953
4015
 
3954
- @param {String|Function} method
3955
- The method to invoke. If you pass a string it will be resolved on the
3956
- target object at the time the scheduled item is invoked allowing you to
3957
- change the target function.
4016
+ Transforms are invoked in the order they were added. If you are
4017
+ extending a binding and want to reset the transforms, you can call
4018
+ `resetTransform()` first.
3958
4019
 
3959
- @param {Object} arguments...
3960
- Optional arguments to be passed to the queued method.
4020
+ @param {Function} transformFunc the transform function.
4021
+ @returns {Ember.Binding} this
4022
+ */
4023
+ transform: function(transform) {
4024
+ if ('function' === typeof transform) {
4025
+ transform = { to: transform };
4026
+ }
3961
4027
 
3962
- @returns {void}
3963
- */
3964
- Ember.run.schedule = function(queue, target, method) {
3965
- var loop = run.autorun();
3966
- loop.schedule.apply(loop, arguments);
3967
- };
4028
+ if (!this._transforms) this._transforms = [];
4029
+ this._transforms.push(transform);
4030
+ return this;
4031
+ },
3968
4032
 
3969
- var autorunTimer;
4033
+ /**
4034
+ Resets the transforms for the binding. After calling this method the
4035
+ binding will no longer transform values. You can then add new transforms
4036
+ as needed.
3970
4037
 
3971
- /** @private */
3972
- function autorun() {
3973
- autorunTimer = null;
3974
- if (run.currentRunLoop) run.end();
3975
- }
4038
+ @returns {Ember.Binding} this
4039
+ */
4040
+ resetTransforms: function() {
4041
+ this._transforms = null;
4042
+ return this;
4043
+ },
3976
4044
 
3977
- /**
3978
- Begins a new RunLoop if necessary and schedules a timer to flush the
3979
- RunLoop at a later time. This method is used by parts of Ember to
3980
- ensure the RunLoop always finishes. You normally do not need to call this
3981
- method directly. Instead use Ember.run().
4045
+ /**
4046
+ Adds a transform to the chain that will allow only single values to pass.
4047
+ This will allow single values and nulls to pass through. If you pass an
4048
+ array, it will be mapped as so:
3982
4049
 
3983
- Ember.run.autorun();
4050
+ - [] => null
4051
+ - [a] => a
4052
+ - [a,b,c] => Multiple Placeholder
3984
4053
 
3985
- @returns {Ember.RunLoop} the new current RunLoop
3986
- */
3987
- Ember.run.autorun = function() {
4054
+ You can pass in an optional multiple placeholder or it will use the
4055
+ default.
3988
4056
 
3989
- if (!run.currentRunLoop) {
3990
- run.begin();
4057
+ Note that this transform will only happen on forwarded valued. Reverse
4058
+ values are send unchanged.
3991
4059
 
3992
- // TODO: throw during tests
3993
- if (Ember.testing) {
3994
- run.end();
3995
- } else if (!autorunTimer) {
3996
- autorunTimer = setTimeout(autorun, 1);
3997
- }
3998
- }
4060
+ @param {String} fromPath from path or null
4061
+ @param {Object} [placeholder] Placeholder value.
4062
+ @returns {Ember.Binding} this
4063
+ */
4064
+ single: function(placeholder) {
4065
+ if (placeholder===undefined) placeholder = Ember.MULTIPLE_PLACEHOLDER;
4066
+ this._typeTransform = SINGLE;
4067
+ this._placeholder = placeholder;
4068
+ return this;
4069
+ },
3999
4070
 
4000
- return run.currentRunLoop;
4001
- };
4071
+ /**
4072
+ Adds a transform that will convert the passed value to an array. If
4073
+ the value is null or undefined, it will be converted to an empty array.
4002
4074
 
4003
- /**
4004
- Immediately flushes any events scheduled in the 'sync' queue. Bindings
4005
- use this queue so this method is a useful way to immediately force all
4006
- bindings in the application to sync.
4075
+ @param {String} [fromPath]
4076
+ @returns {Ember.Binding} this
4077
+ */
4078
+ multiple: function() {
4079
+ this._typeTransform = MULTIPLE;
4080
+ this._placeholder = null;
4081
+ return this;
4082
+ },
4007
4083
 
4008
- You should call this method anytime you need any changed state to propagate
4009
- throughout the app immediately without repainting the UI.
4084
+ /**
4085
+ Adds a transform to convert the value to a bool value. If the value is
4086
+ an array it will return true if array is not empty. If the value is a
4087
+ string it will return true if the string is not empty.
4010
4088
 
4011
- Ember.run.sync();
4089
+ @returns {Ember.Binding} this
4090
+ */
4091
+ bool: function() {
4092
+ this.transform(BOOL);
4093
+ return this;
4094
+ },
4012
4095
 
4013
- @returns {void}
4014
- */
4015
- Ember.run.sync = function() {
4016
- run.autorun();
4017
- run.currentRunLoop.flush('sync');
4018
- };
4096
+ /**
4097
+ Adds a transform that will return the placeholder value if the value is
4098
+ null, undefined, an empty array or an empty string. See also notNull().
4019
4099
 
4020
- // ..........................................................
4021
- // TIMERS
4022
- //
4100
+ @param {Object} [placeholder] Placeholder value.
4101
+ @returns {Ember.Binding} this
4102
+ */
4103
+ notEmpty: function(placeholder) {
4104
+ if (placeholder === null || placeholder === undefined) {
4105
+ placeholder = Ember.EMPTY_PLACEHOLDER;
4106
+ }
4023
4107
 
4024
- var timers = {}; // active timers...
4108
+ this.transform({
4109
+ to: function(val) { return empty(val) ? placeholder : val; }
4110
+ });
4025
4111
 
4026
- var laterScheduled = false;
4027
- /** @private */
4028
- function invokeLaterTimers() {
4029
- var now = (+ new Date()), earliest = -1;
4030
- for(var key in timers) {
4031
- if (!timers.hasOwnProperty(key)) continue;
4032
- var timer = timers[key];
4033
- if (timer && timer.expires) {
4034
- if (now >= timer.expires) {
4035
- delete timers[key];
4036
- invoke(timer.target, timer.method, timer.args, 2);
4037
- } else {
4038
- if (earliest<0 || (timer.expires < earliest)) earliest=timer.expires;
4039
- }
4040
- }
4041
- }
4112
+ return this;
4113
+ },
4042
4114
 
4043
- // schedule next timeout to fire...
4044
- if (earliest>0) setTimeout(invokeLaterTimers, earliest-(+ new Date()));
4045
- }
4115
+ /**
4116
+ Adds a transform that will return the placeholder value if the value is
4117
+ null or undefined. Otherwise it will passthrough untouched. See also notEmpty().
4046
4118
 
4047
- /**
4048
- Invokes the passed target/method and optional arguments after a specified
4049
- period if time. The last parameter of this method must always be a number
4050
- of milliseconds.
4119
+ @param {String} fromPath from path or null
4120
+ @param {Object} [placeholder] Placeholder value.
4121
+ @returns {Ember.Binding} this
4122
+ */
4123
+ notNull: function(placeholder) {
4124
+ if (placeholder === null || placeholder === undefined) {
4125
+ placeholder = Ember.EMPTY_PLACEHOLDER;
4126
+ }
4051
4127
 
4052
- You should use this method whenever you need to run some action after a
4053
- period of time instead of using setTimeout(). This method will ensure that
4054
- items that expire during the same script execution cycle all execute
4055
- together, which is often more efficient than using a real setTimeout.
4128
+ this.transform({
4129
+ to: function(val) { return (val === null || val === undefined) ? placeholder : val; }
4130
+ });
4056
4131
 
4057
- Ember.run.later(myContext, function(){
4058
- // code here will execute within a RunLoop in about 500ms with this == myContext
4059
- }, 500);
4132
+ return this;
4133
+ },
4060
4134
 
4061
- @param {Object} target
4062
- (optional) target of method to invoke
4135
+ /**
4136
+ Adds a transform to convert the value to the inverse of a bool value. This
4137
+ uses the same transform as bool() but inverts it.
4063
4138
 
4064
- @param {Function|String} method
4065
- The method to invoke. If you pass a string it will be resolved on the
4066
- target at the time the method is invoked.
4139
+ @returns {Ember.Binding} this
4140
+ */
4141
+ not: function() {
4142
+ this.transform(NOT);
4143
+ return this;
4144
+ },
4067
4145
 
4068
- @param {Object...} args
4069
- Optional arguments to pass to the timeout.
4146
+ /**
4147
+ Adds a transform that will return true if the value is null or undefined, false otherwise.
4070
4148
 
4071
- @param {Number} wait
4072
- Number of milliseconds to wait.
4149
+ @returns {Ember.Binding} this
4150
+ */
4151
+ isNull: function() {
4152
+ this.transform(function(val) { return val === null || val === undefined; });
4153
+ return this;
4154
+ },
4073
4155
 
4074
- @returns {Timer} an object you can use to cancel a timer at a later time.
4075
- */
4076
- Ember.run.later = function(target, method) {
4077
- var args, expires, timer, guid, wait;
4156
+ /** @private */
4157
+ toString: function() {
4158
+ var oneWay = this._oneWay ? '[oneWay]' : '';
4159
+ return "Ember.Binding<" + guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay;
4160
+ },
4078
4161
 
4079
- // setTimeout compatibility...
4080
- if (arguments.length===2 && 'function' === typeof target) {
4081
- wait = method;
4082
- method = target;
4083
- target = undefined;
4084
- args = [target, method];
4162
+ // ..........................................................
4163
+ // CONNECT AND SYNC
4164
+ //
4085
4165
 
4086
- } else {
4087
- args = slice.call(arguments);
4088
- wait = args.pop();
4089
- }
4166
+ /**
4167
+ Attempts to connect this binding instance so that it can receive and relay
4168
+ changes. This method will raise an exception if you have not set the
4169
+ from/to properties yet.
4090
4170
 
4091
- expires = (+ new Date())+wait;
4092
- timer = { target: target, method: method, expires: expires, args: args };
4093
- guid = Ember.guidFor(timer);
4094
- timers[guid] = timer;
4095
- run.once(timers, invokeLaterTimers);
4096
- return guid;
4097
- };
4171
+ @param {Object} obj
4172
+ The root object for this binding.
4098
4173
 
4099
- /** @private */
4100
- function invokeOnceTimer(guid, onceTimers) {
4101
- if (onceTimers[this.tguid]) delete onceTimers[this.tguid][this.mguid];
4102
- if (timers[guid]) invoke(this.target, this.method, this.args, 2);
4103
- delete timers[guid];
4104
- }
4174
+ @param {Boolean} preferFromParam
4175
+ private: Normally, `connect` cannot take an object if `from` already set
4176
+ an object. Internally, we would like to be able to provide a default object
4177
+ to be used if no object was provided via `from`, so this parameter turns
4178
+ off the assertion.
4105
4179
 
4106
- /**
4107
- Schedules an item to run one time during the current RunLoop. Calling
4108
- this method with the same target/method combination will have no effect.
4180
+ @returns {Ember.Binding} this
4181
+ */
4182
+ connect: function(obj) {
4109
4183
 
4110
- Note that although you can pass optional arguments these will not be
4111
- considered when looking for duplicates. New arguments will replace previous
4112
- calls.
4113
4184
 
4114
- Ember.run(function(){
4115
- var doFoo = function() { foo(); }
4116
- Ember.run.once(myContext, doFoo);
4117
- Ember.run.once(myContext, doFoo);
4118
- // doFoo will only be executed once at the end of the RunLoop
4119
- });
4185
+ var oneWay = this._oneWay, operand = this._operand;
4120
4186
 
4121
- @param {Object} target
4122
- (optional) target of method to invoke
4187
+ // add an observer on the object to be notified when the binding should be updated
4188
+ Ember.addObserver(obj, this._from, this, this.fromDidChange);
4123
4189
 
4124
- @param {Function|String} method
4125
- The method to invoke. If you pass a string it will be resolved on the
4126
- target at the time the method is invoked.
4190
+ // if there is an operand, add an observer onto it as well
4191
+ if (operand) { Ember.addObserver(obj, operand, this, this.fromDidChange); }
4127
4192
 
4128
- @param {Object...} args
4129
- Optional arguments to pass to the timeout.
4193
+ // if the binding is a two-way binding, also set up an observer on the target
4194
+ // object.
4195
+ if (!oneWay) { Ember.addObserver(obj, this._to, this, this.toDidChange); }
4130
4196
 
4197
+ if (Ember.meta(obj,false).proto !== obj) { this._scheduleSync(obj, 'fwd'); }
4131
4198
 
4132
- @returns {Object} timer
4133
- */
4134
- Ember.run.once = function(target, method) {
4135
- var tguid = Ember.guidFor(target), mguid = Ember.guidFor(method), guid, timer;
4199
+ this._readyToSync = true;
4200
+ return this;
4201
+ },
4136
4202
 
4137
- var onceTimers = run.autorun().onceTimers;
4138
- guid = onceTimers[tguid] && onceTimers[tguid][mguid];
4139
- if (guid && timers[guid]) {
4140
- timers[guid].args = slice.call(arguments); // replace args
4203
+ /**
4204
+ Disconnects the binding instance. Changes will no longer be relayed. You
4205
+ will not usually need to call this method.
4141
4206
 
4142
- } else {
4143
- timer = {
4144
- target: target,
4145
- method: method,
4146
- args: slice.call(arguments),
4147
- tguid: tguid,
4148
- mguid: mguid
4149
- };
4207
+ @param {Object} obj
4208
+ The root object you passed when connecting the binding.
4150
4209
 
4151
- guid = Ember.guidFor(timer);
4152
- timers[guid] = timer;
4153
- if (!onceTimers[tguid]) onceTimers[tguid] = {};
4154
- onceTimers[tguid][mguid] = guid; // so it isn't scheduled more than once
4210
+ @returns {Ember.Binding} this
4211
+ */
4212
+ disconnect: function(obj) {
4155
4213
 
4156
- run.schedule('actions', timer, invokeOnceTimer, guid, onceTimers);
4157
- }
4158
4214
 
4159
- return guid;
4160
- };
4215
+ var oneWay = this._oneWay, operand = this._operand;
4161
4216
 
4162
- var scheduledNext = false;
4163
- /** @private */
4164
- function invokeNextTimers() {
4165
- scheduledNext = null;
4166
- for(var key in timers) {
4167
- if (!timers.hasOwnProperty(key)) continue;
4168
- var timer = timers[key];
4169
- if (timer.next) {
4170
- delete timers[key];
4171
- invoke(timer.target, timer.method, timer.args, 2);
4172
- }
4173
- }
4174
- }
4217
+ // remove an observer on the object so we're no longer notified of
4218
+ // changes that should update bindings.
4219
+ Ember.removeObserver(obj, this._from, this, this.fromDidChange);
4175
4220
 
4176
- /**
4177
- Schedules an item to run after control has been returned to the system.
4178
- This is often equivalent to calling setTimeout(function...,1).
4221
+ // if there is an operand, remove the observer from it as well
4222
+ if (operand) Ember.removeObserver(obj, operand, this, this.fromDidChange);
4179
4223
 
4180
- Ember.run.next(myContext, function(){
4181
- // code to be executed in the next RunLoop, which will be scheduled after the current one
4182
- });
4224
+ // if the binding is two-way, remove the observer from the target as well
4225
+ if (!oneWay) Ember.removeObserver(obj, this._to, this, this.toDidChange);
4183
4226
 
4184
- @param {Object} target
4185
- (optional) target of method to invoke
4227
+ this._readyToSync = false; // disable scheduled syncs...
4228
+ return this;
4229
+ },
4186
4230
 
4187
- @param {Function|String} method
4188
- The method to invoke. If you pass a string it will be resolved on the
4189
- target at the time the method is invoked.
4231
+ // ..........................................................
4232
+ // PRIVATE
4233
+ //
4190
4234
 
4191
- @param {Object...} args
4192
- Optional arguments to pass to the timeout.
4235
+ /** @private - called when the from side changes */
4236
+ fromDidChange: function(target) {
4237
+ this._scheduleSync(target, 'fwd');
4238
+ },
4193
4239
 
4194
- @returns {Object} timer
4195
- */
4196
- Ember.run.next = function(target, method) {
4197
- var timer, guid;
4240
+ /** @private - called when the to side changes */
4241
+ toDidChange: function(target) {
4242
+ this._scheduleSync(target, 'back');
4243
+ },
4198
4244
 
4199
- timer = {
4200
- target: target,
4201
- method: method,
4202
- args: slice.call(arguments),
4203
- next: true
4204
- };
4245
+ /** @private */
4246
+ _scheduleSync: function(obj, dir) {
4247
+ var guid = guidFor(obj), existingDir = this[guid];
4205
4248
 
4206
- guid = Ember.guidFor(timer);
4207
- timers[guid] = timer;
4249
+ // if we haven't scheduled the binding yet, schedule it
4250
+ if (!existingDir) {
4251
+ Ember.run.schedule('sync', this, this._sync, obj);
4252
+ this[guid] = dir;
4253
+ }
4208
4254
 
4209
- if (!scheduledNext) scheduledNext = setTimeout(invokeNextTimers, 1);
4210
- return guid;
4211
- };
4255
+ // If both a 'back' and 'fwd' sync have been scheduled on the same object,
4256
+ // default to a 'fwd' sync so that it remains deterministic.
4257
+ if (existingDir === 'back' && dir === 'fwd') {
4258
+ this[guid] = 'fwd';
4259
+ }
4260
+ },
4212
4261
 
4213
- /**
4214
- Cancels a scheduled item. Must be a value returned by `Ember.run.later()`,
4215
- `Ember.run.once()`, or `Ember.run.next()`.
4262
+ /** @private */
4263
+ _sync: function(obj) {
4264
+ var log = Ember.LOG_BINDINGS;
4216
4265
 
4217
- var runNext = Ember.run.next(myContext, function(){
4218
- // will not be executed
4219
- });
4220
- Ember.run.cancel(runNext);
4266
+ // don't synchronize destroyed objects or disconnected bindings
4267
+ if (obj.isDestroyed || !this._readyToSync) { return; }
4221
4268
 
4222
- var runLater = Ember.run.next(myContext, function(){
4223
- // will not be executed
4224
- }, 500);
4225
- Ember.run.cancel(runLater);
4269
+ // get the direction of the binding for the object we are
4270
+ // synchronizing from
4271
+ var guid = guidFor(obj), direction = this[guid];
4226
4272
 
4227
- var runOnce = Ember.run.once(myContext, function(){
4228
- // will not be executed
4229
- });
4230
- Ember.run.cancel(runOnce);
4273
+ var fromPath = this._from, toPath = this._to;
4231
4274
 
4232
- @param {Object} timer
4233
- Timer object to cancel
4275
+ delete this[guid];
4276
+
4277
+ // if we're synchronizing from the remote object...
4278
+ if (direction === 'fwd') {
4279
+ var fromValue = getTransformedFromValue(obj, this);
4280
+ if (log) {
4281
+ Ember.Logger.log(' ', this.toString(), '->', fromValue, obj);
4282
+ }
4283
+ if (this._oneWay) {
4284
+ Ember.trySetPath(Ember.isGlobalPath(toPath) ? window : obj, toPath, fromValue);
4285
+ } else {
4286
+ Ember._suspendObserver(obj, toPath, this, this.toDidChange, function () {
4287
+ Ember.trySetPath(Ember.isGlobalPath(toPath) ? window : obj, toPath, fromValue);
4288
+ });
4289
+ }
4290
+ // if we're synchronizing *to* the remote object
4291
+ } else if (direction === 'back') {// && !this._oneWay) {
4292
+ var toValue = getTransformedToValue(obj, this);
4293
+ if (log) {
4294
+ Ember.Logger.log(' ', this.toString(), '<-', toValue, obj);
4295
+ }
4296
+ Ember._suspendObserver(obj, fromPath, this, this.fromDidChange, function () {
4297
+ Ember.trySetPath(Ember.isGlobalPath(fromPath) ? window : obj, fromPath, toValue);
4298
+ });
4299
+ }
4300
+ }
4234
4301
 
4235
- @returns {void}
4236
- */
4237
- Ember.run.cancel = function(timer) {
4238
- delete timers[timer];
4239
4302
  };
4240
4303
 
4241
- // ..........................................................
4242
- // DEPRECATED API
4243
- //
4304
+ /** @private */
4305
+ function mixinProperties(to, from) {
4306
+ for (var key in from) {
4307
+ if (from.hasOwnProperty(key)) {
4308
+ to[key] = from[key];
4309
+ }
4310
+ }
4311
+ }
4244
4312
 
4245
- /**
4246
- @namespace Compatibility for Ember.run
4247
- @name Ember.RunLoop
4248
- @deprecated
4249
- */
4313
+ mixinProperties(Binding,
4314
+ /** @scope Ember.Binding */ {
4250
4315
 
4251
- /**
4252
- @deprecated
4253
- @method
4316
+ /**
4317
+ @see Ember.Binding.prototype.from
4318
+ */
4319
+ from: function() {
4320
+ var C = this, binding = new C();
4321
+ return binding.from.apply(binding, arguments);
4322
+ },
4254
4323
 
4255
- Use `#js:Ember.run.begin()` instead
4256
- */
4257
- Ember.RunLoop.begin = ember_deprecateFunc("Use Ember.run.begin instead of Ember.RunLoop.begin.", Ember.run.begin);
4324
+ /**
4325
+ @see Ember.Binding.prototype.to
4326
+ */
4327
+ to: function() {
4328
+ var C = this, binding = new C();
4329
+ return binding.to.apply(binding, arguments);
4330
+ },
4258
4331
 
4259
- /**
4260
- @deprecated
4261
- @method
4332
+ /**
4333
+ @see Ember.Binding.prototype.oneWay
4334
+ */
4335
+ oneWay: function(from, flag) {
4336
+ var C = this, binding = new C(null, from);
4337
+ return binding.oneWay(flag);
4338
+ },
4262
4339
 
4263
- Use `#js:Ember.run.end()` instead
4264
- */
4265
- Ember.RunLoop.end = ember_deprecateFunc("Use Ember.run.end instead of Ember.RunLoop.end.", Ember.run.end);
4340
+ /**
4341
+ @see Ember.Binding.prototype.single
4342
+ */
4343
+ single: function(from, placeholder) {
4344
+ var C = this, binding = new C(null, from);
4345
+ return binding.single(placeholder);
4346
+ },
4266
4347
 
4348
+ /**
4349
+ @see Ember.Binding.prototype.multiple
4350
+ */
4351
+ multiple: function(from) {
4352
+ var C = this, binding = new C(null, from);
4353
+ return binding.multiple();
4354
+ },
4267
4355
 
4356
+ /**
4357
+ @see Ember.Binding.prototype.transform
4358
+ */
4359
+ transform: function(from, func) {
4360
+ if (!func) {
4361
+ func = from;
4362
+ from = null;
4363
+ }
4364
+ var C = this, binding = new C(null, from);
4365
+ return binding.transform(func);
4366
+ },
4268
4367
 
4269
- })();
4368
+ /**
4369
+ @see Ember.Binding.prototype.notEmpty
4370
+ */
4371
+ notEmpty: function(from, placeholder) {
4372
+ var C = this, binding = new C(null, from);
4373
+ return binding.notEmpty(placeholder);
4374
+ },
4270
4375
 
4376
+ /**
4377
+ @see Ember.Binding.prototype.notNull
4378
+ */
4379
+ notNull: function(from, placeholder) {
4380
+ var C = this, binding = new C(null, from);
4381
+ return binding.notNull(placeholder);
4382
+ },
4271
4383
 
4272
4384
 
4273
- (function() {
4274
- // ==========================================================================
4275
- // Project: Ember Runtime
4276
- // Copyright: ©2011 Strobe Inc. and contributors.
4277
- // License: Licensed under MIT license (see license.js)
4278
- // ==========================================================================
4279
- /*globals ember_assert */
4280
- // Ember.Logger
4281
- // get, getPath, setPath, trySetPath
4282
- // guidFor, isArray, meta
4283
- // addObserver, removeObserver
4284
- // Ember.run.schedule
4385
+ /**
4386
+ @see Ember.Binding.prototype.bool
4387
+ */
4388
+ bool: function(from) {
4389
+ var C = this, binding = new C(null, from);
4390
+ return binding.bool();
4391
+ },
4285
4392
 
4286
- // ..........................................................
4287
- // CONSTANTS
4288
- //
4393
+ /**
4394
+ @see Ember.Binding.prototype.not
4395
+ */
4396
+ not: function(from) {
4397
+ var C = this, binding = new C(null, from);
4398
+ return binding.not();
4399
+ },
4289
4400
 
4401
+ /**
4402
+ @see Ember.Binding.prototype.isNull
4403
+ */
4404
+ isNull: function(from) {
4405
+ var C = this, binding = new C(null, from);
4406
+ return binding.isNull();
4407
+ },
4290
4408
 
4291
- /**
4292
- @static
4409
+ /**
4410
+ Adds a transform that forwards the logical 'AND' of values at 'pathA' and
4411
+ 'pathB' whenever either source changes. Note that the transform acts
4412
+ strictly as a one-way binding, working only in the direction
4293
4413
 
4294
- Debug parameter you can turn on. This will log all bindings that fire to
4295
- the console. This should be disabled in production code. Note that you
4296
- can also enable this from the console or temporarily.
4414
+ 'pathA' AND 'pathB' --> value (value returned is the result of ('pathA' && 'pathB'))
4297
4415
 
4298
- @type Boolean
4299
- @default false
4300
- */
4301
- Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS;
4416
+ Usage example where a delete button's `isEnabled` value is determined by
4417
+ whether something is selected in a list and whether the current user is
4418
+ allowed to delete:
4302
4419
 
4303
- /**
4304
- @static
4420
+ deleteButton: Ember.ButtonView.design({
4421
+ isEnabledBinding: Ember.Binding.and('MyApp.itemsController.hasSelection', 'MyApp.userController.canDelete')
4422
+ })
4305
4423
 
4306
- Performance paramter. This will benchmark the time spent firing each
4307
- binding.
4424
+ @param {String} pathA The first part of the conditional
4425
+ @param {String} pathB The second part of the conditional
4426
+ */
4427
+ and: function(pathA, pathB) {
4428
+ var C = this, binding = new C(null, pathA).oneWay();
4429
+ binding._operand = pathB;
4430
+ binding._operation = AND_OPERATION;
4431
+ return binding;
4432
+ },
4308
4433
 
4309
- @type Boolean
4310
- */
4311
- Ember.BENCHMARK_BINDING_NOTIFICATIONS = !!Ember.ENV.BENCHMARK_BINDING_NOTIFICATIONS;
4434
+ /**
4435
+ Adds a transform that forwards the 'OR' of values at 'pathA' and
4436
+ 'pathB' whenever either source changes. Note that the transform acts
4437
+ strictly as a one-way binding, working only in the direction
4312
4438
 
4313
- /**
4314
- @static
4439
+ 'pathA' AND 'pathB' --> value (value returned is the result of ('pathA' || 'pathB'))
4315
4440
 
4316
- Performance parameter. This will benchmark the time spend configuring each
4317
- binding.
4441
+ @param {String} pathA The first part of the conditional
4442
+ @param {String} pathB The second part of the conditional
4443
+ */
4444
+ or: function(pathA, pathB) {
4445
+ var C = this, binding = new C(null, pathA).oneWay();
4446
+ binding._operand = pathB;
4447
+ binding._operation = OR_OPERATION;
4448
+ return binding;
4449
+ },
4318
4450
 
4319
- @type Boolean
4320
- */
4321
- Ember.BENCHMARK_BINDING_SETUP = !!Ember.ENV.BENCHMARK_BINDING_SETUP;
4451
+ /**
4452
+ Registers a custom transform for use in bindings.
4453
+
4454
+ @param {String} name The name of the transform
4455
+ @param {Function} transform The transformation function
4456
+ */
4457
+ registerTransform: function(name, transform) {
4458
+ this.prototype[name] = transform;
4459
+ this[name] = function(from) {
4460
+ var C = this, binding = new C(null, from), args;
4461
+ args = Array.prototype.slice.call(arguments, 1);
4462
+ return binding[name].apply(binding, args);
4463
+ };
4464
+ }
4322
4465
 
4466
+ });
4323
4467
 
4324
4468
  /**
4325
- @static
4469
+ @class
4326
4470
 
4327
- Default placeholder for multiple values in bindings.
4471
+ A binding simply connects the properties of two objects so that whenever the
4472
+ value of one property changes, the other property will be changed also. You
4473
+ do not usually work with Binding objects directly but instead describe
4474
+ bindings in your class definition using something like:
4328
4475
 
4329
- @type String
4330
- @default '@@MULT@@'
4331
- */
4332
- Ember.MULTIPLE_PLACEHOLDER = '@@MULT@@';
4476
+ valueBinding: "MyApp.someController.title"
4333
4477
 
4334
- /**
4335
- @static
4478
+ This will create a binding from `MyApp.someController.title` to the `value`
4479
+ property of your object instance automatically. Now the two values will be
4480
+ kept in sync.
4336
4481
 
4337
- Default placeholder for empty values in bindings. Used by notEmpty()
4338
- helper unless you specify an alternative.
4482
+ ## Customizing Your Bindings
4339
4483
 
4340
- @type String
4341
- @default '@@EMPTY@@'
4342
- */
4343
- Ember.EMPTY_PLACEHOLDER = '@@EMPTY@@';
4344
-
4345
- // ..........................................................
4346
- // TYPE COERCION HELPERS
4347
- //
4484
+ In addition to synchronizing values, bindings can also perform some basic
4485
+ transforms on values. These transforms can help to make sure the data fed
4486
+ into one object always meets the expectations of that object regardless of
4487
+ what the other object outputs.
4348
4488
 
4349
- // Coerces a non-array value into an array.
4350
- /** @private */
4351
- function MULTIPLE(val) {
4352
- if (val instanceof Array) return val;
4353
- if (val === undefined || val === null) return [];
4354
- return [val];
4355
- }
4489
+ To customize a binding, you can use one of the many helper methods defined
4490
+ on Ember.Binding like so:
4356
4491
 
4357
- // Treats a single-element array as the element. Otherwise
4358
- // returns a placeholder.
4359
- /** @private */
4360
- function SINGLE(val, placeholder) {
4361
- if (val instanceof Array) {
4362
- if (val.length>1) return placeholder;
4363
- else return val[0];
4364
- }
4365
- return val;
4366
- }
4492
+ valueBinding: Ember.Binding.single("MyApp.someController.title")
4367
4493
 
4368
- // Coerces the binding value into a Boolean.
4494
+ This will create a binding just like the example above, except that now the
4495
+ binding will convert the value of `MyApp.someController.title` to a single
4496
+ object (removing any arrays) before applying it to the `value` property of
4497
+ your object.
4369
4498
 
4370
- var BOOL = {
4371
- to: function (val) {
4372
- return !!val;
4373
- }
4374
- };
4499
+ You can also chain helper methods to build custom bindings like so:
4375
4500
 
4376
- // Returns the Boolean inverse of the value.
4377
- var NOT = {
4378
- to: function NOT(val) {
4379
- return !val;
4380
- }
4381
- };
4501
+ valueBinding: Ember.Binding.single("MyApp.someController.title").notEmpty("(EMPTY)")
4382
4502
 
4383
- var get = Ember.get,
4384
- getPath = Ember.getPath,
4385
- setPath = Ember.setPath,
4386
- guidFor = Ember.guidFor,
4387
- isGlobalPath = Ember.isGlobalPath;
4503
+ This will force the value of MyApp.someController.title to be a single value
4504
+ and then check to see if the value is "empty" (null, undefined, empty array,
4505
+ or an empty string). If it is empty, the value will be set to the string
4506
+ "(EMPTY)".
4388
4507
 
4389
- // Applies a binding's transformations against a value.
4390
- /** @private */
4391
- function getTransformedValue(binding, val, obj, dir) {
4508
+ ## One Way Bindings
4392
4509
 
4393
- // First run a type transform, if it exists, that changes the fundamental
4394
- // type of the value. For example, some transforms convert an array to a
4395
- // single object.
4510
+ One especially useful binding customization you can use is the `oneWay()`
4511
+ helper. This helper tells Ember that you are only interested in
4512
+ receiving changes on the object you are binding from. For example, if you
4513
+ are binding to a preference and you want to be notified if the preference
4514
+ has changed, but your object will not be changing the preference itself, you
4515
+ could do:
4396
4516
 
4397
- var typeTransform = binding._typeTransform;
4398
- if (typeTransform) { val = typeTransform(val, binding._placeholder); }
4517
+ bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles")
4399
4518
 
4400
- // handle transforms
4401
- var transforms = binding._transforms,
4402
- len = transforms ? transforms.length : 0,
4403
- idx;
4519
+ This way if the value of MyApp.preferencesController.bigTitles changes the
4520
+ "bigTitles" property of your object will change also. However, if you
4521
+ change the value of your "bigTitles" property, it will not update the
4522
+ preferencesController.
4404
4523
 
4405
- for(idx=0;idx<len;idx++) {
4406
- var transform = transforms[idx][dir];
4407
- if (transform) { val = transform.call(this, val, obj); }
4408
- }
4409
- return val;
4410
- }
4524
+ One way bindings are almost twice as fast to setup and twice as fast to
4525
+ execute because the binding only has to worry about changes to one side.
4411
4526
 
4412
- /** @private */
4413
- function empty(val) {
4414
- return val===undefined || val===null || val==='' || (Ember.isArray(val) && get(val, 'length')===0) ;
4415
- }
4527
+ You should consider using one way bindings anytime you have an object that
4528
+ may be created frequently and you do not intend to change a property; only
4529
+ to monitor it for changes. (such as in the example above).
4416
4530
 
4417
- /** @private */
4418
- function getPathWithGlobals(obj, path) {
4419
- return getPath(isGlobalPath(path) ? window : obj, path);
4420
- }
4531
+ ## Adding Custom Transforms
4421
4532
 
4422
- /** @private */
4423
- function getTransformedFromValue(obj, binding) {
4424
- var operation = binding._operation,
4425
- fromValue;
4426
- if (operation) {
4427
- fromValue = operation(obj, binding._from, binding._operand);
4428
- } else {
4429
- fromValue = getPathWithGlobals(obj, binding._from);
4430
- }
4431
- return getTransformedValue(binding, fromValue, obj, 'to');
4432
- }
4533
+ In addition to using the standard helpers provided by Ember, you can
4534
+ also defined your own custom transform functions which will be used to
4535
+ convert the value. To do this, just define your transform function and add
4536
+ it to the binding with the transform() helper. The following example will
4537
+ not allow Integers less than ten. Note that it checks the value of the
4538
+ bindings and allows all other values to pass:
4433
4539
 
4434
- /** @private */
4435
- function getTransformedToValue(obj, binding) {
4436
- var toValue = getPath(obj, binding._to);
4437
- return getTransformedValue(binding, toValue, obj, 'from');
4438
- }
4540
+ valueBinding: Ember.Binding.transform(function(value, binding) {
4541
+ return ((Ember.typeOf(value) === 'number') && (value < 10)) ? 10 : value;
4542
+ }).from("MyApp.someController.value")
4439
4543
 
4440
- /** @private */
4441
- var AND_OPERATION = function(obj, left, right) {
4442
- return getPathWithGlobals(obj, left) && getPathWithGlobals(obj, right);
4443
- };
4544
+ If you would like to instead use this transform on a number of bindings,
4545
+ you can also optionally add your own helper method to Ember.Binding. This
4546
+ method should simply return the value of `this.transform()`. The example
4547
+ below adds a new helper called `notLessThan()` which will limit the value to
4548
+ be not less than the passed minimum:
4444
4549
 
4445
- /** @private */
4446
- var OR_OPERATION = function(obj, left, right) {
4447
- return getPathWithGlobals(obj, left) || getPathWithGlobals(obj, right);
4448
- };
4550
+ Ember.Binding.registerTransform('notLessThan', function(minValue) {
4551
+ return this.transform(function(value, binding) {
4552
+ return ((Ember.typeOf(value) === 'number') && (value < minValue)) ? minValue : value;
4553
+ });
4554
+ });
4449
4555
 
4450
- // ..........................................................
4451
- // BINDING
4452
- //
4453
- /** @private */
4454
- var K = function() {};
4556
+ You could specify this in your core.js file, for example. Then anywhere in
4557
+ your application you can use it to define bindings like so:
4455
4558
 
4456
- /** @private */
4457
- var Binding = function(toPath, fromPath) {
4458
- var self;
4559
+ valueBinding: Ember.Binding.from("MyApp.someController.value").notLessThan(10)
4459
4560
 
4460
- if (this instanceof Binding) {
4461
- self = this;
4462
- } else {
4463
- self = new K();
4464
- }
4561
+ Also, remember that helpers are chained so you can use your helper along
4562
+ with any other helpers. The example below will create a one way binding that
4563
+ does not allow empty values or values less than 10:
4465
4564
 
4466
- /** @private */
4467
- self._direction = 'fwd';
4565
+ valueBinding: Ember.Binding.oneWay("MyApp.someController.value").notEmpty().notLessThan(10)
4468
4566
 
4469
- /** @private */
4470
- self._from = fromPath;
4471
- self._to = toPath;
4567
+ Finally, it's also possible to specify bi-directional transforms. To do this,
4568
+ you can pass a hash to `transform` with `to` and `from`. In the following
4569
+ example, we are expecting a lowercase string that we want to transform to
4570
+ uppercase.
4472
4571
 
4473
- return self;
4474
- };
4572
+ valueBinding: Ember.Binding.transform({
4573
+ to: function(value, binding) { return value.toUpperCase(); },
4574
+ from: function(value, binding) { return value.toLowerCase(); }
4475
4575
 
4476
- K.prototype = Binding.prototype;
4576
+ ## How to Manually Adding Binding
4477
4577
 
4478
- Binding.prototype = /** @scope Ember.Binding.prototype */ {
4479
- // ..........................................................
4480
- // CONFIG
4481
- //
4578
+ All of the examples above show you how to configure a custom binding, but
4579
+ the result of these customizations will be a binding template, not a fully
4580
+ active binding. The binding will actually become active only when you
4581
+ instantiate the object the binding belongs to. It is useful however, to
4582
+ understand what actually happens when the binding is activated.
4482
4583
 
4483
- /**
4484
- This will set "from" property path to the specified value. It will not
4485
- attempt to resolve this property path to an actual object until you
4486
- connect the binding.
4584
+ For a binding to function it must have at least a "from" property and a "to"
4585
+ property. The from property path points to the object/key that you want to
4586
+ bind from while the to path points to the object/key you want to bind to.
4487
4587
 
4488
- The binding will search for the property path starting at the root object
4489
- you pass when you connect() the binding. It follows the same rules as
4490
- `getPath()` - see that method for more information.
4588
+ When you define a custom binding, you are usually describing the property
4589
+ you want to bind from (such as "MyApp.someController.value" in the examples
4590
+ above). When your object is created, it will automatically assign the value
4591
+ you want to bind "to" based on the name of your binding key. In the
4592
+ examples above, during init, Ember objects will effectively call
4593
+ something like this on your binding:
4491
4594
 
4492
- @param {String} propertyPath the property path to connect to
4493
- @returns {Ember.Binding} receiver
4494
- */
4495
- from: function(path) {
4496
- this._from = path;
4497
- return this;
4498
- },
4595
+ binding = Ember.Binding.from(this.valueBinding).to("value");
4499
4596
 
4500
- /**
4501
- This will set the "to" property path to the specified value. It will not
4502
- attempt to resolve this property path to an actual object until you
4503
- connect the binding.
4597
+ This creates a new binding instance based on the template you provide, and
4598
+ sets the to path to the "value" property of the new object. Now that the
4599
+ binding is fully configured with a "from" and a "to", it simply needs to be
4600
+ connected to become active. This is done through the connect() method:
4504
4601
 
4505
- The binding will search for the property path starting at the root object
4506
- you pass when you connect() the binding. It follows the same rules as
4507
- `getPath()` - see that method for more information.
4602
+ binding.connect(this);
4508
4603
 
4509
- @param {String|Tuple} propertyPath A property path or tuple
4510
- @param {Object} [root] Root object to use when resolving the path.
4511
- @returns {Ember.Binding} this
4512
- */
4513
- to: function(path) {
4514
- this._to = path;
4515
- return this;
4516
- },
4604
+ Note that when you connect a binding you pass the object you want it to be
4605
+ connected to. This object will be used as the root for both the from and
4606
+ to side of the binding when inspecting relative paths. This allows the
4607
+ binding to be automatically inherited by subclassed objects as well.
4517
4608
 
4518
- /**
4519
- Configures the binding as one way. A one-way binding will relay changes
4520
- on the "from" side to the "to" side, but not the other way around. This
4521
- means that if you change the "to" side directly, the "from" side may have
4522
- a different value.
4609
+ Now that the binding is connected, it will observe both the from and to side
4610
+ and relay changes.
4523
4611
 
4524
- @param {Boolean} flag
4525
- (Optional) passing nothing here will make the binding oneWay. You can
4526
- instead pass false to disable oneWay, making the binding two way again.
4612
+ If you ever needed to do so (you almost never will, but it is useful to
4613
+ understand this anyway), you could manually create an active binding by
4614
+ using the Ember.bind() helper method. (This is the same method used by
4615
+ to setup your bindings on objects):
4527
4616
 
4528
- @returns {Ember.Binding} receiver
4529
- */
4530
- oneWay: function(flag) {
4531
- this._oneWay = flag===undefined ? true : !!flag;
4532
- return this;
4533
- },
4617
+ Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value");
4534
4618
 
4535
- /**
4536
- Adds the specified transform to the array of transform functions.
4619
+ Both of these code fragments have the same effect as doing the most friendly
4620
+ form of binding creation like so:
4537
4621
 
4538
- A transform is a hash with `to` and `from` properties. Each property
4539
- should be a function that performs a transformation in either the
4540
- forward or back direction.
4622
+ MyApp.anotherObject = Ember.Object.create({
4623
+ valueBinding: "MyApp.someController.value",
4541
4624
 
4542
- The functions you pass must have the following signature:
4625
+ // OTHER CODE FOR THIS OBJECT...
4543
4626
 
4544
- function(value) {};
4627
+ });
4545
4628
 
4546
- They must also return the transformed value.
4629
+ Ember's built in binding creation method makes it easy to automatically
4630
+ create bindings for you. You should always use the highest-level APIs
4631
+ available, even if you understand how it works underneath.
4547
4632
 
4548
- Transforms are invoked in the order they were added. If you are
4549
- extending a binding and want to reset the transforms, you can call
4550
- `resetTransform()` first.
4633
+ @since Ember 0.9
4634
+ */
4635
+ Ember.Binding = Binding;
4551
4636
 
4552
- @param {Function} transformFunc the transform function.
4553
- @returns {Ember.Binding} this
4554
- */
4555
- transform: function(transform) {
4556
- if ('function' === typeof transform) {
4557
- transform = { to: transform };
4558
- }
4637
+ /**
4638
+ Global helper method to create a new binding. Just pass the root object
4639
+ along with a to and from path to create and connect the binding. The new
4640
+ binding object will be returned which you can further configure with
4641
+ transforms and other conditions.
4559
4642
 
4560
- if (!this._transforms) this._transforms = [];
4561
- this._transforms.push(transform);
4562
- return this;
4563
- },
4643
+ @param {Object} obj
4644
+ The root object of the transform.
4564
4645
 
4565
- /**
4566
- Resets the transforms for the binding. After calling this method the
4567
- binding will no longer transform values. You can then add new transforms
4568
- as needed.
4646
+ @param {String} to
4647
+ The path to the 'to' side of the binding. Must be relative to obj.
4569
4648
 
4570
- @returns {Ember.Binding} this
4571
- */
4572
- resetTransforms: function() {
4573
- this._transforms = null;
4574
- return this;
4575
- },
4649
+ @param {String} from
4650
+ The path to the 'from' side of the binding. Must be relative to obj or
4651
+ a global path.
4576
4652
 
4577
- /**
4578
- Adds a transform to the chain that will allow only single values to pass.
4579
- This will allow single values and nulls to pass through. If you pass an
4580
- array, it will be mapped as so:
4653
+ @returns {Ember.Binding} binding instance
4654
+ */
4655
+ Ember.bind = function(obj, to, from) {
4656
+ return new Ember.Binding(to, from).connect(obj);
4657
+ };
4581
4658
 
4582
- - [] => null
4583
- - [a] => a
4584
- - [a,b,c] => Multiple Placeholder
4659
+ Ember.oneWay = function(obj, to, from) {
4660
+ return new Ember.Binding(to, from).oneWay().connect(obj);
4661
+ };
4585
4662
 
4586
- You can pass in an optional multiple placeholder or it will use the
4587
- default.
4663
+ })();
4588
4664
 
4589
- Note that this transform will only happen on forwarded valued. Reverse
4590
- values are send unchanged.
4591
4665
 
4592
- @param {String} fromPath from path or null
4593
- @param {Object} [placeholder] Placeholder value.
4594
- @returns {Ember.Binding} this
4595
- */
4596
- single: function(placeholder) {
4597
- if (placeholder===undefined) placeholder = Ember.MULTIPLE_PLACEHOLDER;
4598
- this._typeTransform = SINGLE;
4599
- this._placeholder = placeholder;
4600
- return this;
4601
- },
4602
4666
 
4603
- /**
4604
- Adds a transform that will convert the passed value to an array. If
4605
- the value is null or undefined, it will be converted to an empty array.
4667
+ (function() {
4668
+ // ==========================================================================
4669
+ // Project: Ember Runtime
4670
+ // Copyright: ©2011 Strobe Inc. and contributors.
4671
+ // License: Licensed under MIT license (see license.js)
4672
+ // ==========================================================================
4673
+ var Mixin, MixinDelegate, REQUIRED, Alias;
4674
+ var classToString, superClassString;
4606
4675
 
4607
- @param {String} [fromPath]
4608
- @returns {Ember.Binding} this
4609
- */
4610
- multiple: function() {
4611
- this._typeTransform = MULTIPLE;
4612
- this._placeholder = null;
4613
- return this;
4614
- },
4676
+ var a_map = Ember.ArrayUtils.map;
4677
+ var a_indexOf = Ember.ArrayUtils.indexOf;
4678
+ var a_forEach = Ember.ArrayUtils.forEach;
4679
+ var a_slice = Array.prototype.slice;
4680
+ var EMPTY_META = {}; // dummy for non-writable meta
4681
+ var META_SKIP = { __emberproto__: true, __ember_count__: true };
4615
4682
 
4616
- /**
4617
- Adds a transform to convert the value to a bool value. If the value is
4618
- an array it will return true if array is not empty. If the value is a
4619
- string it will return true if the string is not empty.
4683
+ var o_create = Ember.platform.create;
4620
4684
 
4621
- @returns {Ember.Binding} this
4622
- */
4623
- bool: function() {
4624
- this.transform(BOOL);
4625
- return this;
4626
- },
4685
+ /** @private */
4686
+ function meta(obj, writable) {
4687
+ var m = Ember.meta(obj, writable!==false), ret = m.mixins;
4688
+ if (writable===false) return ret || EMPTY_META;
4627
4689
 
4628
- /**
4629
- Adds a transform that will return the placeholder value if the value is
4630
- null, undefined, an empty array or an empty string. See also notNull().
4690
+ if (!ret) {
4691
+ ret = m.mixins = { __emberproto__: obj };
4692
+ } else if (ret.__emberproto__ !== obj) {
4693
+ ret = m.mixins = o_create(ret);
4694
+ ret.__emberproto__ = obj;
4695
+ }
4696
+ return ret;
4697
+ }
4631
4698
 
4632
- @param {Object} [placeholder] Placeholder value.
4633
- @returns {Ember.Binding} this
4634
- */
4635
- notEmpty: function(placeholder) {
4636
- if (placeholder === null || placeholder === undefined) {
4637
- placeholder = Ember.EMPTY_PLACEHOLDER;
4638
- }
4699
+ /** @private */
4700
+ function initMixin(mixin, args) {
4701
+ if (args && args.length > 0) {
4702
+ mixin.mixins = a_map(args, function(x) {
4703
+ if (x instanceof Mixin) return x;
4639
4704
 
4640
- this.transform({
4641
- to: function(val) { return empty(val) ? placeholder : val; }
4705
+ // Note: Manually setup a primitive mixin here. This is the only
4706
+ // way to actually get a primitive mixin. This way normal creation
4707
+ // of mixins will give you combined mixins...
4708
+ var mixin = new Mixin();
4709
+ mixin.properties = x;
4710
+ return mixin;
4642
4711
  });
4712
+ }
4713
+ return mixin;
4714
+ }
4643
4715
 
4644
- return this;
4645
- },
4716
+ var NATIVES = [Boolean, Object, Number, Array, Date, String];
4717
+ /** @private */
4718
+ function isMethod(obj) {
4719
+ if ('function' !== typeof obj || obj.isMethod===false) return false;
4720
+ return a_indexOf(NATIVES, obj)<0;
4721
+ }
4646
4722
 
4647
- /**
4648
- Adds a transform that will return the placeholder value if the value is
4649
- null or undefined. Otherwise it will passthrough untouched. See also notEmpty().
4723
+ /** @private */
4724
+ function mergeMixins(mixins, m, descs, values, base) {
4725
+ var len = mixins.length, idx, mixin, guid, props, value, key, ovalue, concats;
4650
4726
 
4651
- @param {String} fromPath from path or null
4652
- @param {Object} [placeholder] Placeholder value.
4653
- @returns {Ember.Binding} this
4654
- */
4655
- notNull: function(placeholder) {
4656
- if (placeholder === null || placeholder === undefined) {
4657
- placeholder = Ember.EMPTY_PLACEHOLDER;
4658
- }
4727
+ /** @private */
4728
+ function removeKeys(keyName) {
4729
+ delete descs[keyName];
4730
+ delete values[keyName];
4731
+ }
4659
4732
 
4660
- this.transform({
4661
- to: function(val) { return (val === null || val === undefined) ? placeholder : val; }
4662
- });
4733
+ for(idx=0;idx<len;idx++) {
4663
4734
 
4664
- return this;
4665
- },
4735
+ mixin = mixins[idx];
4736
+ if (!mixin) throw new Error('Null value found in Ember.mixin()');
4666
4737
 
4667
- /**
4668
- Adds a transform to convert the value to the inverse of a bool value. This
4669
- uses the same transform as bool() but inverts it.
4738
+ if (mixin instanceof Mixin) {
4739
+ guid = Ember.guidFor(mixin);
4740
+ if (m[guid]) continue;
4741
+ m[guid] = mixin;
4742
+ props = mixin.properties;
4743
+ } else {
4744
+ props = mixin; // apply anonymous mixin properties
4745
+ }
4670
4746
 
4671
- @returns {Ember.Binding} this
4672
- */
4673
- not: function() {
4674
- this.transform(NOT);
4675
- return this;
4676
- },
4747
+ if (props) {
4677
4748
 
4678
- /**
4679
- Adds a transform that will return true if the value is null or undefined, false otherwise.
4749
+ // reset before adding each new mixin to pickup concats from previous
4750
+ concats = values.concatenatedProperties || base.concatenatedProperties;
4751
+ if (props.concatenatedProperties) {
4752
+ concats = concats ? concats.concat(props.concatenatedProperties) : props.concatenatedProperties;
4753
+ }
4680
4754
 
4681
- @returns {Ember.Binding} this
4682
- */
4683
- isNull: function() {
4684
- this.transform(function(val) { return val === null || val === undefined; });
4685
- return this;
4686
- },
4755
+ for (key in props) {
4756
+ if (!props.hasOwnProperty(key)) continue;
4757
+ value = props[key];
4758
+ if (value instanceof Ember.Descriptor) {
4759
+ if (value === REQUIRED && descs[key]) { continue; }
4687
4760
 
4688
- /** @private */
4689
- toString: function() {
4690
- var oneWay = this._oneWay ? '[oneWay]' : '';
4691
- return "Ember.Binding<" + guidFor(this) + ">(" + this._from + " -> " + this._to + ")" + oneWay;
4692
- },
4761
+ descs[key] = value;
4762
+ values[key] = undefined;
4763
+ } else {
4693
4764
 
4694
- // ..........................................................
4695
- // CONNECT AND SYNC
4696
- //
4765
+ // impl super if needed...
4766
+ if (isMethod(value)) {
4767
+ ovalue = (descs[key] === Ember.SIMPLE_PROPERTY) && values[key];
4768
+ if (!ovalue) ovalue = base[key];
4769
+ if ('function' !== typeof ovalue) ovalue = null;
4770
+ if (ovalue) {
4771
+ var o = value.__ember_observes__, ob = value.__ember_observesBefore__;
4772
+ value = Ember.wrap(value, ovalue);
4773
+ value.__ember_observes__ = o;
4774
+ value.__ember_observesBefore__ = ob;
4775
+ }
4776
+ } else if ((concats && a_indexOf(concats, key)>=0) || key === 'concatenatedProperties') {
4777
+ var baseValue = values[key] || base[key];
4778
+ value = baseValue ? baseValue.concat(value) : Ember.makeArray(value);
4779
+ }
4697
4780
 
4698
- /**
4699
- Attempts to connect this binding instance so that it can receive and relay
4700
- changes. This method will raise an exception if you have not set the
4701
- from/to properties yet.
4702
-
4703
- @param {Object} obj
4704
- The root object for this binding.
4781
+ descs[key] = Ember.SIMPLE_PROPERTY;
4782
+ values[key] = value;
4783
+ }
4784
+ }
4705
4785
 
4706
- @param {Boolean} preferFromParam
4707
- private: Normally, `connect` cannot take an object if `from` already set
4708
- an object. Internally, we would like to be able to provide a default object
4709
- to be used if no object was provided via `from`, so this parameter turns
4710
- off the assertion.
4786
+ // manually copy toString() because some JS engines do not enumerate it
4787
+ if (props.hasOwnProperty('toString')) {
4788
+ base.toString = props.toString;
4789
+ }
4711
4790
 
4712
- @returns {Ember.Binding} this
4713
- */
4714
- connect: function(obj) {
4791
+ } else if (mixin.mixins) {
4792
+ mergeMixins(mixin.mixins, m, descs, values, base);
4793
+ if (mixin._without) a_forEach(mixin._without, removeKeys);
4794
+ }
4795
+ }
4796
+ }
4715
4797
 
4798
+ /** @private */
4799
+ var defineProperty = Ember.defineProperty;
4716
4800
 
4717
- var oneWay = this._oneWay, operand = this._operand;
4801
+ /** @private */
4802
+ function writableReq(obj) {
4803
+ var m = Ember.meta(obj), req = m.required;
4804
+ if (!req || (req.__emberproto__ !== obj)) {
4805
+ req = m.required = req ? o_create(req) : { __ember_count__: 0 };
4806
+ req.__emberproto__ = obj;
4807
+ }
4808
+ return req;
4809
+ }
4718
4810
 
4719
- // add an observer on the object to be notified when the binding should be updated
4720
- Ember.addObserver(obj, this._from, this, this.fromDidChange);
4811
+ /** @private */
4812
+ function getObserverPaths(value) {
4813
+ return ('function' === typeof value) && value.__ember_observes__;
4814
+ }
4721
4815
 
4722
- // if there is an operand, add an observer onto it as well
4723
- if (operand) { Ember.addObserver(obj, operand, this, this.fromDidChange); }
4816
+ /** @private */
4817
+ function getBeforeObserverPaths(value) {
4818
+ return ('function' === typeof value) && value.__ember_observesBefore__;
4819
+ }
4724
4820
 
4725
- // if the binding is a two-way binding, also set up an observer on the target
4726
- // object.
4727
- if (!oneWay) { Ember.addObserver(obj, this._to, this, this.toDidChange); }
4821
+ var IS_BINDING = Ember.IS_BINDING = /^.+Binding$/;
4728
4822
 
4729
- if (Ember.meta(obj,false).proto !== obj) { this._scheduleSync(obj, 'fwd'); }
4823
+ function detectBinding(obj, key, m) {
4824
+ if (IS_BINDING.test(key)) {
4825
+ var bindings = m.bindings;
4826
+ if (!bindings) {
4827
+ bindings = m.bindings = { __emberproto__: obj };
4828
+ } else if (bindings.__emberproto__ !== obj) {
4829
+ bindings = m.bindings = o_create(m.bindings);
4830
+ bindings.__emberproto__ = obj;
4831
+ }
4832
+ bindings[key] = true;
4833
+ }
4834
+ }
4730
4835
 
4731
- this._readyToSync = true;
4732
- return this;
4733
- },
4836
+ function connectBindings(obj, m) {
4837
+ if (m === undefined) {
4838
+ m = Ember.meta(obj);
4839
+ }
4840
+ var bindings = m.bindings, key, binding;
4841
+ if (bindings) {
4842
+ for (key in bindings) {
4843
+ binding = key !== '__emberproto__' && obj[key];
4844
+ if (binding) {
4845
+ if (binding instanceof Ember.Binding) {
4846
+ binding = binding.copy(); // copy prototypes' instance
4847
+ binding.to(key.slice(0, -7));
4848
+ } else {
4849
+ binding = new Ember.Binding(key.slice(0,-7), binding);
4850
+ }
4851
+ binding.connect(obj);
4852
+ obj[key] = binding;
4853
+ }
4854
+ }
4855
+ }
4856
+ }
4734
4857
 
4735
- /**
4736
- Disconnects the binding instance. Changes will no longer be relayed. You
4737
- will not usually need to call this method.
4858
+ /** @private */
4859
+ function applyMixin(obj, mixins, partial) {
4860
+ var descs = {}, values = {}, m = Ember.meta(obj), req = m.required;
4861
+ var key, willApply, didApply, value, desc;
4738
4862
 
4739
- @param {Object} obj
4740
- The root object you passed when connecting the binding.
4863
+ // Go through all mixins and hashes passed in, and:
4864
+ //
4865
+ // * Handle concatenated properties
4866
+ // * Set up _super wrapping if necessary
4867
+ // * Set up descriptors (simple, watched or computed properties)
4868
+ // * Copying `toString` in broken browsers
4869
+ mergeMixins(mixins, meta(obj), descs, values, obj);
4741
4870
 
4742
- @returns {Ember.Binding} this
4743
- */
4744
- disconnect: function(obj) {
4871
+ if (MixinDelegate.detect(obj)) {
4872
+ willApply = values.willApplyProperty || obj.willApplyProperty;
4873
+ didApply = values.didApplyProperty || obj.didApplyProperty;
4874
+ }
4745
4875
 
4876
+ for(key in descs) {
4877
+ if (!descs.hasOwnProperty(key)) continue;
4746
4878
 
4747
- var oneWay = this._oneWay, operand = this._operand;
4879
+ desc = descs[key];
4880
+ value = values[key];
4748
4881
 
4749
- // remove an observer on the object so we're no longer notified of
4750
- // changes that should update bindings.
4751
- Ember.removeObserver(obj, this._from, this, this.fromDidChange);
4882
+ if (desc === REQUIRED) {
4883
+ if (!(key in obj)) {
4884
+ if (!partial) throw new Error('Required property not defined: '+key);
4752
4885
 
4753
- // if there is an operand, remove the observer from it as well
4754
- if (operand) Ember.removeObserver(obj, operand, this, this.fromDidChange);
4886
+ // for partial applies add to hash of required keys
4887
+ req = writableReq(obj);
4888
+ req.__ember_count__++;
4889
+ req[key] = true;
4890
+ }
4755
4891
 
4756
- // if the binding is two-way, remove the observer from the target as well
4757
- if (!oneWay) Ember.removeObserver(obj, this._to, this, this.toDidChange);
4892
+ } else {
4758
4893
 
4759
- this._readyToSync = false; // disable scheduled syncs...
4760
- return this;
4761
- },
4894
+ while (desc instanceof Alias) {
4762
4895
 
4763
- // ..........................................................
4764
- // PRIVATE
4765
- //
4896
+ var altKey = desc.methodName;
4897
+ if (descs[altKey]) {
4898
+ value = values[altKey];
4899
+ desc = descs[altKey];
4900
+ } else if (m.descs[altKey]) {
4901
+ desc = m.descs[altKey];
4902
+ value = desc.val(obj, altKey);
4903
+ } else {
4904
+ value = obj[altKey];
4905
+ desc = Ember.SIMPLE_PROPERTY;
4906
+ }
4907
+ }
4766
4908
 
4767
- /** @private - called when the from side changes */
4768
- fromDidChange: function(target) {
4769
- this._scheduleSync(target, 'fwd');
4770
- },
4909
+ if (willApply) willApply.call(obj, key);
4771
4910
 
4772
- /** @private - called when the to side changes */
4773
- toDidChange: function(target) {
4774
- this._scheduleSync(target, 'back');
4775
- },
4911
+ var observerPaths = getObserverPaths(value),
4912
+ curObserverPaths = observerPaths && getObserverPaths(obj[key]),
4913
+ beforeObserverPaths = getBeforeObserverPaths(value),
4914
+ curBeforeObserverPaths = beforeObserverPaths && getBeforeObserverPaths(obj[key]),
4915
+ len, idx;
4776
4916
 
4777
- /** @private */
4778
- _scheduleSync: function(obj, dir) {
4779
- var guid = guidFor(obj), existingDir = this[guid];
4917
+ if (curObserverPaths) {
4918
+ len = curObserverPaths.length;
4919
+ for(idx=0;idx<len;idx++) {
4920
+ Ember.removeObserver(obj, curObserverPaths[idx], null, key);
4921
+ }
4922
+ }
4780
4923
 
4781
- // if we haven't scheduled the binding yet, schedule it
4782
- if (!existingDir) {
4783
- Ember.run.schedule('sync', this, this._sync, obj);
4784
- this[guid] = dir;
4785
- }
4924
+ if (curBeforeObserverPaths) {
4925
+ len = curBeforeObserverPaths.length;
4926
+ for(idx=0;idx<len;idx++) {
4927
+ Ember.removeBeforeObserver(obj, curBeforeObserverPaths[idx], null,key);
4928
+ }
4929
+ }
4786
4930
 
4787
- // If both a 'back' and 'fwd' sync have been scheduled on the same object,
4788
- // default to a 'fwd' sync so that it remains deterministic.
4789
- if (existingDir === 'back' && dir === 'fwd') {
4790
- this[guid] = 'fwd';
4791
- }
4792
- },
4931
+ detectBinding(obj, key, m);
4793
4932
 
4794
- /** @private */
4795
- _sync: function(obj) {
4796
- var log = Ember.LOG_BINDINGS;
4933
+ defineProperty(obj, key, desc, value);
4797
4934
 
4798
- // don't synchronize destroyed objects or disconnected bindings
4799
- if (obj.isDestroyed || !this._readyToSync) { return; }
4935
+ if (observerPaths) {
4936
+ len = observerPaths.length;
4937
+ for(idx=0;idx<len;idx++) {
4938
+ Ember.addObserver(obj, observerPaths[idx], null, key);
4939
+ }
4940
+ }
4800
4941
 
4801
- // get the direction of the binding for the object we are
4802
- // synchronizing from
4803
- var guid = guidFor(obj), direction = this[guid];
4942
+ if (beforeObserverPaths) {
4943
+ len = beforeObserverPaths.length;
4944
+ for(idx=0;idx<len;idx++) {
4945
+ Ember.addBeforeObserver(obj, beforeObserverPaths[idx], null, key);
4946
+ }
4947
+ }
4804
4948
 
4805
- var fromPath = this._from, toPath = this._to;
4949
+ if (req && req[key]) {
4950
+ req = writableReq(obj);
4951
+ req.__ember_count__--;
4952
+ req[key] = false;
4953
+ }
4806
4954
 
4807
- delete this[guid];
4955
+ if (didApply) didApply.call(obj, key);
4808
4956
 
4809
- // if we're synchronizing from the remote object...
4810
- if (direction === 'fwd') {
4811
- var fromValue = getTransformedFromValue(obj, this);
4812
- if (log) {
4813
- Ember.Logger.log(' ', this.toString(), '->', fromValue, obj);
4814
- }
4815
- if (this._oneWay) {
4816
- Ember.trySetPath(Ember.isGlobalPath(toPath) ? window : obj, toPath, fromValue);
4817
- } else {
4818
- Ember._suspendObserver(obj, toPath, this, this.toDidChange, function () {
4819
- Ember.trySetPath(Ember.isGlobalPath(toPath) ? window : obj, toPath, fromValue);
4820
- });
4821
- }
4822
- // if we're synchronizing *to* the remote object
4823
- } else if (direction === 'back') {// && !this._oneWay) {
4824
- var toValue = getTransformedToValue(obj, this);
4825
- if (log) {
4826
- Ember.Logger.log(' ', this.toString(), '<-', toValue, obj);
4827
- }
4828
- Ember._suspendObserver(obj, fromPath, this, this.fromDidChange, function () {
4829
- Ember.trySetPath(Ember.isGlobalPath(fromPath) ? window : obj, fromPath, toValue);
4830
- });
4831
4957
  }
4832
4958
  }
4833
4959
 
4834
- };
4960
+ if (!partial) { // don't apply to prototype
4961
+ value = connectBindings(obj, m);
4962
+ }
4835
4963
 
4836
- /** @private */
4837
- function mixinProperties(to, from) {
4838
- for (var key in from) {
4839
- if (from.hasOwnProperty(key)) {
4840
- to[key] = from[key];
4964
+ // Make sure no required attrs remain
4965
+ if (!partial && req && req.__ember_count__>0) {
4966
+ var keys = [];
4967
+ for(key in req) {
4968
+ if (META_SKIP[key]) continue;
4969
+ keys.push(key);
4841
4970
  }
4971
+ throw new Error('Required properties not defined: '+keys.join(','));
4842
4972
  }
4973
+ return obj;
4843
4974
  }
4844
4975
 
4845
- mixinProperties(Binding,
4846
- /** @scope Ember.Binding */ {
4847
-
4848
- /**
4849
- @see Ember.Binding.prototype.from
4850
- */
4851
- from: function() {
4852
- var C = this, binding = new C();
4853
- return binding.from.apply(binding, arguments);
4854
- },
4855
-
4856
- /**
4857
- @see Ember.Binding.prototype.to
4858
- */
4859
- to: function() {
4860
- var C = this, binding = new C();
4861
- return binding.to.apply(binding, arguments);
4862
- },
4863
-
4864
- /**
4865
- @see Ember.Binding.prototype.oneWay
4866
- */
4867
- oneWay: function(from, flag) {
4868
- var C = this, binding = new C(null, from);
4869
- return binding.oneWay(flag);
4870
- },
4871
-
4872
- /**
4873
- @see Ember.Binding.prototype.single
4874
- */
4875
- single: function(from, placeholder) {
4876
- var C = this, binding = new C(null, from);
4877
- return binding.single(placeholder);
4878
- },
4879
-
4880
- /**
4881
- @see Ember.Binding.prototype.multiple
4882
- */
4883
- multiple: function(from) {
4884
- var C = this, binding = new C(null, from);
4885
- return binding.multiple();
4886
- },
4887
-
4888
- /**
4889
- @see Ember.Binding.prototype.transform
4890
- */
4891
- transform: function(from, func) {
4892
- if (!func) {
4893
- func = from;
4894
- from = null;
4895
- }
4896
- var C = this, binding = new C(null, from);
4897
- return binding.transform(func);
4898
- },
4899
-
4900
- /**
4901
- @see Ember.Binding.prototype.notEmpty
4902
- */
4903
- notEmpty: function(from, placeholder) {
4904
- var C = this, binding = new C(null, from);
4905
- return binding.notEmpty(placeholder);
4906
- },
4907
-
4908
- /**
4909
- @see Ember.Binding.prototype.notNull
4910
- */
4911
- notNull: function(from, placeholder) {
4912
- var C = this, binding = new C(null, from);
4913
- return binding.notNull(placeholder);
4914
- },
4915
-
4916
-
4917
- /**
4918
- @see Ember.Binding.prototype.bool
4919
- */
4920
- bool: function(from) {
4921
- var C = this, binding = new C(null, from);
4922
- return binding.bool();
4923
- },
4924
-
4925
- /**
4926
- @see Ember.Binding.prototype.not
4927
- */
4928
- not: function(from) {
4929
- var C = this, binding = new C(null, from);
4930
- return binding.not();
4931
- },
4976
+ Ember.mixin = function(obj) {
4977
+ var args = a_slice.call(arguments, 1);
4978
+ return applyMixin(obj, args, false);
4979
+ };
4932
4980
 
4933
- /**
4934
- @see Ember.Binding.prototype.isNull
4935
- */
4936
- isNull: function(from) {
4937
- var C = this, binding = new C(null, from);
4938
- return binding.isNull();
4939
- },
4940
4981
 
4941
- /**
4942
- Adds a transform that forwards the logical 'AND' of values at 'pathA' and
4943
- 'pathB' whenever either source changes. Note that the transform acts
4944
- strictly as a one-way binding, working only in the direction
4982
+ /**
4983
+ @constructor
4984
+ */
4985
+ Ember.Mixin = function() { return initMixin(this, arguments); };
4945
4986
 
4946
- 'pathA' AND 'pathB' --> value (value returned is the result of ('pathA' && 'pathB'))
4987
+ /** @private */
4988
+ Mixin = Ember.Mixin;
4947
4989
 
4948
- Usage example where a delete button's `isEnabled` value is determined by
4949
- whether something is selected in a list and whether the current user is
4950
- allowed to delete:
4990
+ /** @private */
4991
+ Mixin._apply = applyMixin;
4951
4992
 
4952
- deleteButton: Ember.ButtonView.design({
4953
- isEnabledBinding: Ember.Binding.and('MyApp.itemsController.hasSelection', 'MyApp.userController.canDelete')
4954
- })
4993
+ Mixin.applyPartial = function(obj) {
4994
+ var args = a_slice.call(arguments, 1);
4995
+ return applyMixin(obj, args, true);
4996
+ };
4955
4997
 
4956
- @param {String} pathA The first part of the conditional
4957
- @param {String} pathB The second part of the conditional
4958
- */
4959
- and: function(pathA, pathB) {
4960
- var C = this, binding = new C(null, pathA).oneWay();
4961
- binding._operand = pathB;
4962
- binding._operation = AND_OPERATION;
4963
- return binding;
4964
- },
4998
+ Mixin.finishPartial = function(obj) {
4999
+ connectBindings(obj);
5000
+ return obj;
5001
+ };
4965
5002
 
4966
- /**
4967
- Adds a transform that forwards the 'OR' of values at 'pathA' and
4968
- 'pathB' whenever either source changes. Note that the transform acts
4969
- strictly as a one-way binding, working only in the direction
5003
+ Mixin.create = function() {
5004
+ classToString.processed = false;
5005
+ var M = this;
5006
+ return initMixin(new M(), arguments);
5007
+ };
4970
5008
 
4971
- 'pathA' AND 'pathB' --> value (value returned is the result of ('pathA' || 'pathB'))
5009
+ Mixin.prototype.reopen = function() {
4972
5010
 
4973
- @param {String} pathA The first part of the conditional
4974
- @param {String} pathB The second part of the conditional
4975
- */
4976
- or: function(pathA, pathB) {
4977
- var C = this, binding = new C(null, pathA).oneWay();
4978
- binding._operand = pathB;
4979
- binding._operation = OR_OPERATION;
4980
- return binding;
4981
- }
5011
+ var mixin, tmp;
4982
5012
 
4983
- });
5013
+ if (this.properties) {
5014
+ mixin = Mixin.create();
5015
+ mixin.properties = this.properties;
5016
+ delete this.properties;
5017
+ this.mixins = [mixin];
5018
+ }
4984
5019
 
4985
- /**
4986
- @class
5020
+ var len = arguments.length, mixins = this.mixins, idx;
4987
5021
 
4988
- A binding simply connects the properties of two objects so that whenever the
4989
- value of one property changes, the other property will be changed also. You
4990
- do not usually work with Binding objects directly but instead describe
4991
- bindings in your class definition using something like:
5022
+ for(idx=0;idx<len;idx++) {
5023
+ mixin = arguments[idx];
5024
+ if (mixin instanceof Mixin) {
5025
+ mixins.push(mixin);
5026
+ } else {
5027
+ tmp = Mixin.create();
5028
+ tmp.properties = mixin;
5029
+ mixins.push(tmp);
5030
+ }
5031
+ }
4992
5032
 
4993
- valueBinding: "MyApp.someController.title"
5033
+ return this;
5034
+ };
4994
5035
 
4995
- This will create a binding from `MyApp.someController.title` to the `value`
4996
- property of your object instance automatically. Now the two values will be
4997
- kept in sync.
5036
+ var TMP_ARRAY = [];
5037
+ Mixin.prototype.apply = function(obj) {
5038
+ TMP_ARRAY[0] = this;
5039
+ var ret = applyMixin(obj, TMP_ARRAY, false);
5040
+ TMP_ARRAY.length=0;
5041
+ return ret;
5042
+ };
4998
5043
 
4999
- ## Customizing Your Bindings
5044
+ Mixin.prototype.applyPartial = function(obj) {
5045
+ TMP_ARRAY[0] = this;
5046
+ var ret = applyMixin(obj, TMP_ARRAY, true);
5047
+ TMP_ARRAY.length=0;
5048
+ return ret;
5049
+ };
5000
5050
 
5001
- In addition to synchronizing values, bindings can also perform some basic
5002
- transforms on values. These transforms can help to make sure the data fed
5003
- into one object always meets the expectations of that object regardless of
5004
- what the other object outputs.
5051
+ /** @private */
5052
+ function _detect(curMixin, targetMixin, seen) {
5053
+ var guid = Ember.guidFor(curMixin);
5005
5054
 
5006
- To customize a binding, you can use one of the many helper methods defined
5007
- on Ember.Binding like so:
5055
+ if (seen[guid]) return false;
5056
+ seen[guid] = true;
5008
5057
 
5009
- valueBinding: Ember.Binding.single("MyApp.someController.title")
5058
+ if (curMixin === targetMixin) return true;
5059
+ var mixins = curMixin.mixins, loc = mixins ? mixins.length : 0;
5060
+ while(--loc >= 0) {
5061
+ if (_detect(mixins[loc], targetMixin, seen)) return true;
5062
+ }
5063
+ return false;
5064
+ }
5010
5065
 
5011
- This will create a binding just like the example above, except that now the
5012
- binding will convert the value of `MyApp.someController.title` to a single
5013
- object (removing any arrays) before applying it to the `value` property of
5014
- your object.
5066
+ Mixin.prototype.detect = function(obj) {
5067
+ if (!obj) return false;
5068
+ if (obj instanceof Mixin) return _detect(obj, this, {});
5069
+ return !!meta(obj, false)[Ember.guidFor(this)];
5070
+ };
5015
5071
 
5016
- You can also chain helper methods to build custom bindings like so:
5072
+ Mixin.prototype.without = function() {
5073
+ var ret = new Mixin(this);
5074
+ ret._without = a_slice.call(arguments);
5075
+ return ret;
5076
+ };
5017
5077
 
5018
- valueBinding: Ember.Binding.single("MyApp.someController.title").notEmpty("(EMPTY)")
5078
+ /** @private */
5079
+ function _keys(ret, mixin, seen) {
5080
+ if (seen[Ember.guidFor(mixin)]) return;
5081
+ seen[Ember.guidFor(mixin)] = true;
5019
5082
 
5020
- This will force the value of MyApp.someController.title to be a single value
5021
- and then check to see if the value is "empty" (null, undefined, empty array,
5022
- or an empty string). If it is empty, the value will be set to the string
5023
- "(EMPTY)".
5083
+ if (mixin.properties) {
5084
+ var props = mixin.properties;
5085
+ for(var key in props) {
5086
+ if (props.hasOwnProperty(key)) ret[key] = true;
5087
+ }
5088
+ } else if (mixin.mixins) {
5089
+ a_forEach(mixin.mixins, function(x) { _keys(ret, x, seen); });
5090
+ }
5091
+ }
5024
5092
 
5025
- ## One Way Bindings
5093
+ Mixin.prototype.keys = function() {
5094
+ var keys = {}, seen = {}, ret = [];
5095
+ _keys(keys, this, seen);
5096
+ for(var key in keys) {
5097
+ if (keys.hasOwnProperty(key)) ret.push(key);
5098
+ }
5099
+ return ret;
5100
+ };
5026
5101
 
5027
- One especially useful binding customization you can use is the `oneWay()`
5028
- helper. This helper tells Ember that you are only interested in
5029
- receiving changes on the object you are binding from. For example, if you
5030
- are binding to a preference and you want to be notified if the preference
5031
- has changed, but your object will not be changing the preference itself, you
5032
- could do:
5102
+ /** @private - make Mixin's have nice displayNames */
5033
5103
 
5034
- bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles")
5104
+ var NAME_KEY = Ember.GUID_KEY+'_name';
5105
+ var get = Ember.get;
5035
5106
 
5036
- This way if the value of MyApp.preferencesController.bigTitles changes the
5037
- "bigTitles" property of your object will change also. However, if you
5038
- change the value of your "bigTitles" property, it will not update the
5039
- preferencesController.
5107
+ /** @private */
5108
+ function processNames(paths, root, seen) {
5109
+ var idx = paths.length;
5110
+ for(var key in root) {
5111
+ if (!root.hasOwnProperty || !root.hasOwnProperty(key)) continue;
5112
+ var obj = root[key];
5113
+ paths[idx] = key;
5040
5114
 
5041
- One way bindings are almost twice as fast to setup and twice as fast to
5042
- execute because the binding only has to worry about changes to one side.
5115
+ if (obj && obj.toString === classToString) {
5116
+ obj[NAME_KEY] = paths.join('.');
5117
+ } else if (obj && get(obj, 'isNamespace')) {
5118
+ if (seen[Ember.guidFor(obj)]) continue;
5119
+ seen[Ember.guidFor(obj)] = true;
5120
+ processNames(paths, obj, seen);
5121
+ }
5043
5122
 
5044
- You should consider using one way bindings anytime you have an object that
5045
- may be created frequently and you do not intend to change a property; only
5046
- to monitor it for changes. (such as in the example above).
5123
+ }
5124
+ paths.length = idx; // cut out last item
5125
+ }
5047
5126
 
5048
- ## Adding Custom Transforms
5127
+ /** @private */
5128
+ function findNamespaces() {
5129
+ var Namespace = Ember.Namespace, obj, isNamespace;
5049
5130
 
5050
- In addition to using the standard helpers provided by Ember, you can
5051
- also defined your own custom transform functions which will be used to
5052
- convert the value. To do this, just define your transform function and add
5053
- it to the binding with the transform() helper. The following example will
5054
- not allow Integers less than ten. Note that it checks the value of the
5055
- bindings and allows all other values to pass:
5131
+ if (Namespace.PROCESSED) { return; }
5056
5132
 
5057
- valueBinding: Ember.Binding.transform(function(value, binding) {
5058
- return ((Ember.typeOf(value) === 'number') && (value < 10)) ? 10 : value;
5059
- }).from("MyApp.someController.value")
5133
+ for (var prop in window) {
5134
+ // get(window.globalStorage, 'isNamespace') would try to read the storage for domain isNamespace and cause exception in Firefox.
5135
+ // globalStorage is a storage obsoleted by the WhatWG storage specification. See https://developer.mozilla.org/en/DOM/Storage#globalStorage
5136
+ if (prop === "globalStorage" && window.StorageList && window.globalStorage instanceof window.StorageList) { continue; }
5137
+ // Unfortunately, some versions of IE don't support window.hasOwnProperty
5138
+ if (window.hasOwnProperty && !window.hasOwnProperty(prop)) { continue; }
5060
5139
 
5061
- If you would like to instead use this transform on a number of bindings,
5062
- you can also optionally add your own helper method to Ember.Binding. This
5063
- method should simply return the value of `this.transform()`. The example
5064
- below adds a new helper called `notLessThan()` which will limit the value to
5065
- be not less than the passed minimum:
5140
+ // At times we are not allowed to access certain properties for security reasons.
5141
+ // There are also times where even if we can access them, we are not allowed to access their properties.
5142
+ try {
5143
+ obj = window[prop];
5144
+ isNamespace = obj && get(obj, 'isNamespace');
5145
+ } catch (e) {
5146
+ continue;
5147
+ }
5066
5148
 
5067
- Ember.Binding.reopen({
5068
- notLessThan: function(minValue) {
5069
- return this.transform(function(value, binding) {
5070
- return ((Ember.typeOf(value) === 'number') && (value < minValue)) ? minValue : value;
5071
- });
5072
- }
5073
- });
5149
+ if (isNamespace) {
5074
5150
 
5075
- You could specify this in your core.js file, for example. Then anywhere in
5076
- your application you can use it to define bindings like so:
5151
+ obj[NAME_KEY] = prop;
5152
+ }
5153
+ }
5154
+ }
5077
5155
 
5078
- valueBinding: Ember.Binding.from("MyApp.someController.value").notLessThan(10)
5156
+ Ember.identifyNamespaces = findNamespaces;
5079
5157
 
5080
- Also, remember that helpers are chained so you can use your helper along
5081
- with any other helpers. The example below will create a one way binding that
5082
- does not allow empty values or values less than 10:
5158
+ /** @private */
5159
+ superClassString = function(mixin) {
5160
+ var superclass = mixin.superclass;
5161
+ if (superclass) {
5162
+ if (superclass[NAME_KEY]) { return superclass[NAME_KEY]; }
5163
+ else { return superClassString(superclass); }
5164
+ } else {
5165
+ return;
5166
+ }
5167
+ };
5083
5168
 
5084
- valueBinding: Ember.Binding.oneWay("MyApp.someController.value").notEmpty().notLessThan(10)
5169
+ /** @private */
5170
+ classToString = function() {
5171
+ var Namespace = Ember.Namespace, namespace;
5085
5172
 
5086
- Finally, it's also possible to specify bi-directional transforms. To do this,
5087
- you can pass a hash to `transform` with `to` and `from`. In the following
5088
- example, we are expecting a lowercase string that we want to transform to
5089
- uppercase.
5173
+ // TODO: Namespace should really be in Metal
5174
+ if (Namespace) {
5175
+ if (!this[NAME_KEY] && !classToString.processed) {
5176
+ if (!Namespace.PROCESSED) {
5177
+ findNamespaces();
5178
+ Namespace.PROCESSED = true;
5179
+ }
5090
5180
 
5091
- valueBinding: Ember.Binding.transform({
5092
- to: function(value, binding) { return value.toUpperCase(); },
5093
- from: function(value, binding) { return value.toLowerCase(); }
5181
+ classToString.processed = true;
5094
5182
 
5095
- ## How to Manually Adding Binding
5183
+ var namespaces = Namespace.NAMESPACES;
5184
+ for (var i=0, l=namespaces.length; i<l; i++) {
5185
+ namespace = namespaces[i];
5186
+ processNames([namespace.toString()], namespace, {});
5187
+ }
5188
+ }
5189
+ }
5096
5190
 
5097
- All of the examples above show you how to configure a custom binding, but
5098
- the result of these customizations will be a binding template, not a fully
5099
- active binding. The binding will actually become active only when you
5100
- instantiate the object the binding belongs to. It is useful however, to
5101
- understand what actually happens when the binding is activated.
5191
+ if (this[NAME_KEY]) {
5192
+ return this[NAME_KEY];
5193
+ } else {
5194
+ var str = superClassString(this);
5195
+ if (str) {
5196
+ return "(subclass of " + str + ")";
5197
+ } else {
5198
+ return "(unknown mixin)";
5199
+ }
5200
+ }
5201
+ };
5102
5202
 
5103
- For a binding to function it must have at least a "from" property and a "to"
5104
- property. The from property path points to the object/key that you want to
5105
- bind from while the to path points to the object/key you want to bind to.
5203
+ Mixin.prototype.toString = classToString;
5106
5204
 
5107
- When you define a custom binding, you are usually describing the property
5108
- you want to bind from (such as "MyApp.someController.value" in the examples
5109
- above). When your object is created, it will automatically assign the value
5110
- you want to bind "to" based on the name of your binding key. In the
5111
- examples above, during init, Ember objects will effectively call
5112
- something like this on your binding:
5205
+ // returns the mixins currently applied to the specified object
5206
+ // TODO: Make Ember.mixin
5207
+ Mixin.mixins = function(obj) {
5208
+ var ret = [], mixins = meta(obj, false), key, mixin;
5209
+ for(key in mixins) {
5210
+ if (META_SKIP[key]) continue;
5211
+ mixin = mixins[key];
5113
5212
 
5114
- binding = Ember.Binding.from(this.valueBinding).to("value");
5213
+ // skip primitive mixins since these are always anonymous
5214
+ if (!mixin.properties) ret.push(mixins[key]);
5215
+ }
5216
+ return ret;
5217
+ };
5115
5218
 
5116
- This creates a new binding instance based on the template you provide, and
5117
- sets the to path to the "value" property of the new object. Now that the
5118
- binding is fully configured with a "from" and a "to", it simply needs to be
5119
- connected to become active. This is done through the connect() method:
5219
+ REQUIRED = new Ember.Descriptor();
5220
+ REQUIRED.toString = function() { return '(Required Property)'; };
5120
5221
 
5121
- binding.connect(this);
5222
+ Ember.required = function() {
5223
+ return REQUIRED;
5224
+ };
5122
5225
 
5123
- Note that when you connect a binding you pass the object you want it to be
5124
- connected to. This object will be used as the root for both the from and
5125
- to side of the binding when inspecting relative paths. This allows the
5126
- binding to be automatically inherited by subclassed objects as well.
5226
+ /** @private */
5227
+ Alias = function(methodName) {
5228
+ this.methodName = methodName;
5229
+ };
5230
+ Alias.prototype = new Ember.Descriptor();
5127
5231
 
5128
- Now that the binding is connected, it will observe both the from and to side
5129
- and relay changes.
5232
+ Ember.alias = function(methodName) {
5233
+ return new Alias(methodName);
5234
+ };
5130
5235
 
5131
- If you ever needed to do so (you almost never will, but it is useful to
5132
- understand this anyway), you could manually create an active binding by
5133
- using the Ember.bind() helper method. (This is the same method used by
5134
- to setup your bindings on objects):
5236
+ Ember.MixinDelegate = Mixin.create({
5135
5237
 
5136
- Ember.bind(MyApp.anotherObject, "value", "MyApp.someController.value");
5238
+ willApplyProperty: Ember.required(),
5239
+ didApplyProperty: Ember.required()
5137
5240
 
5138
- Both of these code fragments have the same effect as doing the most friendly
5139
- form of binding creation like so:
5241
+ });
5140
5242
 
5141
- MyApp.anotherObject = Ember.Object.create({
5142
- valueBinding: "MyApp.someController.value",
5243
+ /** @private */
5244
+ MixinDelegate = Ember.MixinDelegate;
5143
5245
 
5144
- // OTHER CODE FOR THIS OBJECT...
5145
5246
 
5146
- });
5247
+ // ..........................................................
5248
+ // OBSERVER HELPER
5249
+ //
5147
5250
 
5148
- Ember's built in binding creation method makes it easy to automatically
5149
- create bindings for you. You should always use the highest-level APIs
5150
- available, even if you understand how to it works underneath.
5251
+ Ember.observer = function(func) {
5252
+ var paths = a_slice.call(arguments, 1);
5253
+ func.__ember_observes__ = paths;
5254
+ return func;
5255
+ };
5151
5256
 
5152
- @since Ember 0.9
5153
- */
5154
- Ember.Binding = Binding;
5257
+ Ember.beforeObserver = function(func) {
5258
+ var paths = a_slice.call(arguments, 1);
5259
+ func.__ember_observesBefore__ = paths;
5260
+ return func;
5261
+ };
5155
5262
 
5156
- /**
5157
- Global helper method to create a new binding. Just pass the root object
5158
- along with a to and from path to create and connect the binding. The new
5159
- binding object will be returned which you can further configure with
5160
- transforms and other conditions.
5161
5263
 
5162
- @param {Object} obj
5163
- The root object of the transform.
5164
5264
 
5165
- @param {String} to
5166
- The path to the 'to' side of the binding. Must be relative to obj.
5167
5265
 
5168
- @param {String} from
5169
- The path to the 'from' side of the binding. Must be relative to obj or
5170
- a global path.
5171
5266
 
5172
- @returns {Ember.Binding} binding instance
5173
- */
5174
- Ember.bind = function(obj, to, from) {
5175
- return new Ember.Binding(to, from).connect(obj);
5176
- };
5177
5267
 
5178
- Ember.oneWay = function(obj, to, from) {
5179
- return new Ember.Binding(to, from).oneWay().connect(obj);
5180
- };
5181
5268
 
5182
5269
  })();
5183
5270
 
@@ -5233,20 +5320,9 @@ Ember.oneWay = function(obj, to, from) {
5233
5320
  // Copyright: ©2011 Strobe Inc. and contributors.
5234
5321
  // License: Licensed under MIT license (see license.js)
5235
5322
  // ==========================================================================
5236
- /*globals ENV ember_assert */
5323
+ /*globals ENV */
5237
5324
  var indexOf = Ember.ArrayUtils.indexOf;
5238
5325
 
5239
- // ........................................
5240
- // GLOBAL CONSTANTS
5241
- //
5242
-
5243
- // ensure no undefined errors in browsers where console doesn't exist
5244
- if (typeof console === 'undefined') {
5245
- window.console = {};
5246
- console.log = console.info = console.warn = console.error = function() {};
5247
- }
5248
-
5249
-
5250
5326
  // ........................................
5251
5327
  // TYPING & ARRAY MESSAGING
5252
5328
  //
@@ -5935,7 +6011,10 @@ if (Ember.EXTEND_PROTOTYPES) {
5935
6011
  });
5936
6012
 
5937
6013
  Make sure you list these dependencies so Ember.js knows when to update
5938
- bindings that connect to a computed property.
6014
+ bindings that connect to a computed property. Changing a dependency
6015
+ will not immediately trigger an update of the computed property, but
6016
+ will instead clear the cache so that it is updated when the next `get`
6017
+ is called on the property.
5939
6018
 
5940
6019
  Note: you will usually want to use `property(...)` with `cacheable()`.
5941
6020
 
@@ -5996,44 +6075,6 @@ if (Ember.EXTEND_PROTOTYPES) {
5996
6075
 
5997
6076
 
5998
6077
 
5999
- (function() {
6000
- // ==========================================================================
6001
- // Project: Ember Runtime
6002
- // Copyright: ©2006-2011 Strobe Inc. and contributors.
6003
- // Portions ©2008-2011 Apple Inc. All rights reserved.
6004
- // License: Licensed under MIT license (see license.js)
6005
- // ==========================================================================
6006
- var IS_BINDING = Ember.IS_BINDING = /^.+Binding$/;
6007
-
6008
- Ember._mixinBindings = function(obj, key, value, m) {
6009
- if (IS_BINDING.test(key)) {
6010
- if (!(value instanceof Ember.Binding)) {
6011
- value = new Ember.Binding(key.slice(0,-7), value); // make binding
6012
- } else {
6013
- value.to(key.slice(0, -7));
6014
- }
6015
- value.connect(obj);
6016
-
6017
- // keep a set of bindings in the meta so that when we rewatch we can
6018
- // resync them...
6019
- var bindings = m.bindings;
6020
- if (!bindings) {
6021
- bindings = m.bindings = { __emberproto__: obj };
6022
- } else if (bindings.__emberproto__ !== obj) {
6023
- bindings = m.bindings = Ember.create(m.bindings);
6024
- bindings.__emberproto__ = obj;
6025
- }
6026
-
6027
- bindings[key] = true;
6028
- }
6029
-
6030
- return value;
6031
- };
6032
-
6033
- })();
6034
-
6035
-
6036
-
6037
6078
  (function() {
6038
6079
  // ==========================================================================
6039
6080
  // Project: Ember Runtime
@@ -6187,14 +6228,13 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6187
6228
  */
6188
6229
  firstObject: Ember.computed(function() {
6189
6230
  if (get(this, 'length')===0) return undefined ;
6190
- if (Ember.Array && Ember.Array.detect(this)) return this.objectAt(0);
6191
6231
 
6192
6232
  // handle generic enumerables
6193
6233
  var context = popCtx(), ret;
6194
6234
  ret = this.nextObject(0, null, context);
6195
6235
  pushCtx(context);
6196
6236
  return ret ;
6197
- }).property(),
6237
+ }).property('[]').cacheable(),
6198
6238
 
6199
6239
  /**
6200
6240
  Helper method returns the last object from a collection. If your enumerable
@@ -6212,18 +6252,14 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6212
6252
  lastObject: Ember.computed(function() {
6213
6253
  var len = get(this, 'length');
6214
6254
  if (len===0) return undefined ;
6215
- if (Ember.Array && Ember.Array.detect(this)) {
6216
- return this.objectAt(len-1);
6217
- } else {
6218
- var context = popCtx(), idx=0, cur, last = null;
6219
- do {
6220
- last = cur;
6221
- cur = this.nextObject(idx++, last, context);
6222
- } while (cur !== undefined);
6223
- pushCtx(context);
6224
- return last;
6225
- }
6226
- }).property(),
6255
+ var context = popCtx(), idx=0, cur, last = null;
6256
+ do {
6257
+ last = cur;
6258
+ cur = this.nextObject(idx++, last, context);
6259
+ } while (cur !== undefined);
6260
+ pushCtx(context);
6261
+ return last;
6262
+ }).property('[]').cacheable(),
6227
6263
 
6228
6264
  /**
6229
6265
  Returns true if the passed object can be found in the receiver. The
@@ -6758,6 +6794,7 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6758
6794
  if (removing === -1) removing = null;
6759
6795
  if (adding === -1) adding = null;
6760
6796
 
6797
+ Ember.propertyWillChange(this, '[]');
6761
6798
  if (hasDelta) Ember.propertyWillChange(this, 'length');
6762
6799
  Ember.sendEvent(this, '@enumerable:before', removing, adding);
6763
6800
 
@@ -6775,15 +6812,13 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6775
6812
  optional start offset for the content change. For unordered
6776
6813
  enumerables, you should always pass -1.
6777
6814
 
6778
- @param {Enumerable} added
6779
- optional enumerable containing items that were added to the set. For
6780
- ordered enumerables, this should be an ordered array of items. If no
6781
- items were added you can pass null.
6815
+ @param {Ember.Enumerable|Number} removing
6816
+ An enumerable of the objects to be removed or the number of items to
6817
+ be removed.
6782
6818
 
6783
- @param {Enumerable} removes
6784
- optional enumerable containing items that were removed from the set.
6785
- For ordered enumerables, this should be an ordered array of items. If
6786
- no items were removed you can pass null.
6819
+ @param {Ember.Enumerable|Numbe} adding
6820
+ An enumerable of the objects to be added or the number of items to be
6821
+ added.
6787
6822
 
6788
6823
  @returns {Object} receiver
6789
6824
  */
@@ -6805,6 +6840,7 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6805
6840
 
6806
6841
  Ember.sendEvent(this, '@enumerable:change', removing, adding);
6807
6842
  if (hasDelta) Ember.propertyDidChange(this, 'length');
6843
+ Ember.propertyDidChange(this, '[]');
6808
6844
 
6809
6845
  return this ;
6810
6846
  }
@@ -6828,7 +6864,7 @@ Ember.Enumerable = Ember.Mixin.create( /** @lends Ember.Enumerable */ {
6828
6864
  // HELPERS
6829
6865
  //
6830
6866
 
6831
- var get = Ember.get, set = Ember.set, meta = Ember.meta, map = Ember.ArrayUtils.map;
6867
+ var get = Ember.get, set = Ember.set, meta = Ember.meta, map = Ember.ArrayUtils.map, cacheFor = Ember.cacheFor;
6832
6868
 
6833
6869
  /** @private */
6834
6870
  function none(obj) { return obj===null || obj===undefined; }
@@ -6931,6 +6967,14 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
6931
6967
  return this ;
6932
6968
  }).property().cacheable(),
6933
6969
 
6970
+ firstObject: Ember.computed(function() {
6971
+ return this.objectAt(0);
6972
+ }).property().cacheable(),
6973
+
6974
+ lastObject: Ember.computed(function() {
6975
+ return this.objectAt(get(this, 'length')-1);
6976
+ }).property().cacheable(),
6977
+
6934
6978
  /** @private (nodoc) - optimized version from Enumerable */
6935
6979
  contains: function(obj){
6936
6980
  return this.indexOf(obj) >= 0;
@@ -7121,8 +7165,8 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
7121
7165
  startIdx = 0;
7122
7166
  removeAmt = addAmt = -1;
7123
7167
  } else {
7124
- if (!removeAmt) removeAmt=0;
7125
- if (!addAmt) addAmt=0;
7168
+ if (removeAmt === undefined) removeAmt=-1;
7169
+ if (addAmt === undefined) addAmt=-1;
7126
7170
  }
7127
7171
 
7128
7172
  Ember.sendEvent(this, '@array:before', startIdx, removeAmt, addAmt);
@@ -7140,6 +7184,7 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
7140
7184
 
7141
7185
  // Make sure the @each proxy is set up if anyone is observing @each
7142
7186
  if (Ember.isWatching(this, '@each')) { get(this, '@each'); }
7187
+
7143
7188
  return this;
7144
7189
  },
7145
7190
 
@@ -7150,8 +7195,8 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
7150
7195
  startIdx = 0;
7151
7196
  removeAmt = addAmt = -1;
7152
7197
  } else {
7153
- if (!removeAmt) removeAmt=0;
7154
- if (!addAmt) addAmt=0;
7198
+ if (removeAmt === undefined) removeAmt=-1;
7199
+ if (addAmt === undefined) addAmt=-1;
7155
7200
  }
7156
7201
 
7157
7202
  var adding, lim;
@@ -7165,6 +7210,19 @@ Ember.Array = Ember.Mixin.create(Ember.Enumerable, /** @scope Ember.Array.protot
7165
7210
 
7166
7211
  this.enumerableContentDidChange(removeAmt, adding);
7167
7212
  Ember.sendEvent(this, '@array:change', startIdx, removeAmt, addAmt);
7213
+
7214
+ var length = get(this, 'length'),
7215
+ cachedFirst = cacheFor(this, 'firstObject'),
7216
+ cachedLast = cacheFor(this, 'lastObject');
7217
+ if (this.objectAt(0) !== cachedFirst) {
7218
+ Ember.propertyWillChange(this, 'firstObject');
7219
+ Ember.propertyDidChange(this, 'firstObject');
7220
+ }
7221
+ if (this.objectAt(length-1) !== cachedLast) {
7222
+ Ember.propertyWillChange(this, 'lastObject');
7223
+ Ember.propertyDidChange(this, 'lastObject');
7224
+ }
7225
+
7168
7226
  return this;
7169
7227
  },
7170
7228
 
@@ -7631,7 +7689,7 @@ Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,
7631
7689
  colors.removeAt(2, 2); => ["green", "blue"]
7632
7690
  colors.removeAt(4, 2); => Error: Index out of range
7633
7691
 
7634
- @param {Number|Ember.IndexSet} start index, start of range, or index set
7692
+ @param {Number} start index, start of range
7635
7693
  @param {Number} len length of passing range
7636
7694
  @returns {Object} receiver
7637
7695
  */
@@ -7743,9 +7801,7 @@ Ember.MutableArray = Ember.Mixin.create(Ember.Array, Ember.MutableEnumerable,
7743
7801
  @returns {Ember.Array} receiver
7744
7802
  */
7745
7803
  unshiftObjects: function(objects) {
7746
- this.beginPropertyChanges();
7747
- forEach(objects, function(obj) { this.unshiftObject(obj); }, this);
7748
- this.endPropertyChanges();
7804
+ this.replace(0, 0, objects);
7749
7805
  return this;
7750
7806
  },
7751
7807
 
@@ -7840,6 +7896,11 @@ var get = Ember.get, set = Ember.set;
7840
7896
  This will call the `targetAction` method on the `targetObject` to be called
7841
7897
  whenever the value of the `propertyKey` changes.
7842
7898
 
7899
+ Note that if `propertyKey` is a computed property, the observer will be
7900
+ called when any of the property dependencies are changed, even if the
7901
+ resulting value of the computed property is unchanged. This is necessary
7902
+ because computed properties are not computed until `get` is called.
7903
+
7843
7904
  @extends Ember.Mixin
7844
7905
  */
7845
7906
  Ember.Observable = Ember.Mixin.create(/** @scope Ember.Observable.prototype */ {
@@ -8292,8 +8353,7 @@ Ember.TargetActionSupport = Ember.Mixin.create({
8292
8353
  var target = get(this, 'target');
8293
8354
 
8294
8355
  if (Ember.typeOf(target) === "string") {
8295
- // TODO: Remove the false when deprecation is done
8296
- var value = getPath(this, target, false);
8356
+ var value = getPath(this, target);
8297
8357
  if (value === undefined) { value = getPath(window, target); }
8298
8358
  return value;
8299
8359
  } else {
@@ -8409,6 +8469,7 @@ function makeCtor() {
8409
8469
  this.reopen.apply(this, initMixins);
8410
8470
  initMixins = null;
8411
8471
  rewatch(this); // always rewatch just in case
8472
+ Ember.Mixin.finishPartial(this);
8412
8473
  this.init.apply(this, arguments);
8413
8474
  } else {
8414
8475
  if (hasChains) {
@@ -8420,6 +8481,7 @@ function makeCtor() {
8420
8481
  if (init===false) { init = this.init; } // cache for later instantiations
8421
8482
  Ember.GUID_DESC.value = undefined;
8422
8483
  o_defineProperty(this, '_super', Ember.GUID_DESC);
8484
+ Ember.Mixin.finishPartial(this);
8423
8485
  init.apply(this, arguments);
8424
8486
  }
8425
8487
  };
@@ -8671,11 +8733,8 @@ var get = Ember.get, set = Ember.set, guidFor = Ember.guidFor, none = Ember.none
8671
8733
  can also iterate through a set just like an array, even accessing objects
8672
8734
  by index, however there is no guarantee as to their order.
8673
8735
 
8674
- Starting with Ember 2.0 all Sets are now observable since there is no
8675
- added cost to providing this support. Sets also do away with the more
8676
- specialized Set Observer API in favor of the more generic Enumerable
8677
- Observer API - which works on any enumerable object including both Sets and
8678
- Arrays.
8736
+ All Sets are observable via the Enumerable Observer API - which works
8737
+ on any enumerable object including both Sets and Arrays.
8679
8738
 
8680
8739
  ## Creating a Set
8681
8740
 
@@ -8789,16 +8848,28 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb
8789
8848
  */
8790
8849
  clear: function() {
8791
8850
  if (this.isFrozen) { throw new Error(Ember.FROZEN_ERROR); }
8851
+
8792
8852
  var len = get(this, 'length');
8853
+ if (len === 0) { return this; }
8854
+
8793
8855
  var guid;
8856
+
8794
8857
  this.enumerableContentWillChange(len, 0);
8858
+ Ember.propertyWillChange(this, 'firstObject');
8859
+ Ember.propertyWillChange(this, 'lastObject');
8860
+
8795
8861
  for (var i=0; i < len; i++){
8796
8862
  guid = guidFor(this[i]);
8797
8863
  delete this[guid];
8798
8864
  delete this[i];
8799
8865
  }
8866
+
8800
8867
  set(this, 'length', 0);
8868
+
8869
+ Ember.propertyDidChange(this, 'firstObject');
8870
+ Ember.propertyDidChange(this, 'lastObject');
8801
8871
  this.enumerableContentDidChange(len, 0);
8872
+
8802
8873
  return this;
8803
8874
  },
8804
8875
 
@@ -8866,7 +8937,7 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb
8866
8937
 
8867
8938
  /**
8868
8939
  Removes the last element from the set and returns it, or null if it's empty.
8869
-
8940
+
8870
8941
  var colors = new Ember.Set(["green", "blue"]);
8871
8942
  colors.pop(); => "blue"
8872
8943
  colors.pop(); => "green"
@@ -8974,12 +9045,12 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb
8974
9045
  /** @private - more optimized version */
8975
9046
  firstObject: Ember.computed(function() {
8976
9047
  return this.length > 0 ? this[0] : undefined;
8977
- }).property('[]').cacheable(),
9048
+ }).property().cacheable(),
8978
9049
 
8979
9050
  /** @private - more optimized version */
8980
9051
  lastObject: Ember.computed(function() {
8981
9052
  return this.length > 0 ? this[this.length-1] : undefined;
8982
- }).property('[]').cacheable(),
9053
+ }).property().cacheable(),
8983
9054
 
8984
9055
  /** @private (nodoc) - implements Ember.MutableEnumerable */
8985
9056
  addObject: function(obj) {
@@ -8994,11 +9065,16 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb
8994
9065
  if (idx>=0 && idx<len && (this[idx] === obj)) return this; // added
8995
9066
 
8996
9067
  added = [obj];
9068
+
8997
9069
  this.enumerableContentWillChange(null, added);
9070
+ Ember.propertyWillChange(this, 'lastObject');
9071
+
8998
9072
  len = get(this, 'length');
8999
9073
  this[guid] = len;
9000
9074
  this[len] = obj;
9001
9075
  set(this, 'length', len+1);
9076
+
9077
+ Ember.propertyDidChange(this, 'lastObject');
9002
9078
  this.enumerableContentDidChange(null, added);
9003
9079
 
9004
9080
  return this;
@@ -9012,6 +9088,8 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb
9012
9088
  var guid = guidFor(obj),
9013
9089
  idx = this[guid],
9014
9090
  len = get(this, 'length'),
9091
+ isFirst = idx === 0,
9092
+ isLast = idx === len-1,
9015
9093
  last, removed;
9016
9094
 
9017
9095
 
@@ -9019,6 +9097,8 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb
9019
9097
  removed = [obj];
9020
9098
 
9021
9099
  this.enumerableContentWillChange(removed, null);
9100
+ if (isFirst) { Ember.propertyWillChange(this, 'firstObject'); }
9101
+ if (isLast) { Ember.propertyWillChange(this, 'lastObject'); }
9022
9102
 
9023
9103
  // swap items - basically move the item to the end so it can be removed
9024
9104
  if (idx < len-1) {
@@ -9031,6 +9111,8 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb
9031
9111
  delete this[len-1];
9032
9112
  set(this, 'length', len-1);
9033
9113
 
9114
+ if (isFirst) { Ember.propertyDidChange(this, 'firstObject'); }
9115
+ if (isLast) { Ember.propertyDidChange(this, 'lastObject'); }
9034
9116
  this.enumerableContentDidChange(removed, null);
9035
9117
  }
9036
9118
 
@@ -9060,42 +9142,10 @@ Ember.Set = Ember.CoreObject.extend(Ember.MutableEnumerable, Ember.Copyable, Emb
9060
9142
  array[idx] = this[idx];
9061
9143
  }
9062
9144
  return "Ember.Set<%@>".fmt(array.join(','));
9063
- },
9064
-
9065
- // ..........................................................
9066
- // DEPRECATED
9067
- //
9068
-
9069
- /** @deprecated
9070
-
9071
- This property is often used to determine that a given object is a set.
9072
- Instead you should use instanceof:
9073
-
9074
- #js:
9075
- // SproutCore 1.x:
9076
- isSet = myobject && myobject.isSet;
9077
-
9078
- // Ember:
9079
- isSet = myobject instanceof Ember.Set
9080
-
9081
- @type Boolean
9082
- @default true
9083
- */
9084
- isSet: true
9145
+ }
9085
9146
 
9086
9147
  });
9087
9148
 
9088
- // Support the older API
9089
- var o_create = Ember.Set.create;
9090
- Ember.Set.create = function(items) {
9091
- if (items && Ember.Enumerable.detect(items)) {
9092
-
9093
- return new Ember.Set(items);
9094
- } else {
9095
- return o_create.apply(this, arguments);
9096
- }
9097
- };
9098
-
9099
9149
  })();
9100
9150
 
9101
9151
 
@@ -9226,7 +9276,7 @@ var get = Ember.get, set = Ember.set;
9226
9276
  A simple example of usage:
9227
9277
 
9228
9278
  var pets = ['dog', 'cat', 'fish'];
9229
- var arrayProxy = Ember.ArrayProxy.create({ content: Ember.A(pets) });
9279
+ var ap = Ember.ArrayProxy.create({ content: Ember.A(pets) });
9230
9280
  ap.get('firstObject'); // => 'dog'
9231
9281
  ap.set('content', ['amoeba', 'paramecium']);
9232
9282
  ap.get('firstObject'); // => 'amoeba'
@@ -9329,7 +9379,8 @@ Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,
9329
9379
  length: Ember.computed(function() {
9330
9380
  var content = get(this, 'content');
9331
9381
  return content ? get(content, 'length') : 0;
9332
- }).property('content.length').cacheable(),
9382
+ // No dependencies since Enumerable notifies length of change
9383
+ }).property().cacheable(),
9333
9384
 
9334
9385
  /** @private (nodoc) */
9335
9386
  replace: function(idx, amt, objects) {
@@ -9350,6 +9401,7 @@ Ember.ArrayProxy = Ember.Object.extend(Ember.MutableArray,
9350
9401
  /** @private (nodoc) */
9351
9402
  init: function() {
9352
9403
  this._super();
9404
+ this.contentWillChange();
9353
9405
  this.contentDidChange();
9354
9406
  }
9355
9407
 
@@ -9388,7 +9440,7 @@ var EachArray = Ember.Object.extend(Ember.Array, {
9388
9440
  length: Ember.computed(function() {
9389
9441
  var content = this._content;
9390
9442
  return content ? get(content, 'length') : 0;
9391
- }).property('[]').cacheable()
9443
+ }).property().cacheable()
9392
9444
 
9393
9445
  });
9394
9446
 
@@ -9918,6 +9970,37 @@ Map.prototype = {
9918
9970
 
9919
9971
 
9920
9972
 
9973
+ (function() {
9974
+ var loadHooks = {};
9975
+ var loaded = {};
9976
+
9977
+ Ember.onLoad = function(name, callback) {
9978
+ var object;
9979
+
9980
+ loadHooks[name] = loadHooks[name] || Ember.A();
9981
+ loadHooks[name].pushObject(callback);
9982
+
9983
+ if (object = loaded[name]) {
9984
+ callback(object);
9985
+ }
9986
+ };
9987
+
9988
+ Ember.runLoadHooks = function(name, object) {
9989
+ var hooks;
9990
+
9991
+ loaded[name] = object;
9992
+
9993
+ if (hooks = loadHooks[name]) {
9994
+ loadHooks[name].forEach(function(callback) {
9995
+ callback(object);
9996
+ });
9997
+ }
9998
+ };
9999
+
10000
+ })();
10001
+
10002
+
10003
+
9921
10004
  (function() {
9922
10005
  // ==========================================================================
9923
10006
  // Project: Ember Runtime