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.
- data/LICENSE +19 -0
- data/README.md +16 -15
- data/lib/ember/handlebars/source.rb +1 -1
- data/lib/ember/handlebars/template.rb +13 -7
- data/lib/ember/rails/engine.rb +5 -10
- data/lib/ember/rails/version.rb +1 -1
- data/lib/ember_rails.rb +36 -5
- data/lib/generators/ember/bootstrap_generator.rb +15 -9
- data/lib/generators/ember/controller_generator.rb +3 -3
- data/lib/generators/ember/generator_helpers.rb +1 -1
- data/lib/generators/ember/model_generator.rb +1 -1
- data/lib/generators/ember/view_generator.rb +18 -0
- data/lib/generators/templates/app.js +16 -7
- data/lib/generators/templates/array_controller.js +4 -7
- data/lib/generators/templates/controller.js +0 -1
- data/lib/generators/templates/states.js +18 -0
- data/lib/generators/templates/view.handlebars +3 -0
- data/lib/generators/templates/view.js +4 -0
- data/vendor/ember/development/ember-data.js +3748 -0
- data/vendor/{assets/javascripts → ember}/development/ember-runtime.js +1977 -1874
- data/vendor/{assets/javascripts/production → ember/development}/ember.js +10388 -8991
- data/vendor/ember/production/ember-data.js +3744 -0
- data/vendor/{assets/javascripts → ember}/production/ember-runtime.js +1918 -1835
- data/vendor/{assets/javascripts/development → ember/production}/ember.js +9954 -8764
- data/vendor/ember/spade/ember-data.js +1 -0
- data/vendor/ember/spade/ember.js +1 -0
- metadata +55 -17
- data/vendor/assets/javascripts/ember-dev.js +0 -3
- data/vendor/assets/javascripts/ember-runtime-dev.js +0 -3
@@ -10,7 +10,7 @@ if ('undefined' === typeof Ember) {
|
|
10
10
|
/**
|
11
11
|
@namespace
|
12
12
|
@name Ember
|
13
|
-
@version 0.9.
|
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 = {
|
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.
|
52
|
+
@default '0.9.8.1'
|
46
53
|
@constant
|
47
54
|
*/
|
48
|
-
Ember.VERSION = '0.9.
|
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
|
114
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
919
|
-
var
|
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
|
-
|
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 &&
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
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
|
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(
|
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
|
-
|
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)
|
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
|
-
|
3088
|
-
|
3127
|
+
// Ember.Logger
|
3128
|
+
// Ember.watch.flushPending
|
3129
|
+
// Ember.beginPropertyChanges, Ember.endPropertyChanges
|
3130
|
+
// Ember.guidFor
|
3131
|
+
// Ember.ArrayUtils
|
3089
3132
|
|
3090
|
-
|
3091
|
-
|
3092
|
-
|
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
|
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
|
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 (
|
3105
|
-
|
3106
|
-
|
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
|
-
|
3114
|
-
|
3115
|
-
|
3116
|
-
|
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
|
-
|
3120
|
-
|
3121
|
-
|
3122
|
-
|
3123
|
-
|
3124
|
-
return
|
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
|
-
|
3170
|
+
|
3171
|
+
// ..........................................................
|
3172
|
+
// RUNLOOP
|
3173
|
+
//
|
3174
|
+
|
3175
|
+
var timerMark; // used by timers...
|
3176
|
+
|
3131
3177
|
/** @private */
|
3132
|
-
function
|
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
|
-
|
3139
|
-
var
|
3181
|
+
var RunLoop = function(prev) {
|
3182
|
+
var self;
|
3140
3183
|
|
3141
|
-
|
3142
|
-
|
3143
|
-
|
3144
|
-
|
3184
|
+
if (this instanceof RunLoop) {
|
3185
|
+
self = this;
|
3186
|
+
} else {
|
3187
|
+
self = new K();
|
3145
3188
|
}
|
3146
3189
|
|
3147
|
-
|
3190
|
+
self._prev = prev || null;
|
3191
|
+
self.onceTimers = {};
|
3148
3192
|
|
3149
|
-
|
3150
|
-
|
3193
|
+
return self;
|
3194
|
+
};
|
3151
3195
|
|
3152
|
-
|
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
|
-
|
3198
|
+
RunLoop.prototype = {
|
3199
|
+
end: function() {
|
3200
|
+
this.flush();
|
3201
|
+
},
|
3162
3202
|
|
3163
|
-
|
3164
|
-
|
3165
|
-
|
3166
|
-
concats = concats ? concats.concat(props.concatenatedProperties) : props.concatenatedProperties;
|
3167
|
-
}
|
3203
|
+
prev: function() {
|
3204
|
+
return this._prev;
|
3205
|
+
},
|
3168
3206
|
|
3169
|
-
|
3170
|
-
|
3171
|
-
|
3172
|
-
if (value instanceof Ember.Descriptor) {
|
3173
|
-
if (value === REQUIRED && descs[key]) { continue; }
|
3207
|
+
// ..........................................................
|
3208
|
+
// Delayed Actions
|
3209
|
+
//
|
3174
3210
|
|
3175
|
-
|
3176
|
-
|
3177
|
-
|
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
|
-
|
3180
|
-
|
3181
|
-
|
3182
|
-
|
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
|
-
|
3196
|
-
|
3197
|
-
}
|
3198
|
-
}
|
3222
|
+
flush: function(queueName) {
|
3223
|
+
var queues = this._queues, queueNames, idx, len, queue, log;
|
3199
3224
|
|
3200
|
-
|
3201
|
-
if (props.hasOwnProperty('toString')) {
|
3202
|
-
base.toString = props.toString;
|
3203
|
-
}
|
3225
|
+
if (!queues) return this; // nothing to do
|
3204
3226
|
|
3205
|
-
|
3206
|
-
|
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
|
-
|
3213
|
-
var defineProperty = Ember.defineProperty;
|
3231
|
+
Ember.watch.flushPending(); // make sure all chained watchers are setup
|
3214
3232
|
|
3215
|
-
|
3216
|
-
|
3217
|
-
|
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
|
-
|
3260
|
-
|
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
|
-
|
3263
|
-
|
3243
|
+
Ember.beginPropertyChanges();
|
3244
|
+
try {
|
3245
|
+
forEach(queue, iter);
|
3246
|
+
} finally {
|
3247
|
+
Ember.endPropertyChanges();
|
3248
|
+
}
|
3264
3249
|
|
3265
|
-
|
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
|
-
|
3270
|
-
|
3271
|
-
|
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
|
-
|
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
|
-
|
3280
|
-
|
3281
|
-
|
3282
|
-
|
3283
|
-
|
3284
|
-
|
3285
|
-
|
3286
|
-
|
3287
|
-
|
3288
|
-
|
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
|
-
|
3289
|
+
timerMark = null;
|
3293
3290
|
|
3294
|
-
|
3295
|
-
|
3296
|
-
beforeObserverPaths = getBeforeObserverPaths(value),
|
3297
|
-
curBeforeObserverPaths = beforeObserverPaths && getBeforeObserverPaths(obj[key]),
|
3298
|
-
len, idx;
|
3291
|
+
return this;
|
3292
|
+
}
|
3299
3293
|
|
3300
|
-
|
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
|
-
|
3308
|
-
|
3309
|
-
|
3310
|
-
|
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
|
-
|
3315
|
-
|
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
|
-
|
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
|
-
|
3320
|
-
|
3321
|
-
|
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
|
-
|
3327
|
-
|
3328
|
-
|
3329
|
-
|
3330
|
-
}
|
3331
|
-
}
|
3321
|
+
@name run^2
|
3322
|
+
@methodOf Ember.run
|
3323
|
+
@param {Object} target
|
3324
|
+
(Optional) target of method to call
|
3332
3325
|
|
3333
|
-
|
3334
|
-
|
3335
|
-
|
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
|
-
|
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
|
-
|
3345
|
-
|
3346
|
-
|
3347
|
-
|
3348
|
-
|
3349
|
-
|
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
|
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
|
-
|
3364
|
-
|
3365
|
-
|
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
|
-
|
3368
|
-
|
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
|
-
|
3374
|
-
|
3375
|
-
|
3361
|
+
@returns {void}
|
3362
|
+
*/
|
3363
|
+
Ember.run.begin = function() {
|
3364
|
+
run.currentRunLoop = new RunLoop(run.currentRunLoop);
|
3376
3365
|
};
|
3377
3366
|
|
3378
|
-
|
3379
|
-
|
3380
|
-
|
3381
|
-
|
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
|
-
|
3372
|
+
Ember.run.begin();
|
3373
|
+
// code to be execute within a RunLoop
|
3374
|
+
Ember.run.end();
|
3385
3375
|
|
3386
|
-
|
3376
|
+
@returns {void}
|
3377
|
+
*/
|
3378
|
+
Ember.run.end = function() {
|
3387
3379
|
|
3388
|
-
|
3389
|
-
|
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
|
-
|
3396
|
-
|
3397
|
-
|
3398
|
-
|
3399
|
-
|
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
|
-
|
3409
|
-
|
3394
|
+
@property {String}
|
3395
|
+
@default ['sync', 'actions', 'destroy', 'timers']
|
3396
|
+
*/
|
3397
|
+
Ember.run.queues = ['sync', 'actions', 'destroy', 'timers'];
|
3410
3398
|
|
3411
|
-
|
3412
|
-
|
3413
|
-
|
3414
|
-
|
3415
|
-
|
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
|
-
|
3420
|
-
|
3421
|
-
|
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
|
-
|
3427
|
-
|
3428
|
-
|
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
|
-
|
3431
|
-
|
3421
|
+
@param {String} queue
|
3422
|
+
The name of the queue to schedule against. Default queues are 'sync' and
|
3423
|
+
'actions'
|
3432
3424
|
|
3433
|
-
|
3434
|
-
|
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
|
-
|
3442
|
-
|
3443
|
-
|
3444
|
-
|
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
|
-
|
3448
|
-
|
3449
|
-
|
3450
|
-
|
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
|
3455
|
-
|
3456
|
-
|
3446
|
+
function autorun() {
|
3447
|
+
autorunTimer = null;
|
3448
|
+
if (run.currentRunLoop) run.end();
|
3449
|
+
}
|
3457
3450
|
|
3458
|
-
|
3459
|
-
|
3460
|
-
|
3461
|
-
|
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
|
-
|
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
|
-
/**
|
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
|
-
|
3480
|
-
|
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
|
-
|
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
|
-
|
3491
|
-
|
3492
|
-
|
3493
|
-
|
3494
|
-
|
3495
|
-
|
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
|
-
|
3516
|
+
|
3517
|
+
// schedule next timeout to fire...
|
3518
|
+
if (earliest>0) setTimeout(invokeLaterTimers, earliest-(+ new Date()));
|
3500
3519
|
}
|
3501
3520
|
|
3502
|
-
/**
|
3503
|
-
|
3504
|
-
|
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
|
-
|
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
|
-
|
3509
|
-
|
3510
|
-
|
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
|
-
|
3516
|
-
|
3517
|
-
} catch (e) {
|
3518
|
-
continue;
|
3519
|
-
}
|
3535
|
+
@param {Object} target
|
3536
|
+
(optional) target of method to invoke
|
3520
3537
|
|
3521
|
-
|
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
|
-
|
3524
|
-
|
3525
|
-
}
|
3526
|
-
}
|
3542
|
+
@param {Object...} args
|
3543
|
+
Optional arguments to pass to the timeout.
|
3527
3544
|
|
3528
|
-
|
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
|
-
|
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
|
-
|
3543
|
-
|
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
|
-
|
3546
|
-
|
3547
|
-
|
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
|
-
|
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
|
-
|
3556
|
-
|
3557
|
-
|
3558
|
-
|
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
|
-
|
3564
|
-
|
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
|
-
|
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
|
-
|
3578
|
-
|
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
|
-
|
3592
|
-
|
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
|
-
|
3595
|
-
|
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
|
-
|
3600
|
-
|
3601
|
-
|
3602
|
-
|
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
|
-
|
3605
|
-
|
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.
|
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
|
-
|
3611
|
-
|
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
|
-
|
3616
|
-
|
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
|
-
|
3621
|
-
|
3673
|
+
timer = {
|
3674
|
+
target: target,
|
3675
|
+
method: method,
|
3676
|
+
args: slice.call(arguments),
|
3677
|
+
next: true
|
3678
|
+
};
|
3622
3679
|
|
3623
|
-
|
3624
|
-
|
3625
|
-
func.__ember_observes__ = paths;
|
3626
|
-
return func;
|
3627
|
-
};
|
3680
|
+
guid = Ember.guidFor(timer);
|
3681
|
+
timers[guid] = timer;
|
3628
3682
|
|
3629
|
-
|
3630
|
-
|
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: ©
|
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
|
-
//
|
3655
|
-
//
|
3656
|
-
//
|
3657
|
-
// Ember.
|
3726
|
+
// get, getPath, setPath, trySetPath
|
3727
|
+
// guidFor, isArray, meta
|
3728
|
+
// addObserver, removeObserver
|
3729
|
+
// Ember.run.schedule
|
3658
3730
|
|
3659
3731
|
// ..........................................................
|
3660
|
-
//
|
3732
|
+
// CONSTANTS
|
3661
3733
|
//
|
3662
3734
|
|
3663
|
-
var slice = Array.prototype.slice;
|
3664
|
-
var forEach = Ember.ArrayUtils.forEach;
|
3665
3735
|
|
3666
|
-
|
3667
|
-
|
3668
|
-
/** @private */
|
3669
|
-
function invoke(target, method, args, ignore) {
|
3736
|
+
/**
|
3737
|
+
@static
|
3670
3738
|
|
3671
|
-
|
3672
|
-
|
3673
|
-
|
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
|
-
|
3677
|
-
|
3678
|
-
|
3679
|
-
|
3743
|
+
@type Boolean
|
3744
|
+
@default false
|
3745
|
+
*/
|
3746
|
+
Ember.LOG_BINDINGS = false || !!Ember.ENV.LOG_BINDINGS;
|
3680
3747
|
|
3681
|
-
|
3682
|
-
|
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
|
-
|
3699
|
-
|
3754
|
+
@type Boolean
|
3755
|
+
*/
|
3756
|
+
Ember.BENCHMARK_BINDING_NOTIFICATIONS = !!Ember.ENV.BENCHMARK_BINDING_NOTIFICATIONS;
|
3700
3757
|
|
3701
|
-
|
3758
|
+
/**
|
3759
|
+
@static
|
3702
3760
|
|
3703
|
-
|
3704
|
-
|
3761
|
+
Performance parameter. This will benchmark the time spend configuring each
|
3762
|
+
binding.
|
3705
3763
|
|
3706
|
-
|
3707
|
-
|
3708
|
-
|
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
|
-
|
3769
|
+
/**
|
3770
|
+
@static
|
3723
3771
|
|
3724
|
-
|
3725
|
-
end: function() {
|
3726
|
-
this.flush();
|
3727
|
-
},
|
3772
|
+
Default placeholder for multiple values in bindings.
|
3728
3773
|
|
3729
|
-
|
3730
|
-
|
3731
|
-
|
3774
|
+
@type String
|
3775
|
+
@default '@@MULT@@'
|
3776
|
+
*/
|
3777
|
+
Ember.MULTIPLE_PLACEHOLDER = '@@MULT@@';
|
3732
3778
|
|
3733
|
-
|
3734
|
-
|
3735
|
-
//
|
3779
|
+
/**
|
3780
|
+
@static
|
3736
3781
|
|
3737
|
-
|
3738
|
-
|
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
|
-
|
3744
|
-
|
3745
|
-
|
3746
|
-
|
3785
|
+
@type String
|
3786
|
+
@default '@@EMPTY@@'
|
3787
|
+
*/
|
3788
|
+
Ember.EMPTY_PLACEHOLDER = '@@EMPTY@@';
|
3747
3789
|
|
3748
|
-
|
3749
|
-
|
3790
|
+
// ..........................................................
|
3791
|
+
// TYPE COERCION HELPERS
|
3792
|
+
//
|
3750
3793
|
|
3751
|
-
|
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
|
-
|
3754
|
-
|
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
|
-
|
3813
|
+
// Coerces the binding value into a Boolean.
|
3758
3814
|
|
3759
|
-
|
3760
|
-
|
3761
|
-
|
3815
|
+
var BOOL = {
|
3816
|
+
to: function (val) {
|
3817
|
+
return !!val;
|
3818
|
+
}
|
3819
|
+
};
|
3762
3820
|
|
3763
|
-
|
3764
|
-
|
3765
|
-
|
3766
|
-
|
3767
|
-
|
3821
|
+
// Returns the Boolean inverse of the value.
|
3822
|
+
var NOT = {
|
3823
|
+
to: function NOT(val) {
|
3824
|
+
return !val;
|
3825
|
+
}
|
3826
|
+
};
|
3768
3827
|
|
3769
|
-
|
3770
|
-
|
3771
|
-
|
3772
|
-
|
3773
|
-
|
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
|
-
|
3834
|
+
// Applies a binding's transformations against a value.
|
3835
|
+
/** @private */
|
3836
|
+
function getTransformedValue(binding, val, obj, dir) {
|
3777
3837
|
|
3778
|
-
|
3779
|
-
|
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
|
-
|
3784
|
-
|
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
|
-
|
3793
|
-
|
3794
|
-
|
3795
|
-
|
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
|
-
|
3800
|
-
|
3801
|
-
|
3802
|
-
|
3803
|
-
|
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
|
-
|
3807
|
-
|
3808
|
-
|
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
|
-
|
3862
|
+
/** @private */
|
3863
|
+
function getPathWithGlobals(obj, path) {
|
3864
|
+
return getPath(isGlobalPath(path) ? window : obj, path);
|
3865
|
+
}
|
3816
3866
|
|
3817
|
-
|
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
|
-
|
3890
|
+
/** @private */
|
3891
|
+
var OR_OPERATION = function(obj, left, right) {
|
3892
|
+
return getPathWithGlobals(obj, left) || getPathWithGlobals(obj, right);
|
3893
|
+
};
|
3823
3894
|
|
3824
3895
|
// ..........................................................
|
3825
|
-
//
|
3896
|
+
// BINDING
|
3826
3897
|
//
|
3827
|
-
/**
|
3828
|
-
|
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
|
-
|
3848
|
-
|
3849
|
-
|
3850
|
-
(Optional) target of method to call
|
3901
|
+
/** @private */
|
3902
|
+
var Binding = function(toPath, fromPath) {
|
3903
|
+
var self;
|
3851
3904
|
|
3852
|
-
|
3853
|
-
|
3854
|
-
|
3905
|
+
if (this instanceof Binding) {
|
3906
|
+
self = this;
|
3907
|
+
} else {
|
3908
|
+
self = new K();
|
3909
|
+
}
|
3855
3910
|
|
3856
|
-
@
|
3857
|
-
|
3911
|
+
/** @private */
|
3912
|
+
self._direction = 'fwd';
|
3858
3913
|
|
3859
|
-
@
|
3860
|
-
|
3861
|
-
|
3914
|
+
/** @private */
|
3915
|
+
self._from = fromPath;
|
3916
|
+
self._to = toPath;
|
3862
3917
|
|
3863
|
-
|
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
|
-
|
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
|
-
|
3879
|
-
|
3880
|
-
an lower-level way to use a RunLoop instead of using Ember.run().
|
3947
|
+
// ..........................................................
|
3948
|
+
// CONFIG
|
3949
|
+
//
|
3881
3950
|
|
3882
|
-
|
3883
|
-
|
3884
|
-
|
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
|
-
|
3888
|
-
|
3889
|
-
|
3890
|
-
|
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
|
-
|
3895
|
-
|
3896
|
-
|
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
|
-
|
3899
|
-
|
3900
|
-
|
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
|
-
|
3903
|
-
|
3904
|
-
Ember.
|
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
|
-
|
3907
|
-
|
3908
|
-
|
3909
|
-
|
3910
|
-
|
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
|
-
|
3916
|
-
|
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
|
-
|
3921
|
-
|
3922
|
-
|
3923
|
-
|
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
|
-
|
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
|
-
|
3932
|
-
|
3933
|
-
|
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
|
-
|
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
|
-
|
3948
|
-
The name of the queue to schedule against. Default queues are 'sync' and
|
3949
|
-
'actions'
|
4012
|
+
function(value) {};
|
3950
4013
|
|
3951
|
-
|
3952
|
-
(Optional) target object to use as the context when invoking a method.
|
4014
|
+
They must also return the transformed value.
|
3953
4015
|
|
3954
|
-
|
3955
|
-
|
3956
|
-
|
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
|
-
|
3960
|
-
|
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
|
-
|
3963
|
-
|
3964
|
-
|
3965
|
-
|
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
|
-
|
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
|
-
|
3972
|
-
|
3973
|
-
|
3974
|
-
|
3975
|
-
|
4038
|
+
@returns {Ember.Binding} this
|
4039
|
+
*/
|
4040
|
+
resetTransforms: function() {
|
4041
|
+
this._transforms = null;
|
4042
|
+
return this;
|
4043
|
+
},
|
3976
4044
|
|
3977
|
-
/**
|
3978
|
-
|
3979
|
-
|
3980
|
-
|
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
|
-
|
4050
|
+
- [] => null
|
4051
|
+
- [a] => a
|
4052
|
+
- [a,b,c] => Multiple Placeholder
|
3984
4053
|
|
3985
|
-
|
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
|
-
|
3990
|
-
|
4057
|
+
Note that this transform will only happen on forwarded valued. Reverse
|
4058
|
+
values are send unchanged.
|
3991
4059
|
|
3992
|
-
|
3993
|
-
|
3994
|
-
|
3995
|
-
|
3996
|
-
|
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
|
-
|
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
|
-
|
4005
|
-
|
4006
|
-
|
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
|
-
|
4009
|
-
|
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
|
-
|
4089
|
+
@returns {Ember.Binding} this
|
4090
|
+
*/
|
4091
|
+
bool: function() {
|
4092
|
+
this.transform(BOOL);
|
4093
|
+
return this;
|
4094
|
+
},
|
4012
4095
|
|
4013
|
-
|
4014
|
-
|
4015
|
-
|
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
|
-
|
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
|
-
|
4108
|
+
this.transform({
|
4109
|
+
to: function(val) { return empty(val) ? placeholder : val; }
|
4110
|
+
});
|
4025
4111
|
|
4026
|
-
|
4027
|
-
|
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
|
-
|
4044
|
-
|
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
|
-
|
4049
|
-
|
4050
|
-
|
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
|
-
|
4053
|
-
|
4054
|
-
|
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
|
-
|
4058
|
-
|
4059
|
-
}, 500);
|
4132
|
+
return this;
|
4133
|
+
},
|
4060
4134
|
|
4061
|
-
|
4062
|
-
|
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
|
-
|
4065
|
-
|
4066
|
-
|
4139
|
+
@returns {Ember.Binding} this
|
4140
|
+
*/
|
4141
|
+
not: function() {
|
4142
|
+
this.transform(NOT);
|
4143
|
+
return this;
|
4144
|
+
},
|
4067
4145
|
|
4068
|
-
|
4069
|
-
|
4146
|
+
/**
|
4147
|
+
Adds a transform that will return true if the value is null or undefined, false otherwise.
|
4070
4148
|
|
4071
|
-
|
4072
|
-
|
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
|
-
@
|
4075
|
-
|
4076
|
-
|
4077
|
-
|
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
|
-
//
|
4080
|
-
|
4081
|
-
|
4082
|
-
method = target;
|
4083
|
-
target = undefined;
|
4084
|
-
args = [target, method];
|
4162
|
+
// ..........................................................
|
4163
|
+
// CONNECT AND SYNC
|
4164
|
+
//
|
4085
4165
|
|
4086
|
-
|
4087
|
-
|
4088
|
-
|
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
|
-
|
4092
|
-
|
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
|
-
|
4100
|
-
|
4101
|
-
|
4102
|
-
|
4103
|
-
|
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
|
-
|
4108
|
-
|
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
|
-
|
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
|
-
|
4122
|
-
(
|
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
|
-
|
4125
|
-
|
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
|
-
|
4129
|
-
|
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
|
-
|
4133
|
-
|
4134
|
-
|
4135
|
-
var tguid = Ember.guidFor(target), mguid = Ember.guidFor(method), guid, timer;
|
4199
|
+
this._readyToSync = true;
|
4200
|
+
return this;
|
4201
|
+
},
|
4136
4202
|
|
4137
|
-
|
4138
|
-
|
4139
|
-
|
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
|
-
|
4143
|
-
|
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
|
-
|
4152
|
-
|
4153
|
-
|
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
|
-
|
4160
|
-
};
|
4215
|
+
var oneWay = this._oneWay, operand = this._operand;
|
4161
4216
|
|
4162
|
-
|
4163
|
-
|
4164
|
-
|
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
|
-
|
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
|
-
|
4181
|
-
|
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
|
-
|
4185
|
-
|
4227
|
+
this._readyToSync = false; // disable scheduled syncs...
|
4228
|
+
return this;
|
4229
|
+
},
|
4186
4230
|
|
4187
|
-
|
4188
|
-
|
4189
|
-
|
4231
|
+
// ..........................................................
|
4232
|
+
// PRIVATE
|
4233
|
+
//
|
4190
4234
|
|
4191
|
-
@
|
4192
|
-
|
4235
|
+
/** @private - called when the from side changes */
|
4236
|
+
fromDidChange: function(target) {
|
4237
|
+
this._scheduleSync(target, 'fwd');
|
4238
|
+
},
|
4193
4239
|
|
4194
|
-
@
|
4195
|
-
|
4196
|
-
|
4197
|
-
|
4240
|
+
/** @private - called when the to side changes */
|
4241
|
+
toDidChange: function(target) {
|
4242
|
+
this._scheduleSync(target, 'back');
|
4243
|
+
},
|
4198
4244
|
|
4199
|
-
|
4200
|
-
|
4201
|
-
|
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
|
-
|
4207
|
-
|
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
|
-
|
4210
|
-
|
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
|
-
|
4215
|
-
|
4262
|
+
/** @private */
|
4263
|
+
_sync: function(obj) {
|
4264
|
+
var log = Ember.LOG_BINDINGS;
|
4216
4265
|
|
4217
|
-
|
4218
|
-
|
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
|
-
|
4223
|
-
|
4224
|
-
|
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
|
-
|
4228
|
-
// will not be executed
|
4229
|
-
});
|
4230
|
-
Ember.run.cancel(runOnce);
|
4273
|
+
var fromPath = this._from, toPath = this._to;
|
4231
4274
|
|
4232
|
-
|
4233
|
-
|
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
|
-
|
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
|
-
|
4247
|
-
@name Ember.RunLoop
|
4248
|
-
@deprecated
|
4249
|
-
*/
|
4313
|
+
mixinProperties(Binding,
|
4314
|
+
/** @scope Ember.Binding */ {
|
4250
4315
|
|
4251
|
-
/**
|
4252
|
-
|
4253
|
-
|
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
|
-
|
4256
|
-
|
4257
|
-
|
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
|
-
|
4261
|
-
|
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
|
-
|
4264
|
-
|
4265
|
-
|
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
|
-
|
4274
|
-
|
4275
|
-
|
4276
|
-
|
4277
|
-
|
4278
|
-
|
4279
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
4299
|
-
|
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
|
-
|
4420
|
+
deleteButton: Ember.ButtonView.design({
|
4421
|
+
isEnabledBinding: Ember.Binding.and('MyApp.itemsController.hasSelection', 'MyApp.userController.canDelete')
|
4422
|
+
})
|
4305
4423
|
|
4306
|
-
|
4307
|
-
|
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
|
-
|
4310
|
-
|
4311
|
-
|
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
|
-
|
4317
|
-
|
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
|
-
|
4320
|
-
|
4321
|
-
|
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
|
-
@
|
4469
|
+
@class
|
4326
4470
|
|
4327
|
-
|
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
|
-
|
4330
|
-
@default '@@MULT@@'
|
4331
|
-
*/
|
4332
|
-
Ember.MULTIPLE_PLACEHOLDER = '@@MULT@@';
|
4476
|
+
valueBinding: "MyApp.someController.title"
|
4333
4477
|
|
4334
|
-
|
4335
|
-
|
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
|
-
|
4338
|
-
helper unless you specify an alternative.
|
4482
|
+
## Customizing Your Bindings
|
4339
4483
|
|
4340
|
-
|
4341
|
-
|
4342
|
-
|
4343
|
-
|
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
|
-
|
4350
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
4384
|
-
|
4385
|
-
|
4386
|
-
|
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
|
-
|
4390
|
-
/** @private */
|
4391
|
-
function getTransformedValue(binding, val, obj, dir) {
|
4508
|
+
## One Way Bindings
|
4392
4509
|
|
4393
|
-
|
4394
|
-
|
4395
|
-
|
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
|
-
|
4398
|
-
if (typeTransform) { val = typeTransform(val, binding._placeholder); }
|
4517
|
+
bigTitlesBinding: Ember.Binding.oneWay("MyApp.preferencesController.bigTitles")
|
4399
4518
|
|
4400
|
-
|
4401
|
-
|
4402
|
-
|
4403
|
-
|
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
|
-
|
4406
|
-
|
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
|
-
|
4413
|
-
|
4414
|
-
|
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
|
-
|
4418
|
-
function getPathWithGlobals(obj, path) {
|
4419
|
-
return getPath(isGlobalPath(path) ? window : obj, path);
|
4420
|
-
}
|
4531
|
+
## Adding Custom Transforms
|
4421
4532
|
|
4422
|
-
|
4423
|
-
|
4424
|
-
|
4425
|
-
|
4426
|
-
|
4427
|
-
|
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
|
-
|
4435
|
-
|
4436
|
-
|
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
|
-
|
4441
|
-
|
4442
|
-
return
|
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
|
-
|
4446
|
-
|
4447
|
-
|
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
|
-
|
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
|
-
|
4457
|
-
var Binding = function(toPath, fromPath) {
|
4458
|
-
var self;
|
4559
|
+
valueBinding: Ember.Binding.from("MyApp.someController.value").notLessThan(10)
|
4459
4560
|
|
4460
|
-
|
4461
|
-
|
4462
|
-
|
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
|
-
|
4467
|
-
self._direction = 'fwd';
|
4565
|
+
valueBinding: Ember.Binding.oneWay("MyApp.someController.value").notEmpty().notLessThan(10)
|
4468
4566
|
|
4469
|
-
|
4470
|
-
|
4471
|
-
|
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
|
-
|
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
|
-
|
4576
|
+
## How to Manually Adding Binding
|
4477
4577
|
|
4478
|
-
|
4479
|
-
|
4480
|
-
|
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
|
-
|
4485
|
-
|
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
|
-
|
4489
|
-
|
4490
|
-
|
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
|
-
|
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
|
-
|
4502
|
-
|
4503
|
-
|
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
|
-
|
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
|
-
|
4510
|
-
|
4511
|
-
|
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
|
-
|
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
|
-
|
4525
|
-
|
4526
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
4539
|
-
|
4540
|
-
forward or back direction.
|
4622
|
+
MyApp.anotherObject = Ember.Object.create({
|
4623
|
+
valueBinding: "MyApp.someController.value",
|
4541
4624
|
|
4542
|
-
|
4625
|
+
// OTHER CODE FOR THIS OBJECT...
|
4543
4626
|
|
4544
|
-
|
4627
|
+
});
|
4545
4628
|
|
4546
|
-
|
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
|
-
|
4549
|
-
|
4550
|
-
|
4633
|
+
@since Ember 0.9
|
4634
|
+
*/
|
4635
|
+
Ember.Binding = Binding;
|
4551
4636
|
|
4552
|
-
|
4553
|
-
|
4554
|
-
|
4555
|
-
|
4556
|
-
|
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
|
-
|
4561
|
-
|
4562
|
-
return this;
|
4563
|
-
},
|
4643
|
+
@param {Object} obj
|
4644
|
+
The root object of the transform.
|
4564
4645
|
|
4565
|
-
|
4566
|
-
|
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
|
-
|
4571
|
-
|
4572
|
-
|
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
|
-
|
4579
|
-
|
4580
|
-
|
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
|
-
|
4583
|
-
|
4584
|
-
|
4659
|
+
Ember.oneWay = function(obj, to, from) {
|
4660
|
+
return new Ember.Binding(to, from).oneWay().connect(obj);
|
4661
|
+
};
|
4585
4662
|
|
4586
|
-
|
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
|
-
|
4605
|
-
|
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
|
-
|
4608
|
-
|
4609
|
-
|
4610
|
-
|
4611
|
-
|
4612
|
-
|
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
|
-
|
4622
|
-
|
4623
|
-
|
4624
|
-
|
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
|
-
|
4630
|
-
|
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
|
-
|
4633
|
-
|
4634
|
-
|
4635
|
-
|
4636
|
-
|
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
|
-
|
4641
|
-
to
|
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
|
-
|
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
|
-
|
4649
|
-
|
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
|
-
|
4652
|
-
|
4653
|
-
|
4654
|
-
|
4655
|
-
|
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
|
-
|
4661
|
-
to: function(val) { return (val === null || val === undefined) ? placeholder : val; }
|
4662
|
-
});
|
4733
|
+
for(idx=0;idx<len;idx++) {
|
4663
4734
|
|
4664
|
-
|
4665
|
-
|
4735
|
+
mixin = mixins[idx];
|
4736
|
+
if (!mixin) throw new Error('Null value found in Ember.mixin()');
|
4666
4737
|
|
4667
|
-
|
4668
|
-
|
4669
|
-
|
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
|
-
|
4672
|
-
*/
|
4673
|
-
not: function() {
|
4674
|
-
this.transform(NOT);
|
4675
|
-
return this;
|
4676
|
-
},
|
4747
|
+
if (props) {
|
4677
4748
|
|
4678
|
-
|
4679
|
-
|
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
|
-
|
4682
|
-
|
4683
|
-
|
4684
|
-
|
4685
|
-
|
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
|
-
|
4689
|
-
|
4690
|
-
|
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
|
-
|
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
|
-
|
4700
|
-
|
4701
|
-
|
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
|
-
|
4707
|
-
|
4708
|
-
|
4709
|
-
|
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
|
-
|
4713
|
-
|
4714
|
-
|
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
|
-
|
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
|
-
|
4720
|
-
|
4811
|
+
/** @private */
|
4812
|
+
function getObserverPaths(value) {
|
4813
|
+
return ('function' === typeof value) && value.__ember_observes__;
|
4814
|
+
}
|
4721
4815
|
|
4722
|
-
|
4723
|
-
|
4816
|
+
/** @private */
|
4817
|
+
function getBeforeObserverPaths(value) {
|
4818
|
+
return ('function' === typeof value) && value.__ember_observesBefore__;
|
4819
|
+
}
|
4724
4820
|
|
4725
|
-
|
4726
|
-
// object.
|
4727
|
-
if (!oneWay) { Ember.addObserver(obj, this._to, this, this.toDidChange); }
|
4821
|
+
var IS_BINDING = Ember.IS_BINDING = /^.+Binding$/;
|
4728
4822
|
|
4729
|
-
|
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
|
-
|
4732
|
-
|
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
|
-
|
4737
|
-
|
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
|
-
|
4740
|
-
|
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
|
-
|
4743
|
-
|
4744
|
-
|
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
|
-
|
4879
|
+
desc = descs[key];
|
4880
|
+
value = values[key];
|
4748
4881
|
|
4749
|
-
|
4750
|
-
|
4751
|
-
|
4882
|
+
if (desc === REQUIRED) {
|
4883
|
+
if (!(key in obj)) {
|
4884
|
+
if (!partial) throw new Error('Required property not defined: '+key);
|
4752
4885
|
|
4753
|
-
|
4754
|
-
|
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
|
-
|
4757
|
-
if (!oneWay) Ember.removeObserver(obj, this._to, this, this.toDidChange);
|
4892
|
+
} else {
|
4758
4893
|
|
4759
|
-
|
4760
|
-
return this;
|
4761
|
-
},
|
4894
|
+
while (desc instanceof Alias) {
|
4762
4895
|
|
4763
|
-
|
4764
|
-
|
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
|
-
|
4768
|
-
fromDidChange: function(target) {
|
4769
|
-
this._scheduleSync(target, 'fwd');
|
4770
|
-
},
|
4909
|
+
if (willApply) willApply.call(obj, key);
|
4771
4910
|
|
4772
|
-
|
4773
|
-
|
4774
|
-
|
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
|
-
|
4778
|
-
|
4779
|
-
|
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
|
-
|
4782
|
-
|
4783
|
-
|
4784
|
-
|
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
|
-
|
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
|
-
|
4795
|
-
_sync: function(obj) {
|
4796
|
-
var log = Ember.LOG_BINDINGS;
|
4933
|
+
defineProperty(obj, key, desc, value);
|
4797
4934
|
|
4798
|
-
|
4799
|
-
|
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
|
-
|
4802
|
-
|
4803
|
-
|
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
|
-
|
4949
|
+
if (req && req[key]) {
|
4950
|
+
req = writableReq(obj);
|
4951
|
+
req.__ember_count__--;
|
4952
|
+
req[key] = false;
|
4953
|
+
}
|
4806
4954
|
|
4807
|
-
|
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
|
-
|
4837
|
-
|
4838
|
-
|
4839
|
-
|
4840
|
-
|
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
|
-
|
4846
|
-
|
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
|
-
|
4943
|
-
|
4944
|
-
|
4982
|
+
/**
|
4983
|
+
@constructor
|
4984
|
+
*/
|
4985
|
+
Ember.Mixin = function() { return initMixin(this, arguments); };
|
4945
4986
|
|
4946
|
-
|
4987
|
+
/** @private */
|
4988
|
+
Mixin = Ember.Mixin;
|
4947
4989
|
|
4948
|
-
|
4949
|
-
|
4950
|
-
allowed to delete:
|
4990
|
+
/** @private */
|
4991
|
+
Mixin._apply = applyMixin;
|
4951
4992
|
|
4952
|
-
|
4953
|
-
|
4954
|
-
|
4993
|
+
Mixin.applyPartial = function(obj) {
|
4994
|
+
var args = a_slice.call(arguments, 1);
|
4995
|
+
return applyMixin(obj, args, true);
|
4996
|
+
};
|
4955
4997
|
|
4956
|
-
|
4957
|
-
|
4958
|
-
|
4959
|
-
|
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
|
-
|
4968
|
-
|
4969
|
-
|
5003
|
+
Mixin.create = function() {
|
5004
|
+
classToString.processed = false;
|
5005
|
+
var M = this;
|
5006
|
+
return initMixin(new M(), arguments);
|
5007
|
+
};
|
4970
5008
|
|
4971
|
-
|
5009
|
+
Mixin.prototype.reopen = function() {
|
4972
5010
|
|
4973
|
-
|
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
|
-
|
4989
|
-
|
4990
|
-
|
4991
|
-
|
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
|
-
|
5033
|
+
return this;
|
5034
|
+
};
|
4994
5035
|
|
4995
|
-
|
4996
|
-
|
4997
|
-
|
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
|
-
|
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
|
-
|
5002
|
-
|
5003
|
-
|
5004
|
-
what the other object outputs.
|
5051
|
+
/** @private */
|
5052
|
+
function _detect(curMixin, targetMixin, seen) {
|
5053
|
+
var guid = Ember.guidFor(curMixin);
|
5005
5054
|
|
5006
|
-
|
5007
|
-
|
5055
|
+
if (seen[guid]) return false;
|
5056
|
+
seen[guid] = true;
|
5008
5057
|
|
5009
|
-
|
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
|
-
|
5012
|
-
|
5013
|
-
|
5014
|
-
|
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
|
-
|
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
|
-
|
5078
|
+
/** @private */
|
5079
|
+
function _keys(ret, mixin, seen) {
|
5080
|
+
if (seen[Ember.guidFor(mixin)]) return;
|
5081
|
+
seen[Ember.guidFor(mixin)] = true;
|
5019
5082
|
|
5020
|
-
|
5021
|
-
|
5022
|
-
|
5023
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
5104
|
+
var NAME_KEY = Ember.GUID_KEY+'_name';
|
5105
|
+
var get = Ember.get;
|
5035
5106
|
|
5036
|
-
|
5037
|
-
|
5038
|
-
|
5039
|
-
|
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
|
-
|
5042
|
-
|
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
|
-
|
5045
|
-
|
5046
|
-
|
5123
|
+
}
|
5124
|
+
paths.length = idx; // cut out last item
|
5125
|
+
}
|
5047
5126
|
|
5048
|
-
|
5127
|
+
/** @private */
|
5128
|
+
function findNamespaces() {
|
5129
|
+
var Namespace = Ember.Namespace, obj, isNamespace;
|
5049
5130
|
|
5050
|
-
|
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
|
-
|
5058
|
-
|
5059
|
-
|
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
|
-
|
5062
|
-
|
5063
|
-
|
5064
|
-
|
5065
|
-
|
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
|
-
|
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
|
-
|
5076
|
-
|
5151
|
+
obj[NAME_KEY] = prop;
|
5152
|
+
}
|
5153
|
+
}
|
5154
|
+
}
|
5077
5155
|
|
5078
|
-
|
5156
|
+
Ember.identifyNamespaces = findNamespaces;
|
5079
5157
|
|
5080
|
-
|
5081
|
-
|
5082
|
-
|
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
|
-
|
5169
|
+
/** @private */
|
5170
|
+
classToString = function() {
|
5171
|
+
var Namespace = Ember.Namespace, namespace;
|
5085
5172
|
|
5086
|
-
|
5087
|
-
|
5088
|
-
|
5089
|
-
|
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
|
-
|
5092
|
-
to: function(value, binding) { return value.toUpperCase(); },
|
5093
|
-
from: function(value, binding) { return value.toLowerCase(); }
|
5181
|
+
classToString.processed = true;
|
5094
5182
|
|
5095
|
-
|
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
|
-
|
5098
|
-
|
5099
|
-
|
5100
|
-
|
5101
|
-
|
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
|
-
|
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
|
-
|
5108
|
-
|
5109
|
-
|
5110
|
-
|
5111
|
-
|
5112
|
-
|
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
|
-
|
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
|
-
|
5117
|
-
|
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
|
-
|
5222
|
+
Ember.required = function() {
|
5223
|
+
return REQUIRED;
|
5224
|
+
};
|
5122
5225
|
|
5123
|
-
|
5124
|
-
|
5125
|
-
|
5126
|
-
|
5226
|
+
/** @private */
|
5227
|
+
Alias = function(methodName) {
|
5228
|
+
this.methodName = methodName;
|
5229
|
+
};
|
5230
|
+
Alias.prototype = new Ember.Descriptor();
|
5127
5231
|
|
5128
|
-
|
5129
|
-
|
5232
|
+
Ember.alias = function(methodName) {
|
5233
|
+
return new Alias(methodName);
|
5234
|
+
};
|
5130
5235
|
|
5131
|
-
|
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
|
-
|
5238
|
+
willApplyProperty: Ember.required(),
|
5239
|
+
didApplyProperty: Ember.required()
|
5137
5240
|
|
5138
|
-
|
5139
|
-
form of binding creation like so:
|
5241
|
+
});
|
5140
5242
|
|
5141
|
-
|
5142
|
-
|
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
|
-
|
5149
|
-
|
5150
|
-
|
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
|
-
|
5153
|
-
|
5154
|
-
|
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
|
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
|
-
|
6216
|
-
|
6217
|
-
|
6218
|
-
|
6219
|
-
|
6220
|
-
|
6221
|
-
|
6222
|
-
|
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}
|
6779
|
-
|
6780
|
-
|
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}
|
6784
|
-
|
6785
|
-
|
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 (
|
7125
|
-
if (
|
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 (
|
7154
|
-
if (
|
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
|
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.
|
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
|
-
|
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
|
-
|
8675
|
-
|
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(
|
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(
|
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
|
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
|
-
|
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(
|
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
|