2 * @author Jacky Nguyen <jacky@sencha.com>
3 * @docauthor Jacky Nguyen <jacky@sencha.com>
6 * The root of all classes created with {@link Ext#define}
7 * All prototype and static members of this class are inherited by any other class
10 (function(flexSetter) {
12 var Base = Ext.Base = function() {};
14 $className: 'Ext.Base',
19 * Get the reference to the current class from which this object was instantiated. Unlike {@link Ext.Base#statics},
20 * `this.self` is scope-dependent and it's meant to be used for dynamic inheritance. See {@link Ext.Base#statics}
21 * for a detailed comparison
23 * Ext.define('My.Cat', {
25 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
28 * constructor: function() {
29 * alert(this.self.speciesName); / dependent on 'this'
35 * return new this.self();
40 * Ext.define('My.SnowLeopard', {
43 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
47 * var cat = new My.Cat(); // alerts 'Cat'
48 * var snowLeopard = new My.SnowLeopard(); // alerts 'Snow Leopard'
50 * var clone = snowLeopard.clone();
51 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
60 * Default constructor, simply returns `this`
64 * @return {Object} this
66 constructor: function() {
71 * Initialize configuration for this class. a typical example:
73 * Ext.define('My.awesome.Class', {
74 * // The default config
80 * constructor: function(config) {
81 * this.initConfig(config);
87 * var awesome = new My.awesome.Class({
88 * name: 'Super Awesome'
91 * alert(awesome.getName()); // 'Super Awesome'
94 * @param {Object} config
95 * @return {Object} mixins The mixin prototypes as key - value pairs
98 initConfig: function(config) {
99 if (!this.$configInited) {
100 this.config = Ext.Object.merge({}, this.config || {}, config || {});
102 this.applyConfig(this.config);
104 this.$configInited = true;
113 setConfig: function(config) {
114 this.applyConfig(config || {});
122 applyConfig: flexSetter(function(name, value) {
123 var setter = 'set' + Ext.String.capitalize(name);
125 if (typeof this[setter] === 'function') {
126 this[setter].call(this, value);
133 * Call the parent's overridden method. For example:
135 * Ext.define('My.own.A', {
136 * constructor: function(test) {
141 * Ext.define('My.own.B', {
142 * extend: 'My.own.A',
144 * constructor: function(test) {
147 * this.callParent([test + 1]);
151 * Ext.define('My.own.C', {
152 * extend: 'My.own.B',
154 * constructor: function() {
155 * alert("Going to call parent's overriden constructor...");
157 * this.callParent(arguments);
161 * var a = new My.own.A(1); // alerts '1'
162 * var b = new My.own.B(1); // alerts '1', then alerts '2'
163 * var c = new My.own.C(2); // alerts "Going to call parent's overriden constructor..."
164 * // alerts '2', then alerts '3'
167 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
168 * from the current method, for example: `this.callParent(arguments)`
169 * @return {Mixed} Returns the result from the superclass' method
172 callParent: function(args) {
173 var method = this.callParent.caller,
174 parentClass, methodName;
176 if (!method.$owner) {
178 if (!method.caller) {
180 sourceClass: Ext.getClassName(this),
181 sourceMethod: "callParent",
182 msg: "Attempting to call a protected method from the public scope, which is not allowed"
187 method = method.caller;
190 parentClass = method.$owner.superclass;
191 methodName = method.$name;
194 if (!(methodName in parentClass)) {
196 sourceClass: Ext.getClassName(this),
197 sourceMethod: methodName,
198 msg: "this.callParent() was called but there's no such method (" + methodName +
199 ") found in the parent class (" + (Ext.getClassName(parentClass) || 'Object') + ")"
204 return parentClass[methodName].apply(this, args || []);
209 * Get the reference to the class from which this object was instantiated. Note that unlike {@link Ext.Base#self},
210 * `this.statics()` is scope-independent and it always returns the class from which it was called, regardless of what
211 * `this` points to during run-time
213 * Ext.define('My.Cat', {
216 * speciesName: 'Cat' // My.Cat.speciesName = 'Cat'
219 * constructor: function() {
220 * var statics = this.statics();
222 * alert(statics.speciesName); // always equals to 'Cat' no matter what 'this' refers to
223 * // equivalent to: My.Cat.speciesName
225 * alert(this.self.speciesName); // dependent on 'this'
227 * statics.totalCreated++;
232 * clone: function() {
233 * var cloned = new this.self; // dependent on 'this'
235 * cloned.groupName = this.statics().speciesName; // equivalent to: My.Cat.speciesName
242 * Ext.define('My.SnowLeopard', {
246 * speciesName: 'Snow Leopard' // My.SnowLeopard.speciesName = 'Snow Leopard'
249 * constructor: function() {
254 * var cat = new My.Cat(); // alerts 'Cat', then alerts 'Cat'
256 * var snowLeopard = new My.SnowLeopard(); // alerts 'Cat', then alerts 'Snow Leopard'
258 * var clone = snowLeopard.clone();
259 * alert(Ext.getClassName(clone)); // alerts 'My.SnowLeopard'
260 * alert(clone.groupName); // alerts 'Cat'
262 * alert(My.Cat.totalCreated); // alerts 3
268 statics: function() {
269 var method = this.statics.caller,
276 return method.$owner;
280 * Call the original method that was previously overridden with {@link Ext.Base#override}
282 * Ext.define('My.Cat', {
283 * constructor: function() {
284 * alert("I'm a cat!");
291 * constructor: function() {
292 * alert("I'm going to be a cat!");
294 * var instance = this.callOverridden();
296 * alert("Meeeeoooowwww");
302 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
303 * // alerts "I'm a cat!"
304 * // alerts "Meeeeoooowwww"
306 * @param {Array/Arguments} args The arguments, either an array or the `arguments` object
307 * @return {Mixed} Returns the result after calling the overridden method
310 callOverridden: function(args) {
311 var method = this.callOverridden.caller;
314 if (!method.$owner) {
316 sourceClass: Ext.getClassName(this),
317 sourceMethod: "callOverridden",
318 msg: "Attempting to call a protected method from the public scope, which is not allowed"
322 if (!method.$previous) {
324 sourceClass: Ext.getClassName(this),
325 sourceMethod: "callOverridden",
326 msg: "this.callOverridden was called in '" + method.$name +
327 "' but this method has never been overridden"
332 return method.$previous.apply(this, args || []);
335 destroy: function() {}
338 // These static properties will be copied to every newly created class with {@link Ext#define}
339 Ext.apply(Ext.Base, {
341 * Create a new instance of this Class.
343 * Ext.define('My.cool.Class', {
347 * My.cool.Class.create({
357 return Ext.create.apply(Ext, [this].concat(Array.prototype.slice.call(arguments, 0)));
363 own: flexSetter(function(name, value) {
364 if (typeof value === 'function') {
365 this.ownMethod(name, value);
368 this.prototype[name] = value;
375 ownMethod: function(name, fn) {
378 if (fn.$owner !== undefined && fn !== Ext.emptyFn) {
382 return originalFn.apply(this, arguments);
388 className = Ext.getClassName(this);
390 fn.displayName = className + '#' + name;
396 this.prototype[name] = fn;
400 * Add / override static properties of this class.
402 * Ext.define('My.cool.Class', {
406 * My.cool.Class.addStatics({
407 * someProperty: 'someValue', // My.cool.Class.someProperty = 'someValue'
408 * method1: function() { ... }, // My.cool.Class.method1 = function() { ... };
409 * method2: function() { ... } // My.cool.Class.method2 = function() { ... };
412 * @property addStatics
415 * @param {Object} members
418 addStatics: function(members) {
419 for (var name in members) {
420 if (members.hasOwnProperty(name)) {
421 this[name] = members[name];
429 * Add methods / properties to the prototype of this class.
431 * Ext.define('My.awesome.Cat', {
432 * constructor: function() {
437 * My.awesome.Cat.implement({
439 * alert('Meowww...');
443 * var kitty = new My.awesome.Cat;
446 * @property implement
449 * @param {Object} members
452 implement: function(members) {
453 var prototype = this.prototype,
454 name, i, member, previous;
456 var className = Ext.getClassName(this);
458 for (name in members) {
459 if (members.hasOwnProperty(name)) {
460 member = members[name];
462 if (typeof member === 'function') {
463 member.$owner = this;
467 member.displayName = className + '#' + name;
472 prototype[name] = member;
476 if (Ext.enumerables) {
477 var enumerables = Ext.enumerables;
479 for (i = enumerables.length; i--;) {
480 name = enumerables[i];
482 if (members.hasOwnProperty(name)) {
483 member = members[name];
484 member.$owner = this;
486 prototype[name] = member;
493 * Borrow another class' members to the prototype of this class.
495 * Ext.define('Bank', {
497 * printMoney: function() {
502 * Ext.define('Thief', {
506 * Thief.borrow(Bank, ['money', 'printMoney']);
508 * var steve = new Thief();
510 * alert(steve.money); // alerts '$$$'
511 * steve.printMoney(); // alerts '$$$$$$$'
516 * @param {Ext.Base} fromClass The class to borrow members from
517 * @param {Array/String} members The names of the members to borrow
518 * @return {Ext.Base} this
521 borrow: function(fromClass, members) {
522 var fromPrototype = fromClass.prototype,
525 members = Ext.Array.from(members);
527 for (i = 0, ln = members.length; i < ln; i++) {
530 this.own(member, fromPrototype[member]);
537 * Override prototype members of this class. Overridden methods can be invoked via
538 * {@link Ext.Base#callOverridden}
540 * Ext.define('My.Cat', {
541 * constructor: function() {
542 * alert("I'm a cat!");
549 * constructor: function() {
550 * alert("I'm going to be a cat!");
552 * var instance = this.callOverridden();
554 * alert("Meeeeoooowwww");
560 * var kitty = new My.Cat(); // alerts "I'm going to be a cat!"
561 * // alerts "I'm a cat!"
562 * // alerts "Meeeeoooowwww"
567 * @param {Object} members
568 * @return {Ext.Base} this
571 override: function(members) {
572 var prototype = this.prototype,
573 name, i, member, previous;
575 for (name in members) {
576 if (members.hasOwnProperty(name)) {
577 member = members[name];
579 if (typeof member === 'function') {
580 if (typeof prototype[name] === 'function') {
581 previous = prototype[name];
582 member.$previous = previous;
585 this.ownMethod(name, member);
588 prototype[name] = member;
593 if (Ext.enumerables) {
594 var enumerables = Ext.enumerables;
596 for (i = enumerables.length; i--;) {
597 name = enumerables[i];
599 if (members.hasOwnProperty(name)) {
600 if (prototype[name] !== undefined) {
601 previous = prototype[name];
602 members[name].$previous = previous;
605 this.ownMethod(name, members[name]);
614 * Used internally by the mixins pre-processor
617 mixin: flexSetter(function(name, cls) {
618 var mixin = cls.prototype,
623 if (mixin.hasOwnProperty(i)) {
624 if (my[i] === undefined) {
625 if (typeof mixin[i] === 'function') {
628 if (fn.$owner === undefined) {
629 this.ownMethod(i, fn);
639 else if (i === 'config' && my.config && mixin.config) {
640 Ext.Object.merge(my.config, mixin.config);
645 if (my.mixins === undefined) {
649 my.mixins[name] = mixin;
653 * Get the current class' name in string format.
655 * Ext.define('My.cool.Class', {
656 * constructor: function() {
657 * alert(this.self.getName()); // alerts 'My.cool.Class'
661 * My.cool.Class.getName(); // 'My.cool.Class'
663 * @return {String} className
666 getName: function() {
667 return Ext.getClassName(this);
671 * Create aliases for existing prototype methods. Example:
673 * Ext.define('My.cool.Class', {
674 * method1: function() { ... },
675 * method2: function() { ... }
678 * var test = new My.cool.Class();
680 * My.cool.Class.createAlias({
681 * method3: 'method1',
685 * test.method3(); // test.method1()
687 * My.cool.Class.createAlias('method5', 'method3');
689 * test.method5(); // test.method3() -> test.method1()
691 * @property createAlias
694 * @param {String/Object} alias The new method name, or an object to set multiple aliases. See
695 * {@link Ext.Function#flexSetter flexSetter}
696 * @param {String/Object} origin The original method name
699 createAlias: flexSetter(function(alias, origin) {
700 this.prototype[alias] = this.prototype[origin];
704 })(Ext.Function.flexSetter);