]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @class Ext.Config\r | |
3 | * This class manages a config property. Instances of this type are created and cached as\r | |
4 | * classes declare their config properties. One instance of this class is created per\r | |
5 | * config property name.\r | |
6 | *\r | |
7 | * Ext.define('MyClass', {\r | |
8 | * config: {\r | |
9 | * foo: 42\r | |
10 | * }\r | |
11 | * });\r | |
12 | *\r | |
13 | * This uses the cached `Ext.Config` instance for the "foo" property.\r | |
14 | *\r | |
15 | * When config properties apply options to config properties a prototype chained object is\r | |
16 | * created from the cached instance. For example:\r | |
17 | *\r | |
18 | * Ext.define('MyClass', {\r | |
19 | * config: {\r | |
20 | * foo: {\r | |
21 | * $value: 42,\r | |
22 | * lazy: true\r | |
23 | * }\r | |
24 | * }\r | |
25 | * });\r | |
26 | *\r | |
27 | * This creates a prototype chain to the cached "foo" instance of `Ext.Config` and applies\r | |
28 | * the `lazy` option to that new instance. This chained instance is then kept by the\r | |
29 | * `Ext.Configurator` for that class.\r | |
30 | * @private\r | |
31 | */\r | |
32 | Ext.Config = function (name) {\r | |
33 | // @define Ext.class.Config\r | |
34 | // @define Ext.Config\r | |
35 | \r | |
36 | var me = this,\r | |
37 | capitalizedName = name.charAt(0).toUpperCase() + name.substr(1);\r | |
38 | \r | |
39 | /**\r | |
40 | * @property {String} name\r | |
41 | * The name of this config property.\r | |
42 | * @readonly\r | |
43 | * @private\r | |
44 | * @since 5.0.0\r | |
45 | */\r | |
46 | me.name = name;\r | |
47 | \r | |
48 | /**\r | |
49 | * @property {Object} names\r | |
50 | * This object holds the cached names used to lookup properties or methods for this\r | |
51 | * config property. The properties of this object are explained in the context of an\r | |
52 | * example property named "foo".\r | |
53 | *\r | |
54 | * @property {String} names.internal The default backing property ("_foo").\r | |
55 | *\r | |
56 | * @property {String} names.initializing The property that is `true` when the config\r | |
57 | * is being initialized ("isFooInitializing").\r | |
58 | *\r | |
59 | * @property {String} names.apply The name of the applier method ("applyFoo").\r | |
60 | *\r | |
61 | * @property {String} names.update The name of the updater method ("updateFoo").\r | |
62 | *\r | |
63 | * @property {String} names.get The name of the getter method ("getFoo").\r | |
64 | *\r | |
65 | * @property {String} names.set The name of the setter method ("setFoo").\r | |
66 | *\r | |
67 | * @property {String} names.initGet The name of the initializing getter ("initGetFoo").\r | |
68 | *\r | |
69 | * @property {String} names.changeEvent The name of the change event ("foochange").\r | |
70 | *\r | |
71 | * @readonly\r | |
72 | * @private\r | |
73 | * @since 5.0.0\r | |
74 | */\r | |
75 | me.names = {\r | |
76 | internal: '_' + name,\r | |
77 | initializing: 'is' + capitalizedName + 'Initializing',\r | |
78 | apply: 'apply' + capitalizedName,\r | |
79 | update: 'update' + capitalizedName,\r | |
80 | get: 'get' + capitalizedName,\r | |
81 | set: 'set' + capitalizedName,\r | |
82 | initGet: 'initGet' + capitalizedName,\r | |
83 | changeEvent: name.toLowerCase() + 'change'\r | |
84 | };\r | |
85 | \r | |
86 | // This allows folks to prototype chain on top of these objects and yet still cache\r | |
87 | // generated methods at the bottom of the chain.\r | |
88 | me.root = me;\r | |
89 | };\r | |
90 | \r | |
91 | Ext.Config.map = {};\r | |
92 | \r | |
93 | Ext.Config.get = function (name) {\r | |
94 | var map = Ext.Config.map,\r | |
95 | ret = map[name] || (map[name] = new Ext.Config(name));\r | |
96 | \r | |
97 | return ret;\r | |
98 | };\r | |
99 | \r | |
100 | Ext.Config.prototype = {\r | |
101 | self: Ext.Config,\r | |
102 | \r | |
103 | isConfig: true,\r | |
104 | \r | |
105 | /**\r | |
106 | * @cfg {Boolean} [cached=false]\r | |
107 | * When set as `true` the config property will be stored on the class prototype once\r | |
108 | * the first instance has had a chance to process the default value.\r | |
109 | * @private\r | |
110 | * @since 5.0.0\r | |
111 | */\r | |
112 | \r | |
113 | /**\r | |
114 | * @cfg {Boolean} [lazy=false]\r | |
115 | * When set as `true` the config property will not be immediately initialized during\r | |
116 | * the `initConfig` call.\r | |
117 | * @private\r | |
118 | * @since 5.0.0\r | |
119 | */\r | |
120 | \r | |
121 | /**\r | |
122 | * @cfg {Boolean} [evented=false]\r | |
123 | * When set as `true` the config property will be treated as a {@link Ext.Evented Evented Config}.\r | |
124 | * @private\r | |
125 | * @since 5.5.0\r | |
126 | */\r | |
127 | \r | |
128 | /**\r | |
129 | * @cfg {Function} [merge]\r | |
130 | * This function if supplied will be called as classes or instances provide values\r | |
131 | * that need to be combined with inherited values. The function should return the\r | |
132 | * value that will be the config value. Further calls may receive such returned\r | |
133 | * values as `oldValue`.\r | |
134 | *\r | |
135 | * @cfg {Mixed} merge.newValue The new value to merge with the old.\r | |
136 | *\r | |
137 | * @cfg {Mixed} merge.oldValue The current value prior to `newValue` being merged.\r | |
138 | *\r | |
139 | * @cfg {Mixed} merge.target The class or instance to which the merged config value\r | |
140 | * will be applied.\r | |
141 | *\r | |
142 | * @cfg {Ext.Class} merge.mixinClass The mixin providing the `newValue` or `null` if\r | |
143 | * the `newValue` is not being provided by a mixin.\r | |
144 | */\r | |
145 | \r | |
146 | getGetter: function () {\r | |
147 | return this.getter || (this.root.getter = this.makeGetter());\r | |
148 | },\r | |
149 | \r | |
150 | getInitGetter: function () {\r | |
151 | return this.initGetter || (this.root.initGetter = this.makeInitGetter());\r | |
152 | },\r | |
153 | \r | |
154 | getSetter: function () {\r | |
155 | return this.setter || (this.root.setter = this.makeSetter());\r | |
156 | },\r | |
157 | \r | |
158 | getEventedSetter: function () {\r | |
159 | return this.eventedSetter || (this.root.eventedSetter = this.makeEventedSetter());\r | |
160 | },\r | |
161 | \r | |
162 | /**\r | |
163 | * Returns the name of the property that stores this config on the given instance or\r | |
164 | * class prototype.\r | |
165 | * @param {Object} target\r | |
166 | * @return {String}\r | |
167 | */\r | |
168 | getInternalName: function (target) {\r | |
169 | return target.$configPrefixed ? this.names.internal : this.name;\r | |
170 | },\r | |
171 | \r | |
172 | mergeNew: function (newValue, oldValue, target, mixinClass) {\r | |
173 | var ret, key;\r | |
174 | \r | |
175 | if (!oldValue) {\r | |
176 | ret = newValue;\r | |
177 | } else if (!newValue) {\r | |
178 | ret = oldValue;\r | |
179 | } else {\r | |
180 | ret = Ext.Object.chain(oldValue);\r | |
181 | \r | |
182 | for (key in newValue) {\r | |
183 | if (!mixinClass || !(key in ret)) {\r | |
184 | ret[key] = newValue[key];\r | |
185 | }\r | |
186 | }\r | |
187 | }\r | |
188 | return ret;\r | |
189 | },\r | |
190 | \r | |
191 | /**\r | |
192 | * Merges the `newValue` and the `oldValue` assuming that these are basically objects\r | |
193 | * the represent sets. For example something like:\r | |
194 | *\r | |
195 | * {\r | |
196 | * foo: true,\r | |
197 | * bar: true\r | |
198 | * }\r | |
199 | *\r | |
200 | * The merge process converts arrays like the following into the above:\r | |
201 | *\r | |
202 | * [ 'foo', 'bar' ]\r | |
203 | *\r | |
204 | * @param {String/String[]/Object} newValue\r | |
205 | * @param {Object} oldValue\r | |
206 | * @param {Boolean} [preserveExisting=false]\r | |
207 | * @return {Object}\r | |
208 | * @private\r | |
209 | * @since 5.0.0\r | |
210 | */\r | |
211 | mergeSets: function (newValue, oldValue, preserveExisting) {\r | |
212 | var ret = oldValue ? Ext.Object.chain(oldValue) : {},\r | |
213 | i, val;\r | |
214 | \r | |
215 | if (newValue instanceof Array) {\r | |
216 | for (i = newValue.length; i--; ) {\r | |
217 | val = newValue[i];\r | |
218 | if (!preserveExisting || !(val in ret)) {\r | |
219 | ret[val] = true;\r | |
220 | }\r | |
221 | }\r | |
222 | } else if (newValue) {\r | |
223 | if (newValue.constructor === Object) {\r | |
224 | for (i in newValue) {\r | |
225 | val = newValue[i];\r | |
226 | if (!preserveExisting || !(i in ret)) {\r | |
227 | ret[i] = val;\r | |
228 | }\r | |
229 | }\r | |
230 | } else if (!preserveExisting || !(newValue in ret)) {\r | |
231 | ret[newValue] = true;\r | |
232 | }\r | |
233 | }\r | |
234 | \r | |
235 | return ret;\r | |
236 | },\r | |
237 | \r | |
238 | //--------------------------------------------------\r | |
239 | // Factories\r | |
240 | \r | |
241 | makeGetter: function () {\r | |
242 | var name = this.name,\r | |
243 | prefixedName = this.names.internal;\r | |
244 | \r | |
245 | return function () {\r | |
246 | var internalName = this.$configPrefixed ? prefixedName : name;\r | |
247 | return this[internalName];\r | |
248 | };\r | |
249 | },\r | |
250 | \r | |
251 | makeInitGetter: function () {\r | |
252 | var name = this.name,\r | |
253 | names = this.names,\r | |
254 | setName = names.set,\r | |
255 | getName = names.get,\r | |
256 | initializingName = names.initializing;\r | |
257 | \r | |
258 | return function () {\r | |
259 | var me = this;\r | |
260 | \r | |
261 | me[initializingName] = true;\r | |
262 | // Remove the initGetter from the instance now that the value has been set.\r | |
263 | delete me[getName];\r | |
264 | \r | |
265 | me[setName](me.config[name]);\r | |
266 | delete me[initializingName];\r | |
267 | \r | |
268 | return me[getName].apply(me, arguments);\r | |
269 | };\r | |
270 | },\r | |
271 | \r | |
272 | makeSetter: function () {\r | |
273 | var name = this.name,\r | |
274 | names = this.names,\r | |
275 | prefixedName = names.internal,\r | |
276 | getName = names.get,\r | |
277 | applyName = names.apply,\r | |
278 | updateName = names.update,\r | |
279 | setter;\r | |
280 | \r | |
281 | // http://jsperf.com/method-call-apply-or-direct\r | |
282 | // http://jsperf.com/method-detect-invoke\r | |
283 | setter = function (value) {\r | |
284 | var me = this,\r | |
285 | internalName = me.$configPrefixed ? prefixedName : name,\r | |
286 | oldValue = me[internalName];\r | |
287 | \r | |
288 | // Remove the initGetter from the instance now that the value has been set.\r | |
289 | delete me[getName];\r | |
290 | \r | |
291 | if (!me[applyName] || (value = me[applyName](value, oldValue)) !== undefined) {\r | |
292 | // The old value might have been changed at this point\r | |
293 | // (after the apply call chain) so it should be read again\r | |
294 | if (value !== (oldValue = me[internalName])) {\r | |
295 | me[internalName] = value;\r | |
296 | \r | |
297 | if (me[updateName]) {\r | |
298 | me[updateName](value, oldValue);\r | |
299 | }\r | |
300 | }\r | |
301 | }\r | |
302 | \r | |
303 | return me;\r | |
304 | };\r | |
305 | \r | |
306 | setter.$isDefault = true;\r | |
307 | \r | |
308 | return setter;\r | |
309 | },\r | |
310 | \r | |
311 | makeEventedSetter: function () {\r | |
312 | var name = this.name,\r | |
313 | names = this.names,\r | |
314 | prefixedName = names.internal,\r | |
315 | getName = names.get,\r | |
316 | applyName = names.apply,\r | |
317 | updateName = names.update,\r | |
318 | changeEventName = names.changeEvent,\r | |
319 | updateFn = function (me, value, oldValue, internalName) {\r | |
320 | me[internalName] = value;\r | |
321 | if (me[updateName]) {\r | |
322 | me[updateName](value, oldValue);\r | |
323 | }\r | |
324 | },\r | |
325 | setter;\r | |
326 | \r | |
327 | // http://jsperf.com/method-call-apply-or-direct\r | |
328 | // http://jsperf.com/method-detect-invoke\r | |
329 | setter = function (value) {\r | |
330 | var me = this,\r | |
331 | internalName = me.$configPrefixed ? prefixedName : name,\r | |
332 | oldValue = me[internalName];\r | |
333 | \r | |
334 | // Remove the initGetter from the instance now that the value has been set.\r | |
335 | delete me[getName];\r | |
336 | \r | |
337 | if (!me[applyName] || (value = me[applyName](value, oldValue)) !== undefined) {\r | |
338 | // The old value might have been changed at this point\r | |
339 | // (after the apply call chain) so it should be read again\r | |
340 | if (value !== (oldValue = me[internalName])) {\r | |
341 | \r | |
342 | if (me.isConfiguring) {\r | |
343 | me[internalName] = value;\r | |
344 | \r | |
345 | if (me[updateName]) {\r | |
346 | me[updateName](value, oldValue);\r | |
347 | }\r | |
348 | } else {\r | |
349 | me.fireEventedAction(changeEventName, [me, value, oldValue],\r | |
350 | updateFn, me, [me, value, oldValue, internalName]);\r | |
351 | }\r | |
352 | }\r | |
353 | }\r | |
354 | \r | |
355 | return me;\r | |
356 | };\r | |
357 | \r | |
358 | setter.$isDefault = true;\r | |
359 | \r | |
360 | return setter;\r | |
361 | }\r | |
362 | };\r |