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