]>
Commit | Line | Data |
---|---|---|
6527f429 DM |
1 | // @tag class\r |
2 | /**\r | |
3 | * @class Ext.Base\r | |
4 | *\r | |
5 | * The root of all classes created with {@link Ext#define}.\r | |
6 | *\r | |
7 | * Ext.Base is the building block of all Ext classes. All classes in Ext inherit from Ext.Base.\r | |
8 | * All prototype and static members of this class are inherited by all other classes.\r | |
9 | */\r | |
10 | Ext.Base = (function(flexSetter) {\r | |
11 | // @define Ext.Base\r | |
12 | // @require Ext.Util\r | |
13 | // @require Ext.Version\r | |
14 | // @require Ext.Configurator\r | |
15 | // @uses Ext.ClassManager\r | |
16 | var noArgs = [],\r | |
17 | baseStaticMember,\r | |
18 | baseStaticMembers = [],\r | |
19 | getConfig = function (name, peek) {\r | |
20 | var me = this,\r | |
21 | ret, cfg, getterName;\r | |
22 | \r | |
23 | if (name) {\r | |
24 | cfg = Ext.Config.map[name];\r | |
25 | //<debug>\r | |
26 | if (!cfg) {\r | |
27 | Ext.Logger.error("Invalid property name for getter: '" + name + "' for '" + me.$className + "'.");\r | |
28 | }\r | |
29 | //</debug>\r | |
30 | getterName = cfg.names.get;\r | |
31 | if (peek && me.hasOwnProperty(getterName)) {\r | |
32 | ret = me.config[name];\r | |
33 | } else {\r | |
34 | ret = me[getterName]();\r | |
35 | }\r | |
36 | } else {\r | |
37 | ret = me.getCurrentConfig();\r | |
38 | }\r | |
39 | return ret;\r | |
40 | },\r | |
41 | //<debug>\r | |
42 | makeDeprecatedMethod = function (oldName, newName, msg) {\r | |
43 | var message = '"'+ oldName +'" is deprecated.';\r | |
44 | \r | |
45 | if (msg) {\r | |
46 | message += ' ' + msg;\r | |
47 | } else if (newName) {\r | |
48 | message += ' Please use "'+ newName +'" instead.';\r | |
49 | }\r | |
50 | \r | |
51 | return function () {\r | |
52 | Ext.raise(message);\r | |
53 | };\r | |
54 | },\r | |
55 | addDeprecatedProperty = function (object, oldName, newName, message) {\r | |
56 | if (!message) {\r | |
57 | message = '"' + oldName + '" is deprecated.';\r | |
58 | }\r | |
59 | if (newName) {\r | |
60 | message += ' Please use "' + newName + '" instead.';\r | |
61 | }\r | |
62 | \r | |
63 | if (message) {\r | |
64 | Ext.Object.defineProperty(object, oldName, {\r | |
65 | get: function() {\r | |
66 | Ext.raise(message);\r | |
67 | },\r | |
68 | set: function(value) {\r | |
69 | Ext.raise(message);\r | |
70 | },\r | |
71 | configurable: true\r | |
72 | });\r | |
73 | }\r | |
74 | },\r | |
75 | //</debug>\r | |
76 | makeAliasFn = function (name) {\r | |
77 | return function () {\r | |
78 | return this[name].apply(this, arguments);\r | |
79 | };\r | |
80 | },\r | |
81 | Version = Ext.Version,\r | |
82 | leadingDigitRe = /^\d/,\r | |
83 | oneMember = {},\r | |
84 | aliasOneMember = {},\r | |
85 | Base = function(){},\r | |
86 | BasePrototype = Base.prototype;\r | |
87 | \r | |
88 | // These static properties will be copied to every newly created class with {@link Ext#define}\r | |
89 | Ext.apply(Base, {\r | |
90 | $className: 'Ext.Base',\r | |
91 | \r | |
92 | $isClass: true,\r | |
93 | \r | |
94 | /**\r | |
95 | * Create a new instance of this Class.\r | |
96 | *\r | |
97 | * Ext.define('My.cool.Class', {\r | |
98 | * ...\r | |
99 | * });\r | |
100 | *\r | |
101 | * My.cool.Class.create({\r | |
102 | * someConfig: true\r | |
103 | * });\r | |
104 | *\r | |
105 | * All parameters are passed to the constructor of the class.\r | |
106 | *\r | |
107 | * @return {Object} the created instance.\r | |
108 | * @static\r | |
109 | * @inheritable\r | |
110 | */\r | |
111 | create: function() {\r | |
112 | return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));\r | |
113 | },\r | |
114 | \r | |
115 | /**\r | |
116 | * This method applies a versioned, deprecation declaration to this class. This\r | |
117 | * is typically called by the `deprecated` config.\r | |
118 | * @private\r | |
119 | */\r | |
120 | addDeprecations: function (deprecations) {\r | |
121 | var me = this,\r | |
122 | all = [],\r | |
123 | compatVersion = Ext.getCompatVersion(deprecations.name),\r | |
124 | //<debug>\r | |
125 | configurator = me.getConfigurator(),\r | |
126 | displayName = (me.$className || '') + '#',\r | |
127 | //</debug>\r | |
128 | deprecate, versionSpec, index, message, target,\r | |
129 | enabled, existing, fn, names, oldName, newName, member, statics, version;\r | |
130 | \r | |
131 | for (versionSpec in deprecations) {\r | |
132 | if (leadingDigitRe.test(versionSpec)) {\r | |
133 | version = new Ext.Version(versionSpec);\r | |
134 | version.deprecations = deprecations[versionSpec];\r | |
135 | all.push(version);\r | |
136 | }\r | |
137 | }\r | |
138 | \r | |
139 | all.sort(Version.compare);\r | |
140 | \r | |
141 | for (index = all.length; index--; ) {\r | |
142 | deprecate = (version = all[index]).deprecations;\r | |
143 | target = me.prototype;\r | |
144 | statics = deprecate.statics;\r | |
145 | \r | |
146 | // If user specifies, say 4.2 compatibility and we have a 5.0 deprecation\r | |
147 | // then that block needs to be "enabled" to "revert" to behaviors prior\r | |
148 | // to 5.0. By default, compatVersion === currentVersion, so there are no\r | |
149 | // enabled blocks. In dev mode we still want to visit all the blocks and\r | |
150 | // possibly add shims to detect use of deprecated methods, but in a build\r | |
151 | // (if the deprecated block remains somehow) we just break the loop.\r | |
152 | enabled = compatVersion && compatVersion.lt(version);\r | |
153 | \r | |
154 | //<debug>\r | |
155 | if (!enabled) {} else\r | |
156 | //</debug>\r | |
157 | if (!enabled) {\r | |
158 | // we won't get here in dev mode when !enabled\r | |
159 | break;\r | |
160 | }\r | |
161 | \r | |
162 | while (deprecate) {\r | |
163 | names = deprecate.methods;\r | |
164 | if (names) {\r | |
165 | for (oldName in names) {\r | |
166 | member = names[oldName];\r | |
167 | fn = null;\r | |
168 | \r | |
169 | if (!member) {\r | |
170 | /*\r | |
171 | * Something like:\r | |
172 | *\r | |
173 | * '5.1': {\r | |
174 | * methods: {\r | |
175 | * removedMethod: null\r | |
176 | * }\r | |
177 | * }\r | |
178 | *\r | |
179 | * Since there is no recovering the method, we always put\r | |
180 | * on a shim to catch abuse.\r | |
181 | */\r | |
182 | \r | |
183 | //<debug>\r | |
184 | // The class should not already have a method by the oldName\r | |
185 | Ext.Assert.isNotDefinedProp(target, oldName);\r | |
186 | \r | |
187 | fn = makeDeprecatedMethod(displayName + oldName);\r | |
188 | //</debug>\r | |
189 | } else if (Ext.isString(member)) {\r | |
190 | /*\r | |
191 | * Something like:\r | |
192 | *\r | |
193 | * '5.1': {\r | |
194 | * methods: {\r | |
195 | * oldName: 'newName'\r | |
196 | * }\r | |
197 | * }\r | |
198 | *\r | |
199 | * If this block is enabled, we just put an alias in place.\r | |
200 | * Otherwise we need to inject a\r | |
201 | */\r | |
202 | \r | |
203 | //<debug>\r | |
204 | // The class should not already have a method by the oldName\r | |
205 | Ext.Assert.isNotDefinedProp(target, oldName);\r | |
206 | Ext.Assert.isDefinedProp(target, member);\r | |
207 | //</debug>\r | |
208 | \r | |
209 | if (enabled) {\r | |
210 | // This call to the real method name must be late\r | |
211 | // bound if it is to pick up overrides and such.\r | |
212 | fn = makeAliasFn(member);\r | |
213 | }\r | |
214 | //<debug>\r | |
215 | else {\r | |
216 | fn = makeDeprecatedMethod(displayName + oldName, member);\r | |
217 | }\r | |
218 | //</debug>\r | |
219 | } else {\r | |
220 | /*\r | |
221 | * Something like:\r | |
222 | *\r | |
223 | * '5.1': {\r | |
224 | * methods: {\r | |
225 | * foo: function () { ... }\r | |
226 | * }\r | |
227 | * }\r | |
228 | *\r | |
229 | * Or this:\r | |
230 | *\r | |
231 | * '5.1': {\r | |
232 | * methods: {\r | |
233 | * foo: {\r | |
234 | * fn: function () { ... },\r | |
235 | * message: 'Please use "bar" instead.'\r | |
236 | * }\r | |
237 | * }\r | |
238 | * }\r | |
239 | *\r | |
240 | * Or just this:\r | |
241 | *\r | |
242 | * '5.1': {\r | |
243 | * methods: {\r | |
244 | * foo: {\r | |
245 | * message: 'Use something else instead.'\r | |
246 | * }\r | |
247 | * }\r | |
248 | * }\r | |
249 | *\r | |
250 | * If this block is enabled, and "foo" is an existing\r | |
251 | * method, than we apply the given method as an override.\r | |
252 | * If "foo" is not existing, we simply add the method.\r | |
253 | *\r | |
254 | * If the block is not enabled and there is no existing\r | |
255 | * method by that name, than we add a shim to prevent\r | |
256 | * abuse.\r | |
257 | */\r | |
258 | message = '';\r | |
259 | if (member.message || member.fn) {\r | |
260 | //<debug>\r | |
261 | message = member.message;\r | |
262 | //</debug>\r | |
263 | member = member.fn;\r | |
264 | }\r | |
265 | \r | |
266 | existing = target.hasOwnProperty(oldName) && target[oldName];\r | |
267 | \r | |
268 | if (enabled && member) {\r | |
269 | member.$owner = me;\r | |
270 | member.$name = oldName;\r | |
271 | //<debug>\r | |
272 | member.name = displayName + oldName;\r | |
273 | //</debug>\r | |
274 | if (existing) {\r | |
275 | member.$previous = existing;\r | |
276 | }\r | |
277 | \r | |
278 | fn = member;\r | |
279 | }\r | |
280 | //<debug>\r | |
281 | else if (!existing) {\r | |
282 | fn = makeDeprecatedMethod(displayName + oldName, null,\r | |
283 | message);\r | |
284 | }\r | |
285 | //</debug>\r | |
286 | }\r | |
287 | \r | |
288 | if (fn) {\r | |
289 | target[oldName] = fn;\r | |
290 | }\r | |
291 | } // for oldName\r | |
292 | }\r | |
293 | \r | |
294 | //-------------------------------------\r | |
295 | // Debug only\r | |
296 | //<debug>\r | |
297 | \r | |
298 | names = deprecate.configs;\r | |
299 | if (names) {\r | |
300 | //\r | |
301 | // '6.0': {\r | |
302 | // configs: {\r | |
303 | // dead: null,\r | |
304 | //\r | |
305 | // renamed: 'newName',\r | |
306 | //\r | |
307 | // removed: {\r | |
308 | // message: 'This config was replaced by pixie dust'\r | |
309 | // }\r | |
310 | // }\r | |
311 | // }\r | |
312 | //\r | |
313 | configurator.addDeprecations(names);\r | |
314 | }\r | |
315 | \r | |
316 | names = deprecate.properties;\r | |
317 | if (names && !enabled) {\r | |
318 | // For properties about the only thing we can do is (on Good\r | |
319 | // Browsers), add warning shims for accessing them. So if the\r | |
320 | // block is enabled, we don't want those.\r | |
321 | for (oldName in names) {\r | |
322 | newName = names[oldName];\r | |
323 | \r | |
324 | if (Ext.isString(newName)) {\r | |
325 | addDeprecatedProperty(target, displayName + oldName, newName);\r | |
326 | } else if (newName && newName.message) {\r | |
327 | addDeprecatedProperty(target, displayName + oldName, null,\r | |
328 | newName.message);\r | |
329 | } else {\r | |
330 | addDeprecatedProperty(target, displayName + oldName);\r | |
331 | }\r | |
332 | }\r | |
333 | }\r | |
334 | \r | |
335 | //</debug>\r | |
336 | //-------------------------------------\r | |
337 | \r | |
338 | // reset to handle statics and apply them to the class\r | |
339 | deprecate = statics;\r | |
340 | statics = null;\r | |
341 | target = me;\r | |
342 | }\r | |
343 | }\r | |
344 | },\r | |
345 | \r | |
346 | /**\r | |
347 | * @private\r | |
348 | * @static\r | |
349 | * @inheritable\r | |
350 | * @param config\r | |
351 | */\r | |
352 | extend: function(parent) {\r | |
353 | var me = this,\r | |
354 | parentPrototype = parent.prototype,\r | |
355 | prototype, i, ln, name, statics;\r | |
356 | \r | |
357 | prototype = me.prototype = Ext.Object.chain(parentPrototype);\r | |
358 | prototype.self = me;\r | |
359 | \r | |
360 | me.superclass = prototype.superclass = parentPrototype;\r | |
361 | \r | |
362 | if (!parent.$isClass) {\r | |
363 | for (i in BasePrototype) {\r | |
364 | if (i in prototype) {\r | |
365 | prototype[i] = BasePrototype[i];\r | |
366 | }\r | |
367 | }\r | |
368 | }\r | |
369 | \r | |
370 | //<feature classSystem.inheritableStatics>\r | |
371 | // Statics inheritance\r | |
372 | statics = parentPrototype.$inheritableStatics;\r | |
373 | \r | |
374 | if (statics) {\r | |
375 | for (i = 0,ln = statics.length; i < ln; i++) {\r | |
376 | name = statics[i];\r | |
377 | \r | |
378 | if (!me.hasOwnProperty(name)) {\r | |
379 | me[name] = parent[name];\r | |
380 | }\r | |
381 | }\r | |
382 | }\r | |
383 | //</feature>\r | |
384 | \r | |
385 | if (parent.$onExtended) {\r | |
386 | me.$onExtended = parent.$onExtended.slice();\r | |
387 | }\r | |
388 | \r | |
389 | //<feature classSystem.config>\r | |
390 | me.getConfigurator();\r | |
391 | //</feature>\r | |
392 | },\r | |
393 | \r | |
394 | /**\r | |
395 | * @private\r | |
396 | * @static\r | |
397 | * @inheritable\r | |
398 | */\r | |
399 | $onExtended: [],\r | |
400 | \r | |
401 | /**\r | |
402 | * @private\r | |
403 | * @static\r | |
404 | * @inheritable\r | |
405 | */\r | |
406 | triggerExtended: function() {\r | |
407 | //<debug>\r | |
408 | Ext.classSystemMonitor && Ext.classSystemMonitor(this, 'Ext.Base#triggerExtended', arguments);\r | |
409 | //</debug>\r | |
410 | \r | |
411 | var callbacks = this.$onExtended,\r | |
412 | ln = callbacks.length,\r | |
413 | i, callback;\r | |
414 | \r | |
415 | if (ln > 0) {\r | |
416 | for (i = 0; i < ln; i++) {\r | |
417 | callback = callbacks[i];\r | |
418 | callback.fn.apply(callback.scope || this, arguments);\r | |
419 | }\r | |
420 | }\r | |
421 | },\r | |
422 | \r | |
423 | /**\r | |
424 | * @private\r | |
425 | * @static\r | |
426 | * @inheritable\r | |
427 | */\r | |
428 | onExtended: function(fn, scope) {\r | |
429 | this.$onExtended.push({\r | |
430 | fn: fn,\r | |
431 | scope: scope\r | |
432 | });\r | |
433 | \r | |
434 | return this;\r | |
435 | },\r | |
436 | \r | |
437 | /**\r | |
438 | * Add / override static properties of this class.\r | |
439 | *\r | |
440 | * Ext.define('My.cool.Class', {\r | |
441 | * ...\r | |
442 | * });\r | |
443 | *\r | |
444 | * My.cool.Class.addStatics({\r | |
445 | * someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'\r | |
446 | * method1: function() { ... }, // My.cool.Class.method1 = function() { ... };\r | |
447 | * method2: function() { ... } // My.cool.Class.method2 = function() { ... };\r | |
448 | * });\r | |
449 | *\r | |
450 | * @param {Object} members\r | |
451 | * @return {Ext.Base} this\r | |
452 | * @static\r | |
453 | * @inheritable\r | |
454 | */\r | |
455 | addStatics: function (members) {\r | |
456 | this.addMembers(members, true);\r | |
457 | return this;\r | |
458 | },\r | |
459 | \r | |
460 | /**\r | |
461 | * @private\r | |
462 | * @static\r | |
463 | * @inheritable\r | |
464 | * @param {Object} members\r | |
465 | */\r | |
466 | addInheritableStatics: function(members) {\r | |
467 | var inheritableStatics,\r | |
468 | hasInheritableStatics,\r | |
469 | prototype = this.prototype,\r | |
470 | name, member;\r | |
471 | \r | |
472 | inheritableStatics = prototype.$inheritableStatics;\r | |
473 | hasInheritableStatics = prototype.$hasInheritableStatics;\r | |
474 | \r | |
475 | if (!inheritableStatics) {\r | |
476 | inheritableStatics = prototype.$inheritableStatics = [];\r | |
477 | hasInheritableStatics = prototype.$hasInheritableStatics = {};\r | |
478 | }\r | |
479 | \r | |
480 | //<debug>\r | |
481 | var className = Ext.getClassName(this) + '.';\r | |
482 | //</debug>\r | |
483 | \r | |
484 | for (name in members) {\r | |
485 | if (members.hasOwnProperty(name)) {\r | |
486 | member = members[name];\r | |
487 | //<debug>\r | |
488 | if (typeof member == 'function') {\r | |
489 | member.name = className + name;\r | |
490 | }\r | |
491 | //</debug>\r | |
492 | this[name] = member;\r | |
493 | \r | |
494 | if (!hasInheritableStatics[name]) {\r | |
495 | hasInheritableStatics[name] = true;\r | |
496 | inheritableStatics.push(name);\r | |
497 | }\r | |
498 | }\r | |
499 | }\r | |
500 | \r | |
501 | return this;\r | |
502 | },\r | |
503 | \r | |
504 | /**\r | |
505 | * Add methods / properties to the prototype of this class.\r | |
506 | *\r | |
507 | * Ext.define('My.awesome.Cat', {\r | |
508 | * constructor: function() {\r | |
509 | * ...\r | |
510 | * }\r | |
511 | * });\r | |
512 | *\r | |
513 | * My.awesome.Cat.addMembers({\r | |
514 | * meow: function() {\r | |
515 | * alert('Meowww...');\r | |
516 | * }\r | |
517 | * });\r | |
518 | *\r | |
519 | * var kitty = new My.awesome.Cat();\r | |
520 | * kitty.meow();\r | |
521 | *\r | |
522 | * @param {Object} members The members to add to this class.\r | |
523 | * @param {Boolean} [isStatic=false] Pass `true` if the members are static.\r | |
524 | * @param {Boolean} [privacy=false] Pass `true` if the members are private. This\r | |
525 | * only has meaning in debug mode and only for methods.\r | |
526 | * @static\r | |
527 | * @inheritable\r | |
528 | */\r | |
529 | addMembers: function (members, isStatic, privacy) {\r | |
530 | var me = this, // this class\r | |
531 | cloneFunction = Ext.Function.clone,\r | |
532 | target = isStatic ? me : me.prototype,\r | |
533 | defaultConfig = !isStatic && target.defaultConfig,\r | |
534 | enumerables = Ext.enumerables,\r | |
535 | privates = members.privates,\r | |
536 | configs, i, ln, member, name, subPrivacy, privateStatics;\r | |
537 | \r | |
538 | //<debug>\r | |
539 | var displayName = (me.$className || '') + '#';\r | |
540 | //</debug>\r | |
541 | \r | |
542 | if (privates) {\r | |
543 | // This won't run for normal class private members but will pick up all\r | |
544 | // others (statics, overrides, etc).\r | |
545 | delete members.privates;\r | |
546 | if (!isStatic) {\r | |
547 | privateStatics = privates.statics;\r | |
548 | delete privates.statics;\r | |
549 | }\r | |
550 | \r | |
551 | //<debug>\r | |
552 | subPrivacy = privates.privacy || privacy || 'framework';\r | |
553 | //</debug>\r | |
554 | \r | |
555 | me.addMembers(privates, isStatic, subPrivacy);\r | |
556 | if (privateStatics) {\r | |
557 | me.addMembers(privateStatics, true, subPrivacy);\r | |
558 | }\r | |
559 | }\r | |
560 | \r | |
561 | for (name in members) {\r | |
562 | if (members.hasOwnProperty(name)) {\r | |
563 | member = members[name];\r | |
564 | \r | |
565 | //<debug>\r | |
566 | if (privacy === true) {\r | |
567 | privacy = 'framework';\r | |
568 | }\r | |
569 | if (member && member.$nullFn && privacy !== member.$privacy) {\r | |
570 | Ext.raise('Cannot use stock function for private method ' +\r | |
571 | (me.$className ? me.$className + '#' : '') + name);\r | |
572 | }\r | |
573 | //</debug>\r | |
574 | \r | |
575 | if (typeof member === 'function' && !member.$isClass && !member.$nullFn) {\r | |
576 | if (member.$owner) {\r | |
577 | member = cloneFunction(member);\r | |
578 | }\r | |
579 | \r | |
580 | if (target.hasOwnProperty(name)) {\r | |
581 | member.$previous = target[name];\r | |
582 | }\r | |
583 | \r | |
584 | // This information is needed by callParent() and callSuper() as\r | |
585 | // well as statics() and even Ext.fly().\r | |
586 | member.$owner = me;\r | |
587 | member.$name = name;\r | |
588 | \r | |
589 | //<debug>\r | |
590 | member.name = displayName + name;\r | |
591 | \r | |
592 | var existing = target[name];\r | |
593 | \r | |
594 | if (privacy) {\r | |
595 | member.$privacy = privacy;\r | |
596 | \r | |
597 | // The general idea here is that an existing, non-private\r | |
598 | // method can be marked private. This is because the other\r | |
599 | // way is strictly forbidden (private method going public)\r | |
600 | // so if a method is in that gray area it can only be made\r | |
601 | // private in doc form which allows a derived class to make\r | |
602 | // it public.\r | |
603 | if (existing && existing.$privacy && existing.$privacy !== privacy) {\r | |
604 | Ext.privacyViolation(me, existing, member, isStatic);\r | |
605 | }\r | |
606 | } else if (existing && existing.$privacy) {\r | |
607 | Ext.privacyViolation(me, existing, member, isStatic);\r | |
608 | }\r | |
609 | //</debug>\r | |
610 | // The last part of the check here resolves a conflict if we have the same property\r | |
611 | // declared as both a config and a member on the class so that the config wins.\r | |
612 | } else if (defaultConfig && (name in defaultConfig) && !target.config.hasOwnProperty(name)) {\r | |
613 | // This is a config property so it must be added to the configs\r | |
614 | // collection not just smashed on the prototype...\r | |
615 | (configs || (configs = {}))[name] = member;\r | |
616 | continue;\r | |
617 | }\r | |
618 | \r | |
619 | target[name] = member;\r | |
620 | }\r | |
621 | }\r | |
622 | \r | |
623 | if (configs) {\r | |
624 | // Add any configs found in the normal members arena:\r | |
625 | me.addConfig(configs);\r | |
626 | }\r | |
627 | \r | |
628 | if (enumerables) {\r | |
629 | for (i = 0, ln = enumerables.length; i < ln; ++i) {\r | |
630 | if (members.hasOwnProperty(name = enumerables[i])) {\r | |
631 | member = members[name];\r | |
632 | \r | |
633 | // The enumerables are all functions...\r | |
634 | if (member && !member.$nullFn) {\r | |
635 | if (member.$owner) {\r | |
636 | member = cloneFunction(member);\r | |
637 | }\r | |
638 | \r | |
639 | member.$owner = me;\r | |
640 | member.$name = name;\r | |
641 | //<debug>\r | |
642 | member.name = displayName + name;\r | |
643 | //</debug>\r | |
644 | \r | |
645 | if (target.hasOwnProperty(name)) {\r | |
646 | member.$previous = target[name];\r | |
647 | }\r | |
648 | }\r | |
649 | \r | |
650 | target[name] = member;\r | |
651 | }\r | |
652 | }\r | |
653 | }\r | |
654 | \r | |
655 | return this;\r | |
656 | },\r | |
657 | \r | |
658 | /**\r | |
659 | * @private\r | |
660 | * @static\r | |
661 | * @inheritable\r | |
662 | * @param name\r | |
663 | * @param member\r | |
664 | */\r | |
665 | addMember: function (name, member) {\r | |
666 | oneMember[name] = member;\r | |
667 | this.addMembers(oneMember);\r | |
668 | delete oneMember[name];\r | |
669 | return this;\r | |
670 | },\r | |
671 | \r | |
672 | /**\r | |
673 | * Borrow another class' members to the prototype of this class.\r | |
674 | *\r | |
675 | * Ext.define('Bank', {\r | |
676 | * money: '$$$',\r | |
677 | * printMoney: function() {\r | |
678 | * alert('$$$$$$$');\r | |
679 | * }\r | |
680 | * });\r | |
681 | *\r | |
682 | * Ext.define('Thief', {\r | |
683 | * ...\r | |
684 | * });\r | |
685 | *\r | |
686 | * Thief.borrow(Bank, ['money', 'printMoney']);\r | |
687 | *\r | |
688 | * var steve = new Thief();\r | |
689 | *\r | |
690 | * alert(steve.money); // alerts '$$$'\r | |
691 | * steve.printMoney(); // alerts '$$$$$$$'\r | |
692 | *\r | |
693 | * @param {Ext.Base} fromClass The class to borrow members from\r | |
694 | * @param {Array/String} members The names of the members to borrow\r | |
695 | * @return {Ext.Base} this\r | |
696 | * @static\r | |
697 | * @inheritable\r | |
698 | * @private\r | |
699 | */\r | |
700 | borrow: function(fromClass, members) {\r | |
701 | //<debug>\r | |
702 | Ext.classSystemMonitor && Ext.classSystemMonitor(this, 'Ext.Base#borrow', arguments);\r | |
703 | //</debug>\r | |
704 | \r | |
705 | var prototype = fromClass.prototype,\r | |
706 | membersObj = {},\r | |
707 | i, ln, name;\r | |
708 | \r | |
709 | members = Ext.Array.from(members);\r | |
710 | \r | |
711 | for (i = 0,ln = members.length; i < ln; i++) {\r | |
712 | name = members[i];\r | |
713 | membersObj[name] = prototype[name];\r | |
714 | }\r | |
715 | \r | |
716 | return this.addMembers(membersObj);\r | |
717 | },\r | |
718 | \r | |
719 | /**\r | |
720 | * Override members of this class. Overridden methods can be invoked via\r | |
721 | * {@link Ext.Base#callParent}.\r | |
722 | *\r | |
723 | * Ext.define('My.Cat', {\r | |
724 | * constructor: function() {\r | |
725 | * alert("I'm a cat!");\r | |
726 | * }\r | |
727 | * });\r | |
728 | *\r | |
729 | * My.Cat.override({\r | |
730 | * constructor: function() {\r | |
731 | * alert("I'm going to be a cat!");\r | |
732 | *\r | |
733 | * this.callParent(arguments);\r | |
734 | *\r | |
735 | * alert("Meeeeoooowwww");\r | |
736 | * }\r | |
737 | * });\r | |
738 | *\r | |
739 | * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"\r | |
740 | * // alerts "I'm a cat!"\r | |
741 | * // alerts "Meeeeoooowwww"\r | |
742 | *\r | |
743 | * Direct use of this method should be rare. Use {@link Ext#define Ext.define}\r | |
744 | * instead:\r | |
745 | *\r | |
746 | * Ext.define('My.CatOverride', {\r | |
747 | * override: 'My.Cat',\r | |
748 | * constructor: function() {\r | |
749 | * alert("I'm going to be a cat!");\r | |
750 | *\r | |
751 | * this.callParent(arguments);\r | |
752 | *\r | |
753 | * alert("Meeeeoooowwww");\r | |
754 | * }\r | |
755 | * });\r | |
756 | *\r | |
757 | * The above accomplishes the same result but can be managed by the {@link Ext.Loader}\r | |
758 | * which can properly order the override and its target class and the build process\r | |
759 | * can determine whether the override is needed based on the required state of the\r | |
760 | * target class (My.Cat).\r | |
761 | *\r | |
762 | * @param {Object} members The properties to add to this class. This should be\r | |
763 | * specified as an object literal containing one or more properties.\r | |
764 | * @return {Ext.Base} this class\r | |
765 | * @static\r | |
766 | * @inheritable\r | |
767 | */\r | |
768 | override: function(members) {\r | |
769 | var me = this,\r | |
770 | statics = members.statics,\r | |
771 | inheritableStatics = members.inheritableStatics,\r | |
772 | config = members.config,\r | |
773 | mixins = members.mixins,\r | |
774 | cachedConfig = members.cachedConfig;\r | |
775 | \r | |
776 | if (statics || inheritableStatics || config) {\r | |
777 | members = Ext.apply({}, members);\r | |
778 | }\r | |
779 | \r | |
780 | if (statics) {\r | |
781 | me.addMembers(statics, true);\r | |
782 | delete members.statics;\r | |
783 | }\r | |
784 | \r | |
785 | if (inheritableStatics){\r | |
786 | me.addInheritableStatics(inheritableStatics);\r | |
787 | delete members.inheritableStatics;\r | |
788 | }\r | |
789 | \r | |
790 | if (config) {\r | |
791 | me.addConfig(config);\r | |
792 | delete members.config;\r | |
793 | }\r | |
794 | \r | |
795 | if (cachedConfig) {\r | |
796 | me.addCachedConfig(cachedConfig);\r | |
797 | delete members.cachedConfig;\r | |
798 | }\r | |
799 | \r | |
800 | delete members.mixins;\r | |
801 | \r | |
802 | me.addMembers(members);\r | |
803 | if (mixins) {\r | |
804 | me.mixin(mixins);\r | |
805 | }\r | |
806 | return me;\r | |
807 | },\r | |
808 | \r | |
809 | /**\r | |
810 | * @protected\r | |
811 | * @static\r | |
812 | * @inheritable\r | |
813 | */\r | |
814 | callParent: function(args) {\r | |
815 | var method;\r | |
816 | \r | |
817 | // This code is intentionally inlined for the least amount of debugger stepping\r | |
818 | return (method = this.callParent.caller) && (method.$previous ||\r | |
819 | ((method = method.$owner ? method : method.caller) &&\r | |
820 | method.$owner.superclass.self[method.$name])).apply(this, args || noArgs);\r | |
821 | },\r | |
822 | \r | |
823 | /**\r | |
824 | * @protected\r | |
825 | * @static\r | |
826 | * @inheritable\r | |
827 | */\r | |
828 | callSuper: function(args) {\r | |
829 | var method;\r | |
830 | \r | |
831 | // This code is intentionally inlined for the least amount of debugger stepping\r | |
832 | return (method = this.callSuper.caller) &&\r | |
833 | ((method = method.$owner ? method : method.caller) &&\r | |
834 | method.$owner.superclass.self[method.$name]).apply(this, args || noArgs);\r | |
835 | },\r | |
836 | \r | |
837 | //<feature classSystem.mixins>\r | |
838 | /**\r | |
839 | * Used internally by the mixins pre-processor\r | |
840 | * @private\r | |
841 | * @static\r | |
842 | * @inheritable\r | |
843 | */\r | |
844 | mixin: function(name, mixinClass) {\r | |
845 | var me = this,\r | |
846 | mixin, prototype, key, statics, i, ln, staticName, mixinValue, mixins;\r | |
847 | \r | |
848 | if (typeof name !== 'string') {\r | |
849 | mixins = name;\r | |
850 | if (mixins instanceof Array) {\r | |
851 | for (i = 0,ln = mixins.length; i < ln; i++) {\r | |
852 | mixin = mixins[i];\r | |
853 | me.mixin(mixin.prototype.mixinId || mixin.$className, mixin);\r | |
854 | }\r | |
855 | } else {\r | |
856 | // Not a string or array - process the object form:\r | |
857 | // mixins: {\r | |
858 | // foo: ...\r | |
859 | // }\r | |
860 | for (var mixinName in mixins) {\r | |
861 | me.mixin(mixinName, mixins[mixinName]);\r | |
862 | }\r | |
863 | }\r | |
864 | return;\r | |
865 | }\r | |
866 | \r | |
867 | mixin = mixinClass.prototype;\r | |
868 | prototype = me.prototype;\r | |
869 | \r | |
870 | if (mixin.onClassMixedIn) {\r | |
871 | mixin.onClassMixedIn.call(mixinClass, me);\r | |
872 | }\r | |
873 | \r | |
874 | if (!prototype.hasOwnProperty('mixins')) {\r | |
875 | if ('mixins' in prototype) {\r | |
876 | prototype.mixins = Ext.Object.chain(prototype.mixins);\r | |
877 | }\r | |
878 | else {\r | |
879 | prototype.mixins = {};\r | |
880 | }\r | |
881 | }\r | |
882 | \r | |
883 | for (key in mixin) {\r | |
884 | mixinValue = mixin[key];\r | |
885 | if (key === 'mixins') {\r | |
886 | // if 2 superclasses (e.g. a base class and a mixin) of this class both\r | |
887 | // have a mixin with the same id, the first one wins, that is to say,\r | |
888 | // the first mixin's methods to be applied to the prototype will not\r | |
889 | // be overwritten by the second one. Since this is the case we also\r | |
890 | // want to make sure we use the first mixin's prototype as the mixin\r | |
891 | // reference, hence the "applyIf" below. A real world example of this\r | |
892 | // is Ext.Widget which mixes in Ext.mixin.Observable. Ext.Widget can\r | |
893 | // be mixed into subclasses of Ext.Component, which mixes in\r | |
894 | // Ext.util.Observable. In this example, since the first "observable"\r | |
895 | // mixin's methods win, we also want its reference to be preserved.\r | |
896 | Ext.applyIf(prototype.mixins, mixinValue);\r | |
897 | }\r | |
898 | else if (!(key === 'mixinId' || key === 'config') && (prototype[key] === undefined)) {\r | |
899 | prototype[key] = mixinValue;\r | |
900 | }\r | |
901 | }\r | |
902 | \r | |
903 | //<feature classSystem.inheritableStatics>\r | |
904 | // Mixin statics inheritance\r | |
905 | statics = mixin.$inheritableStatics;\r | |
906 | \r | |
907 | if (statics) {\r | |
908 | for (i = 0, ln = statics.length; i < ln; i++) {\r | |
909 | staticName = statics[i];\r | |
910 | \r | |
911 | if (!me.hasOwnProperty(staticName)) {\r | |
912 | me[staticName] = mixinClass[staticName];\r | |
913 | }\r | |
914 | }\r | |
915 | }\r | |
916 | //</feature>\r | |
917 | \r | |
918 | //<feature classSystem.config>\r | |
919 | if ('config' in mixin) {\r | |
920 | me.addConfig(mixin.config, mixinClass);\r | |
921 | }\r | |
922 | //</feature>\r | |
923 | \r | |
924 | prototype.mixins[name] = mixin;\r | |
925 | \r | |
926 | if (mixin.afterClassMixedIn) {\r | |
927 | mixin.afterClassMixedIn.call(mixinClass, me);\r | |
928 | }\r | |
929 | \r | |
930 | return me;\r | |
931 | },\r | |
932 | //</feature>\r | |
933 | \r | |
934 | //<feature classSystem.config>\r | |
935 | /**\r | |
936 | * Adds new config properties to this class. This is called for classes when they\r | |
937 | * are declared, then for any mixins that class may define and finally for any\r | |
938 | * overrides defined that target the class.\r | |
939 | * \r | |
940 | * @param {Object} config\r | |
941 | * @param {Ext.Class} [mixinClass] The mixin class if the configs are from a mixin.\r | |
942 | * @private\r | |
943 | * @static\r | |
944 | * @inheritable\r | |
945 | */\r | |
946 | addConfig: function (config, mixinClass) {\r | |
947 | var cfg = this.$config || this.getConfigurator();\r | |
948 | cfg.add(config, mixinClass);\r | |
949 | },\r | |
950 | \r | |
951 | addCachedConfig: function(config, isMixin) {\r | |
952 | var cached = {},\r | |
953 | key;\r | |
954 | \r | |
955 | for (key in config) {\r | |
956 | cached[key] = {\r | |
957 | cached: true,\r | |
958 | $value: config[key]\r | |
959 | };\r | |
960 | }\r | |
961 | this.addConfig(cached, isMixin);\r | |
962 | },\r | |
963 | \r | |
964 | /**\r | |
965 | * Returns the `Ext.Configurator` for this class.\r | |
966 | * \r | |
967 | * @return {Ext.Configurator}\r | |
968 | * @private\r | |
969 | * @static\r | |
970 | * @inheritable\r | |
971 | */\r | |
972 | getConfigurator: function () {\r | |
973 | // the Ext.Configurator ctor will set $config so micro-opt out fn call:\r | |
974 | return this.$config || new Ext.Configurator(this);\r | |
975 | },\r | |
976 | //</feature>\r | |
977 | \r | |
978 | /**\r | |
979 | * Get the current class' name in string format.\r | |
980 | *\r | |
981 | * Ext.define('My.cool.Class', {\r | |
982 | * constructor: function() {\r | |
983 | * alert(this.self.getName()); // alerts 'My.cool.Class'\r | |
984 | * }\r | |
985 | * });\r | |
986 | *\r | |
987 | * My.cool.Class.getName(); // 'My.cool.Class'\r | |
988 | *\r | |
989 | * @return {String} className\r | |
990 | * @static\r | |
991 | * @inheritable\r | |
992 | */\r | |
993 | getName: function() {\r | |
994 | return Ext.getClassName(this);\r | |
995 | },\r | |
996 | \r | |
997 | /**\r | |
998 | * Create aliases for existing prototype methods. Example:\r | |
999 | *\r | |
1000 | * Ext.define('My.cool.Class', {\r | |
1001 | * method1: function() { ... },\r | |
1002 | * method2: function() { ... }\r | |
1003 | * });\r | |
1004 | *\r | |
1005 | * var test = new My.cool.Class();\r | |
1006 | *\r | |
1007 | * My.cool.Class.createAlias({\r | |
1008 | * method3: 'method1',\r | |
1009 | * method4: 'method2'\r | |
1010 | * });\r | |
1011 | *\r | |
1012 | * test.method3(); // test.method1()\r | |
1013 | *\r | |
1014 | * My.cool.Class.createAlias('method5', 'method3');\r | |
1015 | *\r | |
1016 | * test.method5(); // test.method3() -> test.method1()\r | |
1017 | *\r | |
1018 | * @param {String/Object} alias The new method name, or an object to set multiple aliases. See\r | |
1019 | * {@link Ext.Function#flexSetter flexSetter}\r | |
1020 | * @param {String/Object} origin The original method name\r | |
1021 | * @static\r | |
1022 | * @inheritable\r | |
1023 | * @method\r | |
1024 | */\r | |
1025 | createAlias: flexSetter(function(alias, origin) {\r | |
1026 | aliasOneMember[alias] = function() {\r | |
1027 | return this[origin].apply(this, arguments);\r | |
1028 | };\r | |
1029 | this.override(aliasOneMember);\r | |
1030 | delete aliasOneMember[alias];\r | |
1031 | })\r | |
1032 | });\r | |
1033 | \r | |
1034 | // Capture the set of static members on Ext.Base that we want to copy to all\r | |
1035 | // derived classes. This array is used by Ext.Class as well as the optimizer.\r | |
1036 | for (baseStaticMember in Base) {\r | |
1037 | if (Base.hasOwnProperty(baseStaticMember)) {\r | |
1038 | baseStaticMembers.push(baseStaticMember);\r | |
1039 | }\r | |
1040 | }\r | |
1041 | \r | |
1042 | Base.$staticMembers = baseStaticMembers;\r | |
1043 | \r | |
1044 | //<feature classSystem.config>\r | |
1045 | Base.getConfigurator(); // lazily create now so as not capture in $staticMembers\r | |
1046 | //</feature>\r | |
1047 | \r | |
1048 | Base.addMembers({\r | |
1049 | /** @private */\r | |
1050 | $className: 'Ext.Base',\r | |
1051 | \r | |
1052 | /**\r | |
1053 | * @property {Boolean} isInstance\r | |
1054 | * This value is `true` and is used to identify plain objects from instances of\r | |
1055 | * a defined class.\r | |
1056 | * @protected\r | |
1057 | * @readonly\r | |
1058 | */\r | |
1059 | isInstance: true,\r | |
1060 | \r | |
1061 | /**\r | |
1062 | * @property {Boolean} [$configPrefixed]\r | |
1063 | * The value `true` causes `config` values to be stored on instances using a\r | |
1064 | * property name prefixed with an underscore ("_") character. A value of `false`\r | |
1065 | * stores `config` values as properties using their exact name (no prefix).\r | |
1066 | * @private\r | |
1067 | * @since 5.0.0\r | |
1068 | */\r | |
1069 | $configPrefixed: true,\r | |
1070 | \r | |
1071 | /**\r | |
1072 | * @property {Boolean} [$configStrict]\r | |
1073 | * The value `true` instructs the `initConfig` method to only honor values for\r | |
1074 | * properties declared in the `config` block of a class. When `false`, properties\r | |
1075 | * that are not declared in a `config` block will be placed on the instance.\r | |
1076 | * @private\r | |
1077 | * @since 5.0.0\r | |
1078 | */\r | |
1079 | $configStrict: true,\r | |
1080 | \r | |
1081 | /**\r | |
1082 | * @property {Boolean} isConfiguring\r | |
1083 | * This property is set to `true` during the call to `initConfig`.\r | |
1084 | * @protected\r | |
1085 | * @readonly\r | |
1086 | * @since 5.0.0\r | |
1087 | */\r | |
1088 | isConfiguring: false,\r | |
1089 | \r | |
1090 | /**\r | |
1091 | * @property {Boolean} isFirstInstance\r | |
1092 | * This property is set to `true` if this instance is the first of its class.\r | |
1093 | * @protected\r | |
1094 | * @readonly\r | |
1095 | * @since 5.0.0\r | |
1096 | */\r | |
1097 | isFirstInstance: false,\r | |
1098 | \r | |
1099 | /**\r | |
1100 | * @property {Boolean} destroyed\r | |
1101 | * This property is set to `true` after the `destroy` method is called.\r | |
1102 | * @protected\r | |
1103 | */\r | |
1104 | destroyed: false,\r | |
1105 | \r | |
1106 | /**\r | |
1107 | * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},\r | |
1108 | * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what\r | |
1109 | * `this` points to during run-time\r | |
1110 | *\r | |
1111 | * Ext.define('My.Cat', {\r | |
1112 | * statics: {\r | |
1113 | * totalCreated: 0,\r | |
1114 | * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'\r | |
1115 | * },\r | |
1116 | *\r | |
1117 | * constructor: function() {\r | |
1118 | * var statics = this.statics();\r | |
1119 | *\r | |
1120 | * alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to\r | |
1121 | * // equivalent to: My.Cat.speciesName\r | |
1122 | *\r | |
1123 | * alert(this.self.speciesName); // dependent on 'this'\r | |
1124 | *\r | |
1125 | * statics.totalCreated++;\r | |
1126 | * },\r | |
1127 | *\r | |
1128 | * clone: function() {\r | |
1129 | * var cloned = new this.self(); // dependent on 'this'\r | |
1130 | *\r | |
1131 | * cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName\r | |
1132 | *\r | |
1133 | * return cloned;\r | |
1134 | * }\r | |
1135 | * });\r | |
1136 | *\r | |
1137 | *\r | |
1138 | * Ext.define('My.SnowLeopard', {\r | |
1139 | * extend: 'My.Cat',\r | |
1140 | *\r | |
1141 | * statics: {\r | |
1142 | * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'\r | |
1143 | * },\r | |
1144 | *\r | |
1145 | * constructor: function() {\r | |
1146 | * this.callParent();\r | |
1147 | * }\r | |
1148 | * });\r | |
1149 | *\r | |
1150 | * var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'\r | |
1151 | *\r | |
1152 | * var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'\r | |
1153 | *\r | |
1154 | * var clone = snowLeopard.clone();\r | |
1155 | * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'\r | |
1156 | * alert(clone.groupName); // alerts 'Cat'\r | |
1157 | *\r | |
1158 | * alert(My.Cat.totalCreated); // alerts 3\r | |
1159 | *\r | |
1160 | * @protected\r | |
1161 | * @return {Ext.Class}\r | |
1162 | */\r | |
1163 | statics: function() {\r | |
1164 | var method = this.statics.caller,\r | |
1165 | self = this.self;\r | |
1166 | \r | |
1167 | if (!method) {\r | |
1168 | return self;\r | |
1169 | }\r | |
1170 | \r | |
1171 | return method.$owner;\r | |
1172 | },\r | |
1173 | \r | |
1174 | /**\r | |
1175 | * Call the "parent" method of the current method. That is the method previously\r | |
1176 | * overridden by derivation or by an override (see {@link Ext#define}).\r | |
1177 | *\r | |
1178 | * Ext.define('My.Base', {\r | |
1179 | * constructor: function (x) {\r | |
1180 | * this.x = x;\r | |
1181 | * },\r | |
1182 | *\r | |
1183 | * statics: {\r | |
1184 | * method: function (x) {\r | |
1185 | * return x;\r | |
1186 | * }\r | |
1187 | * }\r | |
1188 | * });\r | |
1189 | *\r | |
1190 | * Ext.define('My.Derived', {\r | |
1191 | * extend: 'My.Base',\r | |
1192 | *\r | |
1193 | * constructor: function () {\r | |
1194 | * this.callParent([21]);\r | |
1195 | * }\r | |
1196 | * });\r | |
1197 | *\r | |
1198 | * var obj = new My.Derived();\r | |
1199 | *\r | |
1200 | * alert(obj.x); // alerts 21\r | |
1201 | *\r | |
1202 | * This can be used with an override as follows:\r | |
1203 | *\r | |
1204 | * Ext.define('My.DerivedOverride', {\r | |
1205 | * override: 'My.Derived',\r | |
1206 | *\r | |
1207 | * constructor: function (x) {\r | |
1208 | * this.callParent([x*2]); // calls original My.Derived constructor\r | |
1209 | * }\r | |
1210 | * });\r | |
1211 | *\r | |
1212 | * var obj = new My.Derived();\r | |
1213 | *\r | |
1214 | * alert(obj.x); // now alerts 42\r | |
1215 | *\r | |
1216 | * This also works with static and private methods.\r | |
1217 | *\r | |
1218 | * Ext.define('My.Derived2', {\r | |
1219 | * extend: 'My.Base',\r | |
1220 | *\r | |
1221 | * // privates: {\r | |
1222 | * statics: {\r | |
1223 | * method: function (x) {\r | |
1224 | * return this.callParent([x*2]); // calls My.Base.method\r | |
1225 | * }\r | |
1226 | * }\r | |
1227 | * });\r | |
1228 | *\r | |
1229 | * alert(My.Base.method(10)); // alerts 10\r | |
1230 | * alert(My.Derived2.method(10)); // alerts 20\r | |
1231 | *\r | |
1232 | * Lastly, it also works with overridden static methods.\r | |
1233 | *\r | |
1234 | * Ext.define('My.Derived2Override', {\r | |
1235 | * override: 'My.Derived2',\r | |
1236 | *\r | |
1237 | * // privates: {\r | |
1238 | * statics: {\r | |
1239 | * method: function (x) {\r | |
1240 | * return this.callParent([x*2]); // calls My.Derived2.method\r | |
1241 | * }\r | |
1242 | * }\r | |
1243 | * });\r | |
1244 | *\r | |
1245 | * alert(My.Derived2.method(10); // now alerts 40\r | |
1246 | *\r | |
1247 | * To override a method and replace it and also call the superclass method, use\r | |
1248 | * {@link #method-callSuper}. This is often done to patch a method to fix a bug.\r | |
1249 | *\r | |
1250 | * @protected\r | |
1251 | * @param {Array/Arguments} args The arguments, either an array or the `arguments` object\r | |
1252 | * from the current method, for example: `this.callParent(arguments)`\r | |
1253 | * @return {Object} Returns the result of calling the parent method\r | |
1254 | */\r | |
1255 | callParent: function(args) {\r | |
1256 | // NOTE: this code is deliberately as few expressions (and no function calls)\r | |
1257 | // as possible so that a debugger can skip over this noise with the minimum number\r | |
1258 | // of steps. Basically, just hit Step Into until you are where you really wanted\r | |
1259 | // to be.\r | |
1260 | var method,\r | |
1261 | superMethod = (method = this.callParent.caller) && (method.$previous ||\r | |
1262 | ((method = method.$owner ? method : method.caller) &&\r | |
1263 | method.$owner.superclass[method.$name]));\r | |
1264 | \r | |
1265 | //<debug>\r | |
1266 | if (!superMethod) {\r | |
1267 | method = this.callParent.caller;\r | |
1268 | var parentClass, methodName;\r | |
1269 | \r | |
1270 | if (!method.$owner) {\r | |
1271 | if (!method.caller) {\r | |
1272 | throw new Error("Attempting to call a protected method from the public scope, which is not allowed");\r | |
1273 | }\r | |
1274 | \r | |
1275 | method = method.caller;\r | |
1276 | }\r | |
1277 | \r | |
1278 | parentClass = method.$owner.superclass;\r | |
1279 | methodName = method.$name;\r | |
1280 | \r | |
1281 | if (!(methodName in parentClass)) {\r | |
1282 | throw new Error("this.callParent() was called but there's no such method (" + methodName +\r | |
1283 | ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")");\r | |
1284 | }\r | |
1285 | }\r | |
1286 | //</debug>\r | |
1287 | \r | |
1288 | return superMethod.apply(this, args || noArgs);\r | |
1289 | },\r | |
1290 | \r | |
1291 | /**\r | |
1292 | * This method is used by an **override** to call the superclass method but \r | |
1293 | * bypass any overridden method. This is often done to "patch" a method that \r | |
1294 | * contains a bug but for whatever reason cannot be fixed directly.\r | |
1295 | * \r | |
1296 | * Consider:\r | |
1297 | * \r | |
1298 | * Ext.define('Ext.some.Class', {\r | |
1299 | * method: function () {\r | |
1300 | * console.log('Good');\r | |
1301 | * }\r | |
1302 | * });\r | |
1303 | * \r | |
1304 | * Ext.define('Ext.some.DerivedClass', {\r | |
1305 | * extend: 'Ext.some.Class',\r | |
1306 | * \r | |
1307 | * method: function () {\r | |
1308 | * console.log('Bad');\r | |
1309 | * \r | |
1310 | * // ... logic but with a bug ...\r | |
1311 | * \r | |
1312 | * this.callParent();\r | |
1313 | * }\r | |
1314 | * });\r | |
1315 | * \r | |
1316 | * To patch the bug in `Ext.some.DerivedClass.method`, the typical solution is to create an\r | |
1317 | * override:\r | |
1318 | * \r | |
1319 | * Ext.define('App.patches.DerivedClass', {\r | |
1320 | * override: 'Ext.some.DerivedClass',\r | |
1321 | * \r | |
1322 | * method: function () {\r | |
1323 | * console.log('Fixed');\r | |
1324 | * \r | |
1325 | * // ... logic but with bug fixed ...\r | |
1326 | *\r | |
1327 | * this.callSuper();\r | |
1328 | * }\r | |
1329 | * });\r | |
1330 | * \r | |
1331 | * The patch method cannot use {@link #method-callParent} to call the superclass \r | |
1332 | * `method` since that would call the overridden method containing the bug. In \r | |
1333 | * other words, the above patch would only produce "Fixed" then "Good" in the \r | |
1334 | * console log, whereas, using `callParent` would produce "Fixed" then "Bad" \r | |
1335 | * then "Good".\r | |
1336 | *\r | |
1337 | * @protected\r | |
1338 | * @param {Array/Arguments} args The arguments, either an array or the `arguments` object\r | |
1339 | * from the current method, for example: `this.callSuper(arguments)`\r | |
1340 | * @return {Object} Returns the result of calling the superclass method\r | |
1341 | */\r | |
1342 | callSuper: function(args) {\r | |
1343 | // NOTE: this code is deliberately as few expressions (and no function calls)\r | |
1344 | // as possible so that a debugger can skip over this noise with the minimum number\r | |
1345 | // of steps. Basically, just hit Step Into until you are where you really wanted\r | |
1346 | // to be.\r | |
1347 | var method,\r | |
1348 | superMethod = (method = this.callSuper.caller) &&\r | |
1349 | ((method = method.$owner ? method : method.caller) &&\r | |
1350 | method.$owner.superclass[method.$name]);\r | |
1351 | \r | |
1352 | //<debug>\r | |
1353 | if (!superMethod) {\r | |
1354 | method = this.callSuper.caller;\r | |
1355 | var parentClass, methodName;\r | |
1356 | \r | |
1357 | if (!method.$owner) {\r | |
1358 | if (!method.caller) {\r | |
1359 | throw new Error("Attempting to call a protected method from the public scope, which is not allowed");\r | |
1360 | }\r | |
1361 | \r | |
1362 | method = method.caller;\r | |
1363 | }\r | |
1364 | \r | |
1365 | parentClass = method.$owner.superclass;\r | |
1366 | methodName = method.$name;\r | |
1367 | \r | |
1368 | if (!(methodName in parentClass)) {\r | |
1369 | throw new Error("this.callSuper() was called but there's no such method (" + methodName +\r | |
1370 | ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")");\r | |
1371 | }\r | |
1372 | }\r | |
1373 | //</debug>\r | |
1374 | \r | |
1375 | return superMethod.apply(this, args || noArgs);\r | |
1376 | },\r | |
1377 | \r | |
1378 | /**\r | |
1379 | * @property {Ext.Class} self\r | |
1380 | *\r | |
1381 | * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},\r | |
1382 | * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}\r | |
1383 | * for a detailed comparison\r | |
1384 | *\r | |
1385 | * Ext.define('My.Cat', {\r | |
1386 | * statics: {\r | |
1387 | * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'\r | |
1388 | * },\r | |
1389 | *\r | |
1390 | * constructor: function() {\r | |
1391 | * alert(this.self.speciesName); // dependent on 'this'\r | |
1392 | * },\r | |
1393 | *\r | |
1394 | * clone: function() {\r | |
1395 | * return new this.self();\r | |
1396 | * }\r | |
1397 | * });\r | |
1398 | *\r | |
1399 | *\r | |
1400 | * Ext.define('My.SnowLeopard', {\r | |
1401 | * extend: 'My.Cat',\r | |
1402 | * statics: {\r | |
1403 | * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'\r | |
1404 | * }\r | |
1405 | * });\r | |
1406 | *\r | |
1407 | * var cat = new My.Cat(); // alerts 'Cat'\r | |
1408 | * var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'\r | |
1409 | *\r | |
1410 | * var clone = snowLeopard.clone();\r | |
1411 | * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'\r | |
1412 | *\r | |
1413 | * @protected\r | |
1414 | */\r | |
1415 | self: Base,\r | |
1416 | \r | |
1417 | // Default constructor, simply returns `this`\r | |
1418 | constructor: function() {\r | |
1419 | return this;\r | |
1420 | },\r | |
1421 | \r | |
1422 | //<feature classSystem.config>\r | |
1423 | getConfigurator: function () {\r | |
1424 | return this.$config || this.self.getConfigurator();\r | |
1425 | },\r | |
1426 | \r | |
1427 | /**\r | |
1428 | * Initialize configuration for this class. a typical example:\r | |
1429 | *\r | |
1430 | * Ext.define('My.awesome.Class', {\r | |
1431 | * // The default config\r | |
1432 | * config: {\r | |
1433 | * name: 'Awesome',\r | |
1434 | * isAwesome: true\r | |
1435 | * },\r | |
1436 | *\r | |
1437 | * constructor: function(config) {\r | |
1438 | * this.initConfig(config);\r | |
1439 | * }\r | |
1440 | * });\r | |
1441 | *\r | |
1442 | * var awesome = new My.awesome.Class({\r | |
1443 | * name: 'Super Awesome'\r | |
1444 | * });\r | |
1445 | *\r | |
1446 | * alert(awesome.getName()); // 'Super Awesome'\r | |
1447 | *\r | |
1448 | * @protected\r | |
1449 | * @param {Object} config\r | |
1450 | * @return {Ext.Base} this\r | |
1451 | */\r | |
1452 | initConfig: function(instanceConfig) {\r | |
1453 | var me = this,\r | |
1454 | cfg = me.getConfigurator();\r | |
1455 | \r | |
1456 | me.initConfig = Ext.emptyFn; // ignore subsequent calls to initConfig\r | |
1457 | me.initialConfig = instanceConfig || {};\r | |
1458 | cfg.configure(me, instanceConfig);\r | |
1459 | \r | |
1460 | return me;\r | |
1461 | },\r | |
1462 | \r | |
1463 | beforeInitConfig: Ext.emptyFn,\r | |
1464 | \r | |
1465 | /**\r | |
1466 | * Returns a specified config property value. If the name parameter is not passed,\r | |
1467 | * all current configuration options will be returned as key value pairs.\r | |
1468 | * @method\r | |
1469 | * @param {String} [name] The name of the config property to get.\r | |
1470 | * @param {Boolean} [peek=false] `true` to peek at the raw value without calling the getter.\r | |
1471 | * @return {Object} The config property value.\r | |
1472 | */\r | |
1473 | getConfig: getConfig,\r | |
1474 | \r | |
1475 | /**\r | |
1476 | * Sets a single/multiple configuration options.\r | |
1477 | * @method\r | |
1478 | * @param {String/Object} name The name of the property to set, or a set of key value pairs to set.\r | |
1479 | * @param {Object} [value] The value to set for the name parameter.\r | |
1480 | * @return {Ext.Base} this\r | |
1481 | */\r | |
1482 | setConfig: function(name, value, /* private */ options) {\r | |
1483 | // options can have the following properties:\r | |
1484 | // - defaults `true` to only set the config(s) that have not been already set on\r | |
1485 | // this instance.\r | |
1486 | // - strict `false` to apply properties to the instance that are not configs,\r | |
1487 | // and do not have setters.\r | |
1488 | var me = this,\r | |
1489 | config;\r | |
1490 | \r | |
1491 | if (name) {\r | |
1492 | if (typeof name === 'string') {\r | |
1493 | config = {};\r | |
1494 | config[name] = value;\r | |
1495 | } else {\r | |
1496 | config = name;\r | |
1497 | }\r | |
1498 | \r | |
1499 | me.getConfigurator().reconfigure(me, config, options);\r | |
1500 | }\r | |
1501 | \r | |
1502 | return me;\r | |
1503 | },\r | |
1504 | \r | |
1505 | /**\r | |
1506 | * @private\r | |
1507 | */\r | |
1508 | getCurrentConfig: function() {\r | |
1509 | var cfg = this.getConfigurator();\r | |
1510 | \r | |
1511 | return cfg.getCurrentConfig(this);\r | |
1512 | },\r | |
1513 | \r | |
1514 | /**\r | |
1515 | * @private\r | |
1516 | * @param config\r | |
1517 | */\r | |
1518 | hasConfig: function(name) {\r | |
1519 | return name in this.defaultConfig;\r | |
1520 | },\r | |
1521 | \r | |
1522 | /**\r | |
1523 | * Returns the initial configuration passed to the constructor when \r | |
1524 | * instantiating this class.\r | |
1525 | * \r | |
1526 | * Given this example Ext.button.Button definition and instance:\r | |
1527 | * \r | |
1528 | * Ext.define('MyApp.view.Button', {\r | |
1529 | * extend: 'Ext.button.Button',\r | |
1530 | * xtype: 'mybutton',\r | |
1531 | * \r | |
1532 | * scale: 'large',\r | |
1533 | * enableToggle: true\r | |
1534 | * });\r | |
1535 | * \r | |
1536 | * var btn = Ext.create({\r | |
1537 | * xtype: 'mybutton',\r | |
1538 | * renderTo: Ext.getBody(),\r | |
1539 | * text: 'Test Button'\r | |
1540 | * });\r | |
1541 | * \r | |
1542 | * Calling `btn.getInitialConfig()` would return an object including the config \r | |
1543 | * options passed to the `create` method:\r | |
1544 | * \r | |
1545 | * xtype: 'mybutton',\r | |
1546 | * renderTo: // The document body itself\r | |
1547 | * text: 'Test Button'\r | |
1548 | * \r | |
1549 | * Calling `btn.getInitialConfig('text')`returns **'Test Button'**.\r | |
1550 | * \r | |
1551 | * @param {String} [name] Name of the config option to return.\r | |
1552 | * @return {Object/Mixed} The full config object or a single config value\r | |
1553 | * when `name` parameter specified.\r | |
1554 | */\r | |
1555 | getInitialConfig: function(name) {\r | |
1556 | var config = this.config;\r | |
1557 | \r | |
1558 | if (!name) {\r | |
1559 | return config;\r | |
1560 | }\r | |
1561 | \r | |
1562 | return config[name];\r | |
1563 | },\r | |
1564 | //</feature>\r | |
1565 | \r | |
1566 | $links: null,\r | |
1567 | \r | |
1568 | /**\r | |
1569 | * Adds a "destroyable" object to an internal list of objects that will be destroyed\r | |
1570 | * when this instance is destroyed (via `{@link #destroy}`).\r | |
1571 | * @param {String} name\r | |
1572 | * @param {Object} value\r | |
1573 | * @return {Object} The `value` passed.\r | |
1574 | * @private\r | |
1575 | */\r | |
1576 | link: function (name, value) {\r | |
1577 | var me = this,\r | |
1578 | links = me.$links || (me.$links = {});\r | |
1579 | \r | |
1580 | links[name] = true;\r | |
1581 | me[name] = value;\r | |
1582 | \r | |
1583 | return value;\r | |
1584 | },\r | |
1585 | \r | |
1586 | /**\r | |
1587 | * Destroys a given set of `{@link #link linked}` objects. This is only needed if\r | |
1588 | * the linked object is being destroyed before this instance.\r | |
1589 | * @param {String[]} names The names of the linked objects to destroy.\r | |
1590 | * @return {Ext.Base} this\r | |
1591 | * @private\r | |
1592 | */\r | |
1593 | unlink: function (names) {\r | |
1594 | var me = this,\r | |
1595 | i, ln, link, value;\r | |
1596 | \r | |
1597 | //<debug>\r | |
1598 | if (!Ext.isArray(names)) {\r | |
1599 | Ext.raise('Invalid argument - expected array of strings');\r | |
1600 | }\r | |
1601 | //</debug>\r | |
1602 | \r | |
1603 | for (i = 0, ln = names.length; i < ln; i++) {\r | |
1604 | link = names[i];\r | |
1605 | value = me[link];\r | |
1606 | \r | |
1607 | if (value) {\r | |
1608 | if (value.isInstance && !value.destroyed) {\r | |
1609 | value.destroy();\r | |
1610 | }\r | |
1611 | else if (value.parentNode && 'nodeType' in value) {\r | |
1612 | value.parentNode.removeChild(value);\r | |
1613 | }\r | |
1614 | }\r | |
1615 | \r | |
1616 | me[link] = null;\r | |
1617 | }\r | |
1618 | \r | |
1619 | return me;\r | |
1620 | },\r | |
1621 | \r | |
1622 | /**\r | |
1623 | * This method is called to cleanup an object and its resources. After calling\r | |
1624 | * this method, the object should not be used any further.\r | |
1625 | */\r | |
1626 | destroy: function() {\r | |
1627 | var me = this,\r | |
1628 | links = me.$links;\r | |
1629 | \r | |
1630 | me.initialConfig = me.config = null;\r | |
1631 | \r | |
1632 | me.destroy = Ext.emptyFn;\r | |
1633 | // isDestroyed added for compat reasons\r | |
1634 | me.isDestroyed = me.destroyed = true;\r | |
1635 | \r | |
1636 | if (links) {\r | |
1637 | me.$links = null;\r | |
1638 | me.unlink(Ext.Object.getKeys(links));\r | |
1639 | }\r | |
1640 | }\r | |
1641 | });\r | |
1642 | \r | |
1643 | /**\r | |
1644 | * Call the original method that was previously overridden with {@link Ext.Base#override}\r | |
1645 | *\r | |
1646 | * Ext.define('My.Cat', {\r | |
1647 | * constructor: function() {\r | |
1648 | * alert("I'm a cat!");\r | |
1649 | * }\r | |
1650 | * });\r | |
1651 | *\r | |
1652 | * My.Cat.override({\r | |
1653 | * constructor: function() {\r | |
1654 | * alert("I'm going to be a cat!");\r | |
1655 | *\r | |
1656 | * this.callOverridden();\r | |
1657 | *\r | |
1658 | * alert("Meeeeoooowwww");\r | |
1659 | * }\r | |
1660 | * });\r | |
1661 | *\r | |
1662 | * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"\r | |
1663 | * // alerts "I'm a cat!"\r | |
1664 | * // alerts "Meeeeoooowwww"\r | |
1665 | *\r | |
1666 | * @param {Array/Arguments} args The arguments, either an array or the `arguments` object\r | |
1667 | * from the current method, for example: `this.callOverridden(arguments)`\r | |
1668 | * @return {Object} Returns the result of calling the overridden method\r | |
1669 | * @protected\r | |
1670 | * @deprecated Use {@link #callParent} instead.\r | |
1671 | */\r | |
1672 | BasePrototype.callOverridden = BasePrototype.callParent;\r | |
1673 | \r | |
1674 | //<debug>\r | |
1675 | Ext.privacyViolation = function (cls, existing, member, isStatic) {\r | |
1676 | var name = member.$name,\r | |
1677 | conflictCls = existing.$owner && existing.$owner.$className,\r | |
1678 | s = isStatic ? 'static ' : '',\r | |
1679 | msg = member.$privacy\r | |
1680 | ? 'Private ' + s + member.$privacy + ' method "' + name + '"'\r | |
1681 | : 'Public ' + s + 'method "' + name + '"';\r | |
1682 | \r | |
1683 | if (cls.$className) {\r | |
1684 | msg = cls.$className + ': ' + msg;\r | |
1685 | }\r | |
1686 | \r | |
1687 | if (!existing.$privacy) {\r | |
1688 | msg += conflictCls\r | |
1689 | ? ' hides public method inherited from ' + conflictCls\r | |
1690 | : ' hides inherited public method.';\r | |
1691 | } else {\r | |
1692 | msg += conflictCls\r | |
1693 | ? ' conflicts with private ' + existing.$privacy +\r | |
1694 | ' method declared by ' + conflictCls\r | |
1695 | : ' conflicts with inherited private ' + existing.$privacy + ' method.';\r | |
1696 | }\r | |
1697 | \r | |
1698 | var compat = Ext.getCompatVersion();\r | |
1699 | var ver = Ext.getVersion();\r | |
1700 | \r | |
1701 | // When compatibility is enabled, log problems instead of throwing errors.\r | |
1702 | if (ver && compat && compat.lt(ver)) {\r | |
1703 | Ext.log.error(msg);\r | |
1704 | } else {\r | |
1705 | Ext.raise(msg);\r | |
1706 | }\r | |
1707 | };\r | |
1708 | //</debug>\r | |
1709 | \r | |
1710 | return Base;\r | |
1711 | }(Ext.Function.flexSetter));\r |