]> git.proxmox.com Git - extjs.git/blame - extjs/packages/core/src/class/Config.js
add extjs 6.0.1 sources
[extjs.git] / extjs / packages / core / src / class / Config.js
CommitLineData
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
32Ext.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
91Ext.Config.map = {};\r
92\r
93Ext.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
100Ext.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