3 This file is part of Ext JS 4
5 Copyright (c) 2011 Sencha Inc
7 Contact: http://www.sencha.com/contact
9 GNU General Public License Usage
10 This file may be used under the terms of the GNU General Public License version 3.0 as published by the Free Software Foundation and appearing in the file LICENSE included in the packaging of this file. Please review the following information to ensure the GNU General Public License version 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 If you are unsure which license is appropriate for your use, please contact the sales department at http://www.sencha.com/contact.
16 * @author Jacky Nguyen <jacky@sencha.com>
17 * @docauthor Jacky Nguyen <jacky@sencha.com>
20 * The root of all classes created with {@link Ext#define}
21 * All prototype and static members of this class are inherited by any other class
24 (function(flexSetter) {
26 var Base = Ext.Base = function() {};
28 $className: 'Ext.Base',
33 * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
34 * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
35 * for a detailed comparison
37 * Ext.define('My.Cat', {
39 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
42 * constructor: function() {
43 * alert(this.self.speciesName); / dependent on 'this'
49 * return new this.self();
54 * Ext.define('My.SnowLeopard', {
57 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
61 * var cat = new My.Cat(); // alerts 'Cat'
62 * var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'
64 * var clone = snowLeopard.clone();
65 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
72 // Default constructor, simply returns `this`
73 constructor: function() {
78 * Initialize configuration for this class. a typical example:
80 * Ext.define('My.awesome.Class', {
81 * // The default config
87 * constructor: function(config) {
88 * this.initConfig(config);
94 * var awesome = new My.awesome.Class({
95 * name: 'Super Awesome'
98 * alert(awesome.getName()); // 'Super Awesome'
101 * @param {Object} config
102 * @return {Object} mixins The mixin prototypes as key - value pairs
104 initConfig: function(config) {
105 if (!this.$configInited) {
106 this.config = Ext.Object.merge({}, this.config || {}, config || {});
108 this.applyConfig(this.config);
110 this.$configInited = true;
119 setConfig: function(config) {
120 this.applyConfig(config || {});
128 applyConfig: flexSetter(function(name, value) {
129 var setter = 'set' + Ext.String.capitalize(name);
131 if (typeof this[setter] === 'function') {
132 this[setter].call(this, value);
139 * Call the parent's overridden method. For example:
141 * Ext.define('My.own.A', {
142 * constructor: function(test) {
147 * Ext.define('My.own.B', {
148 * extend: 'My.own.A',
150 * constructor: function(test) {
153 * this.callParent([test + 1]);
157 * Ext.define('My.own.C', {
158 * extend: 'My.own.B',
160 * constructor: function() {
161 * alert("Going to call parent's overriden constructor...");
163 * this.callParent(arguments);
167 * var a = new My.own.A(1); // alerts '1'
168 * var b = new My.own.B(1); // alerts '1', then alerts '2'
169 * var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
170 * // alerts '2', then alerts '3'
173 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
174 * from the current method, for example: `this.callParent(arguments)`
175 * @return {Mixed} Returns the result from the superclass' method
177 callParent: function(args) {
178 var method = this.callParent.caller,
179 parentClass, methodName;
181 if (!method.$owner) {
183 if (!method.caller) {
185 sourceClass: Ext.getClassName(this),
186 sourceMethod: "callParent",
187 msg: "Attempting to call a protected method from the public scope, which is not allowed"
192 method = method.caller;
195 parentClass = method.$owner.superclass;
196 methodName = method.$name;
199 if (!(methodName in parentClass)) {
201 sourceClass: Ext.getClassName(this),
202 sourceMethod: methodName,
203 msg: "this.callParent() was called but there's no such method (" + methodName +
204 ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"
209 return parentClass[methodName].apply(this, args || []);
214 * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
215 * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
216 * `this` points to during run-time
218 * Ext.define('My.Cat', {
221 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
224 * constructor: function() {
225 * var statics = this.statics();
227 * alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to
228 * // equivalent to: My.Cat.speciesName
230 * alert(this.self.speciesName); // dependent on 'this'
232 * statics.totalCreated++;
237 * clone: function() {
238 * var cloned = new this.self; // dependent on 'this'
240 * cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName
247 * Ext.define('My.SnowLeopard', {
251 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
254 * constructor: function() {
259 * var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'
261 * var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
263 * var clone = snowLeopard.clone();
264 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
265 * alert(clone.groupName); // alerts 'Cat'
267 * alert(My.Cat.totalCreated); // alerts 3
272 statics: function() {
273 var method = this.statics.caller,
280 return method.$owner;
284 * Call the original method that was previously overridden with {@link Ext.Base#override}
286 * Ext.define('My.Cat', {
287 * constructor: function() {
288 * alert("I'm a cat!");
295 * constructor: function() {
296 * alert("I'm going to be a cat!");
298 * var instance = this.callOverridden();
300 * alert("Meeeeoooowwww");
306 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
307 * // alerts "I'm a cat!"
308 * // alerts "Meeeeoooowwww"
310 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
311 * @return {Mixed} Returns the result after calling the overridden method
313 callOverridden: function(args) {
314 var method = this.callOverridden.caller;
317 if (!method.$owner) {
319 sourceClass: Ext.getClassName(this),
320 sourceMethod: "callOverridden",
321 msg: "Attempting to call a protected method from the public scope, which is not allowed"
325 if (!method.$previous) {
327 sourceClass: Ext.getClassName(this),
328 sourceMethod: "callOverridden",
329 msg: "this.callOverridden was called in '" + method.$name +
330 "' but this method has never been overridden"
335 return method.$previous.apply(this, args || []);
338 destroy: function() {}
341 // These static properties will be copied to every newly created class with {@link Ext#define}
342 Ext.apply(Ext.Base, {
344 * Create a new instance of this Class.
346 * Ext.define('My.cool.Class', {
350 * My.cool.Class.create({
354 * All parameters are passed to the constructor of the class.
356 * @return {Object} the created instance.
360 return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
366 own: flexSetter(function(name, value) {
367 if (typeof value === 'function') {
368 this.ownMethod(name, value);
371 this.prototype[name] = value;
378 ownMethod: function(name, fn) {
381 if (fn.$owner !== undefined && fn !== Ext.emptyFn) {
385 return originalFn.apply(this, arguments);
391 className = Ext.getClassName(this);
393 fn.displayName = className + '#' + name;
399 this.prototype[name] = fn;
403 * Add / override static properties of this class.
405 * Ext.define('My.cool.Class', {
409 * My.cool.Class.addStatics({
410 * someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'
411 * method1: function() { ... }, // My.cool.Class.method1 = function() { ... };
412 * method2: function() { ... } // My.cool.Class.method2 = function() { ... };
415 * @param {Object} members
416 * @return {Ext.Base} this
419 addStatics: function(members) {
420 for (var name in members) {
421 if (members.hasOwnProperty(name)) {
422 this[name] = members[name];
430 * Add methods / properties to the prototype of this class.
432 * Ext.define('My.awesome.Cat', {
433 * constructor: function() {
438 * My.awesome.Cat.implement({
440 * alert('Meowww...');
444 * var kitty = new My.awesome.Cat;
447 * @param {Object} members
450 implement: function(members) {
451 var prototype = this.prototype,
452 name, i, member, previous;
454 var className = Ext.getClassName(this);
456 for (name in members) {
457 if (members.hasOwnProperty(name)) {
458 member = members[name];
460 if (typeof member === 'function') {
461 member.$owner = this;
465 member.displayName = className + '#' + name;
470 prototype[name] = member;
474 if (Ext.enumerables) {
475 var enumerables = Ext.enumerables;
477 for (i = enumerables.length; i--;) {
478 name = enumerables[i];
480 if (members.hasOwnProperty(name)) {
481 member = members[name];
482 member.$owner = this;
484 prototype[name] = member;
491 * Borrow another class' members to the prototype of this class.
493 * Ext.define('Bank', {
495 * printMoney: function() {
500 * Ext.define('Thief', {
504 * Thief.borrow(Bank, ['money', 'printMoney']);
506 * var steve = new Thief();
508 * alert(steve.money); // alerts '$$$'
509 * steve.printMoney(); // alerts '$$$$$$$'
511 * @param {Ext.Base} fromClass The class to borrow members from
512 * @param {Array/String} members The names of the members to borrow
513 * @return {Ext.Base} this
517 borrow: function(fromClass, members) {
518 var fromPrototype = fromClass.prototype,
521 members = Ext.Array.from(members);
523 for (i = 0, ln = members.length; i < ln; i++) {
526 this.own(member, fromPrototype[member]);
533 * Override prototype members of this class. Overridden methods can be invoked via
534 * {@link Ext.Base#callOverridden}
536 * Ext.define('My.Cat', {
537 * constructor: function() {
538 * alert("I'm a cat!");
545 * constructor: function() {
546 * alert("I'm going to be a cat!");
548 * var instance = this.callOverridden();
550 * alert("Meeeeoooowwww");
556 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
557 * // alerts "I'm a cat!"
558 * // alerts "Meeeeoooowwww"
560 * @param {Object} members
561 * @return {Ext.Base} this
564 override: function(members) {
565 var prototype = this.prototype,
566 name, i, member, previous;
568 for (name in members) {
569 if (members.hasOwnProperty(name)) {
570 member = members[name];
572 if (typeof member === 'function') {
573 if (typeof prototype[name] === 'function') {
574 previous = prototype[name];
575 member.$previous = previous;
578 this.ownMethod(name, member);
581 prototype[name] = member;
586 if (Ext.enumerables) {
587 var enumerables = Ext.enumerables;
589 for (i = enumerables.length; i--;) {
590 name = enumerables[i];
592 if (members.hasOwnProperty(name)) {
593 if (prototype[name] !== undefined) {
594 previous = prototype[name];
595 members[name].$previous = previous;
598 this.ownMethod(name, members[name]);
607 * Used internally by the mixins pre-processor
610 mixin: flexSetter(function(name, cls) {
611 var mixin = cls.prototype,
616 if (mixin.hasOwnProperty(i)) {
617 if (my[i] === undefined) {
618 if (typeof mixin[i] === 'function') {
621 if (fn.$owner === undefined) {
622 this.ownMethod(i, fn);
632 else if (i === 'config' && my.config && mixin.config) {
633 Ext.Object.merge(my.config, mixin.config);
638 if (my.mixins === undefined) {
642 my.mixins[name] = mixin;
646 * Get the current class' name in string format.
648 * Ext.define('My.cool.Class', {
649 * constructor: function() {
650 * alert(this.self.getName()); // alerts 'My.cool.Class'
654 * My.cool.Class.getName(); // 'My.cool.Class'
656 * @return {String} className
658 getName: function() {
659 return Ext.getClassName(this);
663 * Create aliases for existing prototype methods. Example:
665 * Ext.define('My.cool.Class', {
666 * method1: function() { ... },
667 * method2: function() { ... }
670 * var test = new My.cool.Class();
672 * My.cool.Class.createAlias({
673 * method3: 'method1',
677 * test.method3(); // test.method1()
679 * My.cool.Class.createAlias('method5', 'method3');
681 * test.method5(); // test.method3() -> test.method1()
683 * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
684 * {@link Ext.Function#flexSetter flexSetter}
685 * @param {String/Object} origin The original method name
689 createAlias: flexSetter(function(alias, origin) {
690 this.prototype[alias] = this.prototype[origin];
694 })(Ext.Function.flexSetter);