]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | /**\r |
2 | * @class Ext.Class\r | |
3 | *\r | |
4 | * This is a low level factory that is used by {@link Ext#define Ext.define} and should not be used\r | |
5 | * directly in application code.\r | |
6 | * \r | |
7 | * The configs of this class are intended to be used in `Ext.define` calls to describe the class you\r | |
8 | * are declaring. For example:\r | |
9 | * \r | |
10 | * Ext.define('App.util.Thing', {\r | |
11 | * extend: 'App.util.Other',\r | |
12 | * \r | |
13 | * alias: 'util.thing',\r | |
14 | * \r | |
15 | * config: {\r | |
16 | * foo: 42\r | |
17 | * }\r | |
18 | * });\r | |
19 | *\r | |
20 | * Ext.Class is the factory and **not** the superclass of everything. For the base class that **all**\r | |
21 | * classes inherit from, see {@link Ext.Base}.\r | |
22 | */\r | |
23 | (function() {\r | |
24 | // @tag class\r | |
25 | // @define Ext.Class\r | |
26 | // @require Ext.Base\r | |
27 | // @require Ext.Util\r | |
28 | // @require Ext.util.Cache\r | |
29 | var ExtClass,\r | |
30 | Base = Ext.Base,\r | |
31 | baseStaticMembers = Base.$staticMembers,\r | |
32 | ruleKeySortFn = function (a, b) {\r | |
33 | // longest to shortest, by text if names are equal\r | |
34 | return (a.length - b.length) || ((a < b) ? -1 : ((a > b) ? 1 : 0));\r | |
35 | };\r | |
36 | \r | |
37 | // Creates a constructor that has nothing extra in its scope chain.\r | |
38 | function makeCtor (className) {\r | |
39 | function constructor () {\r | |
40 | // Opera has some problems returning from a constructor when Dragonfly isn't running. The || null seems to\r | |
41 | // be sufficient to stop it misbehaving. Known to be required against 10.53, 11.51 and 11.61.\r | |
42 | return this.constructor.apply(this, arguments) || null;\r | |
43 | }\r | |
44 | //<debug>\r | |
45 | if (className) {\r | |
46 | constructor.name = className;\r | |
47 | }\r | |
48 | //</debug>\r | |
49 | return constructor;\r | |
50 | }\r | |
51 | \r | |
52 | /**\r | |
53 | * @method constructor\r | |
54 | * Create a new anonymous class.\r | |
55 | *\r | |
56 | * @param {Object} data An object represent the properties of this class\r | |
57 | * @param {Function} onCreated Optional, the callback function to be executed when this class is fully created.\r | |
58 | * Note that the creation process can be asynchronous depending on the pre-processors used.\r | |
59 | *\r | |
60 | * @return {Ext.Base} The newly created class\r | |
61 | */\r | |
62 | Ext.Class = ExtClass = function(Class, data, onCreated) {\r | |
63 | if (typeof Class != 'function') {\r | |
64 | onCreated = data;\r | |
65 | data = Class;\r | |
66 | Class = null;\r | |
67 | }\r | |
68 | \r | |
69 | if (!data) {\r | |
70 | data = {};\r | |
71 | }\r | |
72 | \r | |
73 | Class = ExtClass.create(Class, data);\r | |
74 | \r | |
75 | ExtClass.process(Class, data, onCreated);\r | |
76 | \r | |
77 | return Class;\r | |
78 | };\r | |
79 | \r | |
80 | Ext.apply(ExtClass, {\r | |
81 | \r | |
82 | makeCtor: makeCtor,\r | |
83 | \r | |
84 | /**\r | |
85 | * @private\r | |
86 | */\r | |
87 | onBeforeCreated: function(Class, data, hooks) {\r | |
88 | //<debug>\r | |
89 | Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '>> Ext.Class#onBeforeCreated', arguments);\r | |
90 | //</debug>\r | |
91 | \r | |
92 | Class.addMembers(data);\r | |
93 | \r | |
94 | hooks.onCreated.call(Class, Class);\r | |
95 | \r | |
96 | //<debug>\r | |
97 | Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '<< Ext.Class#onBeforeCreated', arguments);\r | |
98 | //</debug>\r | |
99 | },\r | |
100 | \r | |
101 | /**\r | |
102 | * @private\r | |
103 | */\r | |
104 | create: function (Class, data) {\r | |
105 | var i = baseStaticMembers.length,\r | |
106 | name;\r | |
107 | \r | |
108 | if (!Class) {\r | |
109 | Class = makeCtor(\r | |
110 | //<debug>\r | |
111 | data.$className\r | |
112 | //</debug>\r | |
113 | );\r | |
114 | }\r | |
115 | \r | |
116 | while (i--) {\r | |
117 | name = baseStaticMembers[i];\r | |
118 | Class[name] = Base[name];\r | |
119 | }\r | |
120 | \r | |
121 | return Class;\r | |
122 | },\r | |
123 | \r | |
124 | /**\r | |
125 | * @private\r | |
126 | */\r | |
127 | process: function(Class, data, onCreated) {\r | |
128 | var preprocessorStack = data.preprocessors || ExtClass.defaultPreprocessors,\r | |
129 | registeredPreprocessors = this.preprocessors,\r | |
130 | hooks = {\r | |
131 | onBeforeCreated: this.onBeforeCreated\r | |
132 | },\r | |
133 | preprocessors = [],\r | |
134 | preprocessor, preprocessorsProperties,\r | |
135 | i, ln, j, subLn, preprocessorProperty;\r | |
136 | \r | |
137 | delete data.preprocessors;\r | |
138 | Class._classHooks = hooks;\r | |
139 | \r | |
140 | for (i = 0,ln = preprocessorStack.length; i < ln; i++) {\r | |
141 | preprocessor = preprocessorStack[i];\r | |
142 | \r | |
143 | if (typeof preprocessor == 'string') {\r | |
144 | preprocessor = registeredPreprocessors[preprocessor];\r | |
145 | preprocessorsProperties = preprocessor.properties;\r | |
146 | \r | |
147 | if (preprocessorsProperties === true) {\r | |
148 | preprocessors.push(preprocessor.fn);\r | |
149 | }\r | |
150 | else if (preprocessorsProperties) {\r | |
151 | for (j = 0,subLn = preprocessorsProperties.length; j < subLn; j++) {\r | |
152 | preprocessorProperty = preprocessorsProperties[j];\r | |
153 | \r | |
154 | if (data.hasOwnProperty(preprocessorProperty)) {\r | |
155 | preprocessors.push(preprocessor.fn);\r | |
156 | break;\r | |
157 | }\r | |
158 | }\r | |
159 | }\r | |
160 | }\r | |
161 | else {\r | |
162 | preprocessors.push(preprocessor);\r | |
163 | }\r | |
164 | }\r | |
165 | \r | |
166 | hooks.onCreated = onCreated ? onCreated : Ext.emptyFn;\r | |
167 | hooks.preprocessors = preprocessors;\r | |
168 | \r | |
169 | this.doProcess(Class, data, hooks);\r | |
170 | },\r | |
171 | \r | |
172 | doProcess: function(Class, data, hooks) {\r | |
173 | var me = this,\r | |
174 | preprocessors = hooks.preprocessors,\r | |
175 | preprocessor = preprocessors.shift(),\r | |
176 | doProcess = me.doProcess;\r | |
177 | \r | |
178 | for ( ; preprocessor ; preprocessor = preprocessors.shift()) {\r | |
179 | // Returning false signifies an asynchronous preprocessor - it will call doProcess when we can continue\r | |
180 | if (preprocessor.call(me, Class, data, hooks, doProcess) === false) {\r | |
181 | return;\r | |
182 | }\r | |
183 | }\r | |
184 | hooks.onBeforeCreated.apply(me, arguments);\r | |
185 | },\r | |
186 | \r | |
187 | /**\r | |
188 | * @private\r | |
189 | * */\r | |
190 | preprocessors: {},\r | |
191 | \r | |
192 | /**\r | |
193 | * Register a new pre-processor to be used during the class creation process\r | |
194 | *\r | |
195 | * @param {String} name The pre-processor's name\r | |
196 | * @param {Function} fn The callback function to be executed. Typical format:\r | |
197 | *\r | |
198 | * function(cls, data, fn) {\r | |
199 | * // Your code here\r | |
200 | *\r | |
201 | * // Execute this when the processing is finished.\r | |
202 | * // Asynchronous processing is perfectly ok\r | |
203 | * if (fn) {\r | |
204 | * fn.call(this, cls, data);\r | |
205 | * }\r | |
206 | * });\r | |
207 | *\r | |
208 | * @param {Function} fn.cls The created class\r | |
209 | * @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor\r | |
210 | * @param {Function} fn.fn The callback function that **must** to be executed when this\r | |
211 | * pre-processor finishes, regardless of whether the processing is synchronous or asynchronous.\r | |
212 | * @return {Ext.Class} this\r | |
213 | * @private\r | |
214 | * @static\r | |
215 | */\r | |
216 | registerPreprocessor: function(name, fn, properties, position, relativeTo) {\r | |
217 | if (!position) {\r | |
218 | position = 'last';\r | |
219 | }\r | |
220 | \r | |
221 | if (!properties) {\r | |
222 | properties = [name];\r | |
223 | }\r | |
224 | \r | |
225 | this.preprocessors[name] = {\r | |
226 | name: name,\r | |
227 | properties: properties || false,\r | |
228 | fn: fn\r | |
229 | };\r | |
230 | \r | |
231 | this.setDefaultPreprocessorPosition(name, position, relativeTo);\r | |
232 | \r | |
233 | return this;\r | |
234 | },\r | |
235 | \r | |
236 | /**\r | |
237 | * Retrieve a pre-processor callback function by its name, which has been registered before\r | |
238 | *\r | |
239 | * @param {String} name\r | |
240 | * @return {Function} preprocessor\r | |
241 | * @private\r | |
242 | * @static\r | |
243 | */\r | |
244 | getPreprocessor: function(name) {\r | |
245 | return this.preprocessors[name];\r | |
246 | },\r | |
247 | \r | |
248 | /**\r | |
249 | * @private\r | |
250 | */\r | |
251 | getPreprocessors: function() {\r | |
252 | return this.preprocessors;\r | |
253 | },\r | |
254 | \r | |
255 | /**\r | |
256 | * @private\r | |
257 | */\r | |
258 | defaultPreprocessors: [],\r | |
259 | \r | |
260 | /**\r | |
261 | * Retrieve the array stack of default pre-processors\r | |
262 | * @return {Function[]} defaultPreprocessors\r | |
263 | * @private\r | |
264 | * @static\r | |
265 | */\r | |
266 | getDefaultPreprocessors: function() {\r | |
267 | return this.defaultPreprocessors;\r | |
268 | },\r | |
269 | \r | |
270 | /**\r | |
271 | * Set the default array stack of default pre-processors\r | |
272 | *\r | |
273 | * @private\r | |
274 | * @param {Array} preprocessors\r | |
275 | * @return {Ext.Class} this\r | |
276 | * @static\r | |
277 | */\r | |
278 | setDefaultPreprocessors: function(preprocessors) {\r | |
279 | this.defaultPreprocessors = Ext.Array.from(preprocessors);\r | |
280 | \r | |
281 | return this;\r | |
282 | },\r | |
283 | \r | |
284 | /**\r | |
285 | * Insert this pre-processor at a specific position in the stack, optionally relative to\r | |
286 | * any existing pre-processor. For example:\r | |
287 | *\r | |
288 | * Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {\r | |
289 | * // Your code here\r | |
290 | *\r | |
291 | * if (fn) {\r | |
292 | * fn.call(this, cls, data);\r | |
293 | * }\r | |
294 | * }).setDefaultPreprocessorPosition('debug', 'last');\r | |
295 | *\r | |
296 | * @private\r | |
297 | * @param {String} name The pre-processor name. Note that it needs to be registered with\r | |
298 | * {@link Ext.Class#registerPreprocessor registerPreprocessor} before this\r | |
299 | * @param {String} offset The insertion position. Four possible values are:\r | |
300 | * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)\r | |
301 | * @param {String} relativeName\r | |
302 | * @return {Ext.Class} this\r | |
303 | * @static\r | |
304 | */\r | |
305 | setDefaultPreprocessorPosition: function(name, offset, relativeName) {\r | |
306 | var defaultPreprocessors = this.defaultPreprocessors,\r | |
307 | index;\r | |
308 | \r | |
309 | if (typeof offset == 'string') {\r | |
310 | if (offset === 'first') {\r | |
311 | defaultPreprocessors.unshift(name);\r | |
312 | \r | |
313 | return this;\r | |
314 | }\r | |
315 | else if (offset === 'last') {\r | |
316 | defaultPreprocessors.push(name);\r | |
317 | \r | |
318 | return this;\r | |
319 | }\r | |
320 | \r | |
321 | offset = (offset === 'after') ? 1 : -1;\r | |
322 | }\r | |
323 | \r | |
324 | index = Ext.Array.indexOf(defaultPreprocessors, relativeName);\r | |
325 | \r | |
326 | if (index !== -1) {\r | |
327 | Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);\r | |
328 | }\r | |
329 | \r | |
330 | return this;\r | |
331 | }\r | |
332 | });\r | |
333 | \r | |
334 | /**\r | |
335 | * @cfg {String} extend\r | |
336 | * The parent class that this class extends. For example:\r | |
337 | *\r | |
338 | * Ext.define('Person', {\r | |
339 | * say: function(text) { alert(text); }\r | |
340 | * });\r | |
341 | *\r | |
342 | * Ext.define('Developer', {\r | |
343 | * extend: 'Person',\r | |
344 | * say: function(text) { this.callParent(["print "+text]); }\r | |
345 | * });\r | |
346 | */\r | |
347 | ExtClass.registerPreprocessor('extend', function(Class, data, hooks) {\r | |
348 | //<debug>\r | |
349 | Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extendPreProcessor', arguments);\r | |
350 | //</debug>\r | |
351 | \r | |
352 | var Base = Ext.Base,\r | |
353 | basePrototype = Base.prototype,\r | |
354 | extend = data.extend,\r | |
355 | Parent, parentPrototype, i;\r | |
356 | \r | |
357 | delete data.extend;\r | |
358 | \r | |
359 | if (extend && extend !== Object) {\r | |
360 | Parent = extend;\r | |
361 | }\r | |
362 | else {\r | |
363 | Parent = Base;\r | |
364 | }\r | |
365 | \r | |
366 | parentPrototype = Parent.prototype;\r | |
367 | \r | |
368 | if (!Parent.$isClass) {\r | |
369 | for (i in basePrototype) {\r | |
370 | if (!parentPrototype[i]) {\r | |
371 | parentPrototype[i] = basePrototype[i];\r | |
372 | }\r | |
373 | }\r | |
374 | }\r | |
375 | \r | |
376 | Class.extend(Parent);\r | |
377 | \r | |
378 | Class.triggerExtended.apply(Class, arguments);\r | |
379 | \r | |
380 | if (data.onClassExtended) {\r | |
381 | Class.onExtended(data.onClassExtended, Class);\r | |
382 | delete data.onClassExtended;\r | |
383 | }\r | |
384 | \r | |
385 | }, true); // true to always run this preprocessor even w/o "extend" keyword\r | |
386 | \r | |
387 | /**\r | |
388 | * @cfg {Object} privates\r | |
389 | * The `privates` config is a list of methods intended to be used internally by the \r | |
390 | * framework. Methods are placed in a `privates` block to prevent developers from \r | |
391 | * accidentally overriding framework methods in custom classes.\r | |
392 | *\r | |
393 | * Ext.define('Computer', {\r | |
394 | * privates: {\r | |
395 | * runFactory: function(brand) {\r | |
396 | * // internal only processing of brand passed to factory\r | |
397 | * this.factory(brand);\r | |
398 | * }\r | |
399 | * },\r | |
400 | * \r | |
401 | * factory: function (brand) {}\r | |
402 | * });\r | |
403 | * \r | |
404 | * In order to override a method from a `privates` block, the overridden method must \r | |
405 | * also be placed in a `privates` block within the override class.\r | |
406 | * \r | |
407 | * Ext.define('Override.Computer', {\r | |
408 | * override: 'Computer',\r | |
409 | * privates: {\r | |
410 | * runFactory: function() {\r | |
411 | * // overriding logic\r | |
412 | * }\r | |
413 | * }\r | |
414 | * });\r | |
415 | */\r | |
416 | ExtClass.registerPreprocessor('privates', function(Class, data) {\r | |
417 | //<debug>\r | |
418 | Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#privatePreprocessor', arguments);\r | |
419 | //</debug>\r | |
420 | \r | |
421 | var privates = data.privates,\r | |
422 | statics = privates.statics,\r | |
423 | privacy = privates.privacy || true;\r | |
424 | \r | |
425 | delete data.privates;\r | |
426 | delete privates.statics;\r | |
427 | \r | |
428 | // We have to add this preprocessor so that private getters/setters are picked up\r | |
429 | // by the config system. This also catches duplication in the public part of the\r | |
430 | // class since it is an error to override a private method with a public one.\r | |
431 | Class.addMembers(privates, false, privacy);\r | |
432 | if (statics) {\r | |
433 | Class.addMembers(statics, true, privacy);\r | |
434 | }\r | |
435 | });\r | |
436 | \r | |
437 | //<feature classSystem.statics>\r | |
438 | /**\r | |
439 | * @cfg {Object} statics\r | |
440 | * List of static methods for this class. For example:\r | |
441 | *\r | |
442 | * Ext.define('Computer', {\r | |
443 | * statics: {\r | |
444 | * factory: function(brand) {\r | |
445 | * // 'this' in static methods refer to the class itself\r | |
446 | * return new this(brand);\r | |
447 | * }\r | |
448 | * },\r | |
449 | *\r | |
450 | * constructor: function() { ... }\r | |
451 | * });\r | |
452 | *\r | |
453 | * var dellComputer = Computer.factory('Dell');\r | |
454 | */\r | |
455 | ExtClass.registerPreprocessor('statics', function(Class, data) {\r | |
456 | //<debug>\r | |
457 | Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#staticsPreprocessor', arguments);\r | |
458 | //</debug>\r | |
459 | \r | |
460 | Class.addStatics(data.statics);\r | |
461 | \r | |
462 | delete data.statics;\r | |
463 | });\r | |
464 | //</feature>\r | |
465 | \r | |
466 | //<feature classSystem.inheritableStatics>\r | |
467 | /**\r | |
468 | * @cfg {Object} inheritableStatics\r | |
469 | * List of inheritable static methods for this class.\r | |
470 | * Otherwise just like {@link #statics} but subclasses inherit these methods.\r | |
471 | */\r | |
472 | ExtClass.registerPreprocessor('inheritableStatics', function(Class, data) {\r | |
473 | //<debug>\r | |
474 | Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#inheritableStaticsPreprocessor', arguments);\r | |
475 | //</debug>\r | |
476 | \r | |
477 | Class.addInheritableStatics(data.inheritableStatics);\r | |
478 | \r | |
479 | delete data.inheritableStatics;\r | |
480 | });\r | |
481 | //</feature>\r | |
482 | \r | |
483 | Ext.createRuleFn = function (code) {\r | |
484 | return new Function('$c', 'with($c) { return (' + code + '); }');\r | |
485 | };\r | |
486 | Ext.expressionCache = new Ext.util.Cache({\r | |
487 | miss: Ext.createRuleFn\r | |
488 | });\r | |
489 | \r | |
490 | Ext.ruleKeySortFn = ruleKeySortFn;\r | |
491 | Ext.getPlatformConfigKeys = function (platformConfig) {\r | |
492 | var ret = [],\r | |
493 | platform, rule;\r | |
494 | \r | |
495 | for (platform in platformConfig) {\r | |
496 | rule = Ext.expressionCache.get(platform);\r | |
497 | if (rule(Ext.platformTags)) {\r | |
498 | ret.push(platform);\r | |
499 | }\r | |
500 | }\r | |
501 | \r | |
502 | ret.sort(ruleKeySortFn);\r | |
503 | return ret;\r | |
504 | };\r | |
505 | \r | |
506 | //<feature classSystem.platformConfig>\r | |
507 | /**\r | |
508 | * @cfg {Object} platformConfig\r | |
509 | * Allows setting config values for a class based on specific platforms. The value\r | |
510 | * of this config is an object whose properties are "rules" and whose values are\r | |
511 | * objects containing config values.\r | |
512 | *\r | |
513 | * For example:\r | |
514 | *\r | |
515 | * Ext.define('App.view.Foo', {\r | |
516 | * extend: 'Ext.panel.Panel',\r | |
517 | *\r | |
518 | * platformConfig: {\r | |
519 | * desktop: {\r | |
520 | * title: 'Some Rather Descriptive Title'\r | |
521 | * },\r | |
522 | *\r | |
523 | * '!desktop': {\r | |
524 | * title: 'Short Title'\r | |
525 | * }\r | |
526 | * }\r | |
527 | * });\r | |
528 | *\r | |
529 | * In the above, "desktop" and "!desktop" are (mutually exclusive) rules. Whichever\r | |
530 | * evaluates to `true` will have its configs applied to the class. In this case, only\r | |
531 | * the "title" property, but the object can contain any number of config properties.\r | |
532 | * In this case, the `platformConfig` is evaluated as part of the class and there is\r | |
533 | * not cost for each instance created.\r | |
534 | *\r | |
535 | * The rules are evaluated expressions in the context of the platform tags contained\r | |
536 | * in `{@link Ext#platformTags Ext.platformTags}`. Any properties of that object are\r | |
537 | * implicitly usable (as shown above).\r | |
538 | *\r | |
539 | * If a `platformConfig` specifies a config value, it will replace any values declared\r | |
540 | * on the class itself.\r | |
541 | *\r | |
542 | * Use of `platformConfig` on instances is handled by the config system when classes\r | |
543 | * call `{@link Ext.Base#initConfig initConfig}`. For example:\r | |
544 | *\r | |
545 | * Ext.create({\r | |
546 | * xtype: 'panel',\r | |
547 | *\r | |
548 | * platformConfig: {\r | |
549 | * desktop: {\r | |
550 | * title: 'Some Rather Descriptive Title'\r | |
551 | * },\r | |
552 | *\r | |
553 | * '!desktop': {\r | |
554 | * title: 'Short Title'\r | |
555 | * }\r | |
556 | * }\r | |
557 | * });\r | |
558 | *\r | |
559 | * The following is equivalent to the above:\r | |
560 | *\r | |
561 | * if (Ext.platformTags.desktop) {\r | |
562 | * Ext.create({\r | |
563 | * xtype: 'panel',\r | |
564 | * title: 'Some Rather Descriptive Title'\r | |
565 | * });\r | |
566 | * } else {\r | |
567 | * Ext.create({\r | |
568 | * xtype: 'panel',\r | |
569 | * title: 'Short Title'\r | |
570 | * });\r | |
571 | * }\r | |
572 | *\r | |
573 | * To adjust configs based on dynamic conditions, see `{@link Ext.mixin.Responsive}`.\r | |
574 | */\r | |
575 | ExtClass.registerPreprocessor('platformConfig', function(Class, data, hooks) {\r | |
576 | var platformConfigs = data.platformConfig,\r | |
577 | config = data.config,\r | |
578 | added, classConfigs, configs, configurator, hoisted, keys, name, value,\r | |
579 | i, ln;\r | |
580 | \r | |
581 | delete data.platformConfig;\r | |
582 | \r | |
583 | \r | |
584 | //<debug>\r | |
585 | if (platformConfigs instanceof Array) {\r | |
586 | throw new Error('platformConfigs must be specified as an object.');\r | |
587 | }\r | |
588 | //</debug>\r | |
589 | \r | |
590 | configurator = Class.getConfigurator();\r | |
591 | classConfigs = configurator.configs;\r | |
592 | \r | |
593 | // Get the keys shortest to longest (ish).\r | |
594 | keys = Ext.getPlatformConfigKeys(platformConfigs);\r | |
595 | \r | |
596 | // To leverage the Configurator#add method, we want to generate potentially\r | |
597 | // two objects to pass in: "added" and "hoisted". For any properties in an\r | |
598 | // active platformConfig rule that set proper Configs in the base class, we\r | |
599 | // need to put them in "added". If instead of the proper Config coming from\r | |
600 | // a base class, it comes from this class's config block, we still need to\r | |
601 | // put that config in "added" but we also need move the class-level config\r | |
602 | // out of "config" and into "hoisted".\r | |
603 | //\r | |
604 | // This will ensure that the config defined at the class level is added to\r | |
605 | // the Configurator first.\r | |
606 | for (i = 0, ln = keys.length; i < ln; ++i) {\r | |
607 | configs = platformConfigs[keys[i]];\r | |
608 | hoisted = added = null;\r | |
609 | \r | |
610 | for (name in configs) {\r | |
611 | value = configs[name];\r | |
612 | \r | |
613 | // We have a few possibilities for each config name:\r | |
614 | \r | |
615 | if (config && name in config) {\r | |
616 | // It is a proper Config defined by this class.\r | |
617 | \r | |
618 | (added || (added = {}))[name] = value;\r | |
619 | (hoisted || (hoisted = {}))[name] = config[name];\r | |
620 | delete config[name];\r | |
621 | } else if (name in classConfigs) {\r | |
622 | // It is a proper Config defined by a base class.\r | |
623 | \r | |
624 | (added || (added = {}))[name] = value;\r | |
625 | } else {\r | |
626 | // It is just a property to put on the prototype.\r | |
627 | \r | |
628 | data[name] = value;\r | |
629 | }\r | |
630 | }\r | |
631 | \r | |
632 | if (hoisted) {\r | |
633 | configurator.add(hoisted);\r | |
634 | }\r | |
635 | if (added) {\r | |
636 | configurator.add(added);\r | |
637 | }\r | |
638 | }\r | |
639 | });\r | |
640 | //</feature>\r | |
641 | \r | |
642 | //<feature classSystem.config>\r | |
643 | /**\r | |
644 | * @cfg {Object} config\r | |
645 | *\r | |
646 | * List of configuration options with their default values.\r | |
647 | *\r | |
648 | * __Note:__ You need to make sure {@link Ext.Base#initConfig} is called from your constructor if you are defining\r | |
649 | * your own class or singleton, unless you are extending a Component. Otherwise the generated getter and setter\r | |
650 | * methods will not be initialized.\r | |
651 | *\r | |
652 | * Each config item will have its own setter and getter method automatically generated inside the class prototype\r | |
653 | * during class creation time, if the class does not have those methods explicitly defined.\r | |
654 | *\r | |
655 | * As an example, let's convert the name property of a Person class to be a config item, then add extra age and\r | |
656 | * gender items.\r | |
657 | *\r | |
658 | * Ext.define('My.sample.Person', {\r | |
659 | * config: {\r | |
660 | * name: 'Mr. Unknown',\r | |
661 | * age: 0,\r | |
662 | * gender: 'Male'\r | |
663 | * },\r | |
664 | *\r | |
665 | * constructor: function(config) {\r | |
666 | * this.initConfig(config);\r | |
667 | *\r | |
668 | * return this;\r | |
669 | * }\r | |
670 | *\r | |
671 | * // ...\r | |
672 | * });\r | |
673 | *\r | |
674 | * Within the class, this.name still has the default value of "Mr. Unknown". However, it's now publicly accessible\r | |
675 | * without sacrificing encapsulation, via setter and getter methods.\r | |
676 | *\r | |
677 | * var jacky = new Person({\r | |
678 | * name: "Jacky",\r | |
679 | * age: 35\r | |
680 | * });\r | |
681 | *\r | |
682 | * alert(jacky.getAge()); // alerts 35\r | |
683 | * alert(jacky.getGender()); // alerts "Male"\r | |
684 | *\r | |
685 | * jacky.walk(10); // alerts "Jacky is walking 10 steps"\r | |
686 | *\r | |
687 | * jacky.setName("Mr. Nguyen");\r | |
688 | * alert(jacky.getName()); // alerts "Mr. Nguyen"\r | |
689 | *\r | |
690 | * jacky.walk(10); // alerts "Mr. Nguyen is walking 10 steps"\r | |
691 | *\r | |
692 | * Notice that we changed the class constructor to invoke this.initConfig() and pass in the provided config object.\r | |
693 | * Two key things happened:\r | |
694 | *\r | |
695 | * - The provided config object when the class is instantiated is recursively merged with the default config object.\r | |
696 | * - All corresponding setter methods are called with the merged values.\r | |
697 | *\r | |
698 | * Beside storing the given values, throughout the frameworks, setters generally have two key responsibilities:\r | |
699 | *\r | |
700 | * - Filtering / validation / transformation of the given value before it's actually stored within the instance.\r | |
701 | * - Notification (such as firing events) / post-processing after the value has been set, or changed from a\r | |
702 | * previous value.\r | |
703 | *\r | |
704 | * By standardize this common pattern, the default generated setters provide two extra template methods that you\r | |
705 | * can put your own custom logics into, i.e: an "applyFoo" and "updateFoo" method for a "foo" config item, which are\r | |
706 | * executed before and after the value is actually set, respectively. Back to the example class, let's validate that\r | |
707 | * age must be a valid positive number, and fire an 'agechange' if the value is modified.\r | |
708 | *\r | |
709 | * Ext.define('My.sample.Person', {\r | |
710 | * config: {\r | |
711 | * // ...\r | |
712 | * },\r | |
713 | *\r | |
714 | * constructor: {\r | |
715 | * // ...\r | |
716 | * },\r | |
717 | *\r | |
718 | * applyAge: function(age) {\r | |
719 | * if (typeof age !== 'number' || age < 0) {\r | |
720 | * console.warn("Invalid age, must be a positive number");\r | |
721 | * return;\r | |
722 | * }\r | |
723 | *\r | |
724 | * return age;\r | |
725 | * },\r | |
726 | *\r | |
727 | * updateAge: function(newAge, oldAge) {\r | |
728 | * // age has changed from "oldAge" to "newAge"\r | |
729 | * this.fireEvent('agechange', this, newAge, oldAge);\r | |
730 | * }\r | |
731 | *\r | |
732 | * // ...\r | |
733 | * });\r | |
734 | *\r | |
735 | * var jacky = new Person({\r | |
736 | * name: "Jacky",\r | |
737 | * age: 'invalid'\r | |
738 | * });\r | |
739 | *\r | |
740 | * alert(jacky.getAge()); // alerts 0\r | |
741 | *\r | |
742 | * alert(jacky.setAge(-100)); // alerts 0\r | |
743 | * alert(jacky.getAge()); // alerts 0\r | |
744 | *\r | |
745 | * alert(jacky.setAge(35)); // alerts 0\r | |
746 | * alert(jacky.getAge()); // alerts 35\r | |
747 | *\r | |
748 | * In other words, when leveraging the config feature, you mostly never need to define setter and getter methods\r | |
749 | * explicitly. Instead, "apply*" and "update*" methods should be implemented where necessary. Your code will be\r | |
750 | * consistent throughout and only contain the minimal logic that you actually care about.\r | |
751 | *\r | |
752 | * When it comes to inheritance, the default config of the parent class is automatically, recursively merged with\r | |
753 | * the child's default config. The same applies for mixins.\r | |
754 | */\r | |
755 | ExtClass.registerPreprocessor('config', function(Class, data) {\r | |
756 | // Need to copy to the prototype here because that happens after preprocessors\r | |
757 | if (data.hasOwnProperty('$configPrefixed')) {\r | |
758 | Class.prototype.$configPrefixed = data.$configPrefixed;\r | |
759 | }\r | |
760 | Class.addConfig(data.config);\r | |
761 | \r | |
762 | // We need to remove this or it will be applied by addMembers and smash the\r | |
763 | // "config" placed on the prototype by Configurator (which includes *all* configs\r | |
764 | // such as cachedConfigs).\r | |
765 | delete data.config;\r | |
766 | });\r | |
767 | //</feature>\r | |
768 | \r | |
769 | //<feature classSystem.cachedConfig>\r | |
770 | /**\r | |
771 | * @cfg {Object} cachedConfig\r | |
772 | * \r | |
773 | * This configuration works in a very similar manner to the {@link #config} option.\r | |
774 | * The difference is that the configurations are only ever processed when the first instance\r | |
775 | * of that class is created. The processed value is then stored on the class prototype and\r | |
776 | * will not be processed on subsequent instances of the class. Getters/setters will be generated\r | |
777 | * in exactly the same way as {@link #config}.\r | |
778 | * \r | |
779 | * This option is useful for expensive objects that can be shared across class instances. \r | |
780 | * The class itself ensures that the creation only occurs once.\r | |
781 | */\r | |
782 | ExtClass.registerPreprocessor('cachedConfig', function(Class, data) {\r | |
783 | // Need to copy to the prototype here because that happens after preprocessors\r | |
784 | if (data.hasOwnProperty('$configPrefixed')) {\r | |
785 | Class.prototype.$configPrefixed = data.$configPrefixed;\r | |
786 | }\r | |
787 | Class.addCachedConfig(data.cachedConfig);\r | |
788 | \r | |
789 | // Remove this so it won't be placed on the prototype.\r | |
790 | delete data.cachedConfig;\r | |
791 | });\r | |
792 | //</feature>\r | |
793 | \r | |
794 | //<feature classSystem.mixins>\r | |
795 | /**\r | |
796 | * @cfg {String[]/Object} mixins\r | |
797 | * List of classes to mix into this class. For example:\r | |
798 | *\r | |
799 | * Ext.define('CanSing', {\r | |
800 | * sing: function() {\r | |
801 | * alert("For he's a jolly good fellow...")\r | |
802 | * }\r | |
803 | * });\r | |
804 | *\r | |
805 | * Ext.define('Musician', {\r | |
806 | * mixins: ['CanSing']\r | |
807 | * })\r | |
808 | *\r | |
809 | * In this case the Musician class will get a `sing` method from CanSing mixin.\r | |
810 | *\r | |
811 | * But what if the Musician already has a `sing` method? Or you want to mix\r | |
812 | * in two classes, both of which define `sing`? In such a cases it's good\r | |
813 | * to define mixins as an object, where you assign a name to each mixin:\r | |
814 | *\r | |
815 | * Ext.define('Musician', {\r | |
816 | * mixins: {\r | |
817 | * canSing: 'CanSing'\r | |
818 | * },\r | |
819 | * \r | |
820 | * sing: function() {\r | |
821 | * // delegate singing operation to mixin\r | |
822 | * this.mixins.canSing.sing.call(this);\r | |
823 | * }\r | |
824 | * })\r | |
825 | *\r | |
826 | * In this case the `sing` method of Musician will overwrite the\r | |
827 | * mixed in `sing` method. But you can access the original mixed in method\r | |
828 | * through special `mixins` property.\r | |
829 | */\r | |
830 | ExtClass.registerPreprocessor('mixins', function(Class, data, hooks) {\r | |
831 | //<debug>\r | |
832 | Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor', arguments);\r | |
833 | //</debug>\r | |
834 | \r | |
835 | var mixins = data.mixins,\r | |
836 | onCreated = hooks.onCreated;\r | |
837 | \r | |
838 | delete data.mixins;\r | |
839 | \r | |
840 | hooks.onCreated = function() {\r | |
841 | //<debug>\r | |
842 | Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor#beforeCreated', arguments);\r | |
843 | //</debug>\r | |
844 | \r | |
845 | // Put back the original onCreated before processing mixins. This allows a\r | |
846 | // mixin to hook onCreated by access Class._classHooks.\r | |
847 | hooks.onCreated = onCreated;\r | |
848 | \r | |
849 | Class.mixin(mixins);\r | |
850 | \r | |
851 | // We must go back to hooks.onCreated here because it may have changed during\r | |
852 | // calls to onClassMixedIn.\r | |
853 | return hooks.onCreated.apply(this, arguments);\r | |
854 | };\r | |
855 | });\r | |
856 | //</feature>\r | |
857 | \r | |
858 | \r | |
859 | //<feature classSystem.backwardsCompatible>\r | |
860 | // Backwards compatible\r | |
861 | Ext.extend = function(Class, Parent, members) {\r | |
862 | //<debug>\r | |
863 | Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extend-backwards-compatible', arguments);\r | |
864 | //</debug>\r | |
865 | \r | |
866 | if (arguments.length === 2 && Ext.isObject(Parent)) {\r | |
867 | members = Parent;\r | |
868 | Parent = Class;\r | |
869 | Class = null;\r | |
870 | }\r | |
871 | \r | |
872 | var cls;\r | |
873 | \r | |
874 | if (!Parent) {\r | |
875 | throw new Error("[Ext.extend] Attempting to extend from a class which has not been loaded on the page.");\r | |
876 | }\r | |
877 | \r | |
878 | members.extend = Parent;\r | |
879 | members.preprocessors = [\r | |
880 | 'extend'\r | |
881 | //<feature classSystem.statics>\r | |
882 | ,'statics'\r | |
883 | //</feature>\r | |
884 | //<feature classSystem.inheritableStatics>\r | |
885 | ,'inheritableStatics'\r | |
886 | //</feature>\r | |
887 | //<feature classSystem.mixins>\r | |
888 | ,'mixins'\r | |
889 | //</feature>\r | |
890 | //<feature classSystem.platformConfig>\r | |
891 | ,'platformConfig'\r | |
892 | //</feature>\r | |
893 | //<feature classSystem.config>\r | |
894 | ,'config'\r | |
895 | //</feature>\r | |
896 | ];\r | |
897 | \r | |
898 | if (Class) {\r | |
899 | cls = new ExtClass(Class, members);\r | |
900 | // The 'constructor' is given as 'Class' but also needs to be on prototype\r | |
901 | cls.prototype.constructor = Class;\r | |
902 | } else {\r | |
903 | cls = new ExtClass(members);\r | |
904 | }\r | |
905 | \r | |
906 | cls.prototype.override = function(o) {\r | |
907 | for (var m in o) {\r | |
908 | if (o.hasOwnProperty(m)) {\r | |
909 | this[m] = o[m];\r | |
910 | }\r | |
911 | }\r | |
912 | };\r | |
913 | \r | |
914 | return cls;\r | |
915 | };\r | |
916 | //</feature>\r | |
917 | \r | |
918 | /**\r | |
919 | * This object contains properties that describe the current device or platform. These\r | |
920 | * values can be used in `{@link Ext.Class#platformConfig platformConfig}` as well as\r | |
921 | * `{@link Ext.mixin.Responsive#responsiveConfig responsiveConfig}` statements.\r | |
922 | *\r | |
923 | * This object can be modified to include tags that are useful for the application. To\r | |
924 | * add custom properties, it is advisable to use a sub-object. For example:\r | |
925 | *\r | |
926 | * Ext.platformTags.app = {\r | |
927 | * mobile: true\r | |
928 | * };\r | |
929 | *\r | |
930 | * @property {Object} platformTags\r | |
931 | * @property {Boolean} platformTags.phone\r | |
932 | * @property {Boolean} platformTags.tablet\r | |
933 | * @property {Boolean} platformTags.desktop\r | |
934 | * @property {Boolean} platformTags.touch Indicates touch inputs are available.\r | |
935 | * @property {Boolean} platformTags.safari\r | |
936 | * @property {Boolean} platformTags.chrome\r | |
937 | * @property {Boolean} platformTags.windows\r | |
938 | * @property {Boolean} platformTags.firefox\r | |
939 | * @property {Boolean} platformTags.ios True for iPad, iPhone and iPod.\r | |
940 | * @property {Boolean} platformTags.android\r | |
941 | * @property {Boolean} platformTags.blackberry\r | |
942 | * @property {Boolean} platformTags.tizen\r | |
943 | * @member Ext\r | |
944 | */\r | |
945 | }());\r |