2 //@define Ext.ClassManager
6 * @class Ext.ClassManager
7 * @author Jacky Nguyen <jacky@sencha.com>
9 * Ext.ClassManager manages all classes and handles mapping from string class name to
10 * actual class objects throughout the whole framework. It is not generally accessed directly, rather through
11 * these convenient shorthands:
13 * - {@link Ext#define Ext.define}
14 * - {@link Ext.ClassManager#create Ext.create}
15 * - {@link Ext#widget Ext.widget}
16 * - {@link Ext#getClass Ext.getClass}
17 * - {@link Ext#getClassName Ext.getClassName}
19 * For more information about Sencha Touch's Class System, please check out the [class system guide](../../../core_concepts/class_system.html).
23 * Ext.define(className, properties);
25 * in which `properties` is an object represent a collection of properties that apply to the class. See
26 * {@link Ext.ClassManager#create} for more detailed instructions.
29 * Ext.define('Person', {
32 * constructor: function(name) {
40 * eat: function(foodType) {
41 * alert("I'm eating: " + foodType);
47 * var aaron = new Person("Aaron");
48 * aaron.eat("Sandwich"); // alert("I'm eating: Sandwich");
50 * Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of
51 * everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc.
55 * Ext.define('Developer', {
58 * constructor: function(name, isGeek) {
59 * this.isGeek = isGeek;
61 * // Apply a method from the parent class' prototype
62 * this.callParent([name]);
68 * code: function(language) {
69 * alert("I'm coding in: " + language);
77 * var jacky = new Developer("Jacky", true);
78 * jacky.code("JavaScript"); // alert("I'm coding in: JavaScript");
79 * // alert("I'm eating: Bugs");
81 * See {@link Ext.Base#callParent} for more details on calling superclass' methods
85 * Ext.define('CanPlayGuitar', {
86 * playGuitar: function() {
87 * alert("F#...G...D...A");
91 * Ext.define('CanComposeSongs', {
92 * composeSongs: function() { }
95 * Ext.define('CanSing', {
97 * alert("I'm on the highway to hell...");
101 * Ext.define('Musician', {
105 * canPlayGuitar: 'CanPlayGuitar',
106 * canComposeSongs: 'CanComposeSongs',
111 * Ext.define('CoolPerson', {
115 * canPlayGuitar: 'CanPlayGuitar',
122 * this.mixins.canSing.sing.call(this);
124 * alert("[Playing guitar at the same time...]");
130 * var me = new CoolPerson("Jacky");
132 * me.sing(); // alert("Ahem...");
133 * // alert("I'm on the highway to hell...");
134 * // alert("[Playing guitar at the same time...]");
135 * // alert("F#...G...D...A");
139 * Ext.define('SmartPhone', {
141 * hasTouchScreen: false,
142 * operatingSystem: 'Other',
146 * isExpensive: false,
148 * constructor: function(config) {
149 * this.initConfig(config);
154 * applyPrice: function(price) {
155 * this.isExpensive = (price > 500);
160 * applyOperatingSystem: function(operatingSystem) {
161 * if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) {
165 * return operatingSystem;
169 * var iPhone = new SmartPhone({
170 * hasTouchScreen: true,
171 * operatingSystem: 'iOS'
174 * iPhone.getPrice(); // 500;
175 * iPhone.getOperatingSystem(); // 'iOS'
176 * iPhone.getHasTouchScreen(); // true;
178 * iPhone.isExpensive; // false;
179 * iPhone.setPrice(600);
180 * iPhone.getPrice(); // 600
181 * iPhone.isExpensive; // true;
183 * iPhone.setOperatingSystem('AlienOS');
184 * iPhone.getOperatingSystem(); // 'Other'
188 * Ext.define('Computer', {
190 * factory: function(brand) {
191 * // 'this' in static methods refer to the class itself
192 * return new this(brand);
196 * constructor: function() { }
199 * var dellComputer = Computer.factory('Dell');
201 * Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing
202 * static properties within class methods
206 (function(Class
, alias
, arraySlice
, arrayFrom
, global
) {
208 var isNonBrowser
= typeof window
== 'undefined';
210 var Manager
= Ext
.ClassManager
= {
215 * All classes which were defined through the ClassManager. Keys are the
216 * name of the classes and the values are references to the classes.
229 namespaceRewrites
: [{
245 enableNamespaceParseCache
: true,
248 namespaceParseCache
: {},
254 * Checks if a class has already been created.
256 * @param {String} className
257 * @return {Boolean} exist
259 isCreated: function(className
) {
260 var existCache
= this.existCache
,
261 i
, ln
, part
, root
, parts
;
264 if (typeof className
!= 'string' || className
.length
< 1) {
265 throw new Error("[Ext.ClassManager] Invalid classname, must be a string and must not be empty");
269 if (this.classes
[className
] || existCache
[className
]) {
274 parts
= this.parseNamespace(className
);
276 for (i
= 0, ln
= parts
.length
; i
< ln
; i
++) {
279 if (typeof part
!= 'string') {
282 if (!root
|| !root
[part
]) {
290 existCache
[className
] = true;
292 this.triggerCreated(className
);
300 createdListeners
: [],
305 nameCreatedListeners
: {},
310 triggerCreated: function(className
) {
311 var listeners
= this.createdListeners
,
312 nameListeners
= this.nameCreatedListeners
,
313 alternateNames
= this.maps
.nameToAlternates
[className
],
315 i
, ln
, j
, subLn
, listener
, name
;
317 for (i
= 0,ln
= listeners
.length
; i
< ln
; i
++) {
318 listener
= listeners
[i
];
319 listener
.fn
.call(listener
.scope
, className
);
322 if (alternateNames
) {
323 names
.push
.apply(names
, alternateNames
);
326 for (i
= 0,ln
= names
.length
; i
< ln
; i
++) {
328 listeners
= nameListeners
[name
];
331 for (j
= 0,subLn
= listeners
.length
; j
< subLn
; j
++) {
332 listener
= listeners
[j
];
333 listener
.fn
.call(listener
.scope
, name
);
335 delete nameListeners
[name
];
343 onCreated: function(fn
, scope
, className
) {
344 var listeners
= this.createdListeners
,
345 nameListeners
= this.nameCreatedListeners
,
352 if (this.isCreated(className
)) {
353 fn
.call(scope
, className
);
357 if (!nameListeners
[className
]) {
358 nameListeners
[className
] = [];
361 nameListeners
[className
].push(listener
);
364 listeners
.push(listener
);
369 * Supports namespace rewriting.
372 parseNamespace: function(namespace) {
374 if (typeof namespace != 'string') {
375 throw new Error("[Ext.ClassManager] Invalid namespace, must be a string");
379 var cache
= this.namespaceParseCache
;
381 if (this.enableNamespaceParseCache
) {
382 if (cache
.hasOwnProperty(namespace)) {
383 return cache
[namespace];
388 rewrites
= this.namespaceRewrites
,
391 rewrite
, from, to
, i
, ln
;
393 for (i
= 0, ln
= rewrites
.length
; i
< ln
; i
++) {
394 rewrite
= rewrites
[i
];
398 if (name
=== from || name
.substring(0, from.length
) === from) {
399 name
= name
.substring(from.length
);
401 if (typeof to
!= 'string') {
404 parts
= parts
.concat(to
.split('.'));
413 parts
= parts
.concat(name
.split('.'));
415 if (this.enableNamespaceParseCache
) {
416 cache
[namespace] = parts
;
423 * Creates a namespace and assign the `value` to the created object.
425 * Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject);
426 * alert(MyCompany.pkg.Example === someObject); // alerts true
428 * @param {String} name
429 * @param {Mixed} value
431 setNamespace: function(name
, value
) {
433 parts
= this.parseNamespace(name
),
434 ln
= parts
.length
- 1,
438 for (i
= 0; i
< ln
; i
++) {
441 if (typeof part
!= 'string') {
458 * The new Ext.ns, supports namespace rewriting.
461 createNamespaces: function() {
463 parts
, part
, i
, j
, ln
, subLn
;
465 for (i
= 0, ln
= arguments
.length
; i
< ln
; i
++) {
466 parts
= this.parseNamespace(arguments
[i
]);
468 for (j
= 0, subLn
= parts
.length
; j
< subLn
; j
++) {
471 if (typeof part
!= 'string') {
487 * Sets a name reference to a class.
489 * @param {String} name
490 * @param {Object} value
491 * @return {Ext.ClassManager} this
493 set: function(name
, value
) {
496 nameToAlternates
= maps
.nameToAlternates
,
497 targetName
= me
.getName(value
),
500 me
.classes
[name
] = me
.setNamespace(name
, value
);
502 if (targetName
&& targetName
!== name
) {
503 maps
.alternateToName
[name
] = targetName
;
504 alternates
= nameToAlternates
[targetName
] || (nameToAlternates
[targetName
] = []);
505 alternates
.push(name
);
512 * Retrieve a class by its name.
514 * @param {String} name
515 * @return {Ext.Class} class
517 get: function(name
) {
518 var classes
= this.classes
;
521 return classes
[name
];
525 parts
= this.parseNamespace(name
),
528 for (i
= 0, ln
= parts
.length
; i
< ln
; i
++) {
531 if (typeof part
!= 'string') {
534 if (!root
|| !root
[part
]) {
546 * Register the alias for a class.
548 * @param {Ext.Class/String} cls a reference to a class or a `className`.
549 * @param {String} alias Alias to use when referring to this class.
551 setAlias: function(cls
, alias
) {
552 var aliasToNameMap
= this.maps
.aliasToName
,
553 nameToAliasesMap
= this.maps
.nameToAliases
,
556 if (typeof cls
== 'string') {
559 className
= this.getName(cls
);
562 if (alias
&& aliasToNameMap
[alias
] !== className
) {
564 if (aliasToNameMap
[alias
]) {
565 Ext
.Logger
.info("[Ext.ClassManager] Overriding existing alias: '" + alias
+ "' " +
566 "of: '" + aliasToNameMap
[alias
] + "' with: '" + className
+ "'. Be sure it's intentional.");
570 aliasToNameMap
[alias
] = className
;
573 if (!nameToAliasesMap
[className
]) {
574 nameToAliasesMap
[className
] = [];
578 Ext
.Array
.include(nameToAliasesMap
[className
], alias
);
585 * Adds a batch of class name to alias mappings
586 * @param {Object} aliases The set of mappings of the form
587 * className : [values...]
589 addNameAliasMappings: function(aliases
){
590 var aliasToNameMap
= this.maps
.aliasToName
,
591 nameToAliasesMap
= this.maps
.nameToAliases
,
592 className
, aliasList
, alias
, i
;
594 for (className
in aliases
) {
595 aliasList
= nameToAliasesMap
[className
] ||
596 (nameToAliasesMap
[className
] = []);
598 for (i
= 0; i
< aliases
[className
].length
; i
++) {
599 alias
= aliases
[className
][i
];
600 if (!aliasToNameMap
[alias
]) {
601 aliasToNameMap
[alias
] = className
;
602 aliasList
.push(alias
);
612 * @param {Object} alternates The set of mappings of the form
613 * className : [values...]
615 addNameAlternateMappings: function(alternates
) {
616 var alternateToName
= this.maps
.alternateToName
,
617 nameToAlternates
= this.maps
.nameToAlternates
,
618 className
, aliasList
, alternate
, i
;
620 for (className
in alternates
) {
621 aliasList
= nameToAlternates
[className
] ||
622 (nameToAlternates
[className
] = []);
624 for (i
= 0; i
< alternates
[className
].length
; i
++) {
625 alternate
= alternates
[className
];
626 if (!alternateToName
[alternate
]) {
627 alternateToName
[alternate
] = className
;
628 aliasList
.push(alternate
);
637 * Get a reference to the class by its alias.
639 * @param {String} alias
640 * @return {Ext.Class} class
642 getByAlias: function(alias
) {
643 return this.get(this.getNameByAlias(alias
));
647 * Get the name of a class by its alias.
649 * @param {String} alias
650 * @return {String} className
652 getNameByAlias: function(alias
) {
653 return this.maps
.aliasToName
[alias
] || '';
657 * Get the name of a class by its alternate name.
659 * @param {String} alternate
660 * @return {String} className
662 getNameByAlternate: function(alternate
) {
663 return this.maps
.alternateToName
[alternate
] || '';
667 * Get the aliases of a class by the class name
669 * @param {String} name
670 * @return {Array} aliases
672 getAliasesByName: function(name
) {
673 return this.maps
.nameToAliases
[name
] || [];
677 * Get the name of the class by its reference or its instance;
678 * usually invoked by the shorthand {@link Ext#getClassName Ext.getClassName}
680 * Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action"
682 * @param {Ext.Class/Object} object
683 * @return {String} className
685 getName: function(object
) {
686 return object
&& object
.$className
|| '';
690 * Get the class of the provided object; returns null if it's not an instance
691 * of any class created with Ext.define. This is usually invoked by the shorthand {@link Ext#getClass Ext.getClass}.
693 * var component = new Ext.Component();
695 * Ext.ClassManager.getClass(component); // returns Ext.Component
697 * @param {Object} object
698 * @return {Ext.Class} class
700 getClass: function(object
) {
701 return object
&& object
.self
|| null;
707 create: function(className
, data
, createdFn
) {
709 if (typeof className
!= 'string') {
710 throw new Error("[Ext.define] Invalid class name '" + className
+ "' specified, must be a non-empty string");
714 data
.$className
= className
;
716 return new Class(data
, function() {
717 var postprocessorStack
= data
.postprocessors
|| Manager
.defaultPostprocessors
,
718 registeredPostprocessors
= Manager
.postprocessors
,
721 postprocessor
, process
, i
, ln
, j
, subLn
, postprocessorProperties
, postprocessorProperty
;
723 delete data
.postprocessors
;
725 for (i
= 0,ln
= postprocessorStack
.length
; i
< ln
; i
++) {
726 postprocessor
= postprocessorStack
[i
];
728 if (typeof postprocessor
== 'string') {
729 postprocessor
= registeredPostprocessors
[postprocessor
];
730 postprocessorProperties
= postprocessor
.properties
;
732 if (postprocessorProperties
=== true) {
733 postprocessors
.push(postprocessor
.fn
);
735 else if (postprocessorProperties
) {
736 for (j
= 0,subLn
= postprocessorProperties
.length
; j
< subLn
; j
++) {
737 postprocessorProperty
= postprocessorProperties
[j
];
739 if (data
.hasOwnProperty(postprocessorProperty
)) {
740 postprocessors
.push(postprocessor
.fn
);
747 postprocessors
.push(postprocessor
);
751 process = function(clsName
, cls
, clsData
) {
752 postprocessor
= postprocessors
[index
++];
754 if (!postprocessor
) {
755 Manager
.set(className
, cls
);
758 createdFn
.call(cls
, cls
);
761 Manager
.triggerCreated(className
);
765 if (postprocessor
.call(this, clsName
, cls
, clsData
, process
) !== false) {
766 process
.apply(this, arguments
);
770 process
.call(Manager
, className
, this, data
);
774 createOverride: function(className
, data
, createdFn
) {
775 var overriddenClassName
= data
.override
,
776 requires
= Ext
.Array
.from(data
.requires
);
778 delete data
.override
;
779 delete data
.requires
;
781 this.existCache
[className
] = true;
783 Ext
.require(requires
, function() {
784 // Override the target class right after it's created
785 this.onCreated(function() {
786 var overridenClass
= this.get(overriddenClassName
);
787 if (overridenClass
.singleton
) {
788 overridenClass
.self
.override(data
);
791 overridenClass
.override(data
);
795 createdFn
.call(overridenClass
, overridenClass
);
798 // This push the overridding file itself into Ext.Loader.history
799 // Hence if the target class never exists, the overriding file will
800 // never be included in the build
801 this.triggerCreated(className
);
802 }, this, overriddenClassName
);
809 * Instantiate a class by its alias; usually invoked by the convenient shorthand {@link Ext#createByAlias Ext.createByAlias}
810 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
811 * attempt to load the class via synchronous loading.
813 * var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800 });
815 * @param {String} alias
816 * @param {Mixed...} args Additional arguments after the alias will be passed to the class constructor.
817 * @return {Object} instance
819 instantiateByAlias: function() {
820 var alias
= arguments
[0],
821 args
= arraySlice
.call(arguments
),
822 className
= this.getNameByAlias(alias
);
825 className
= this.maps
.aliasToName
[alias
];
829 throw new Error("[Ext.createByAlias] Cannot create an instance of unrecognized alias: " + alias
);
834 Ext
.Logger
.warn("[Ext.Loader] Synchronously loading '" + className
+ "'; consider adding " +
835 "Ext.require('" + alias
+ "') above Ext.onReady");
838 Ext
.syncRequire(className
);
843 return this.instantiate
.apply(this, args
);
847 * Instantiate a class by either full name, alias or alternate name; usually invoked by the convenient
848 * shorthand {@link Ext.ClassManager#create Ext.create}.
850 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
851 * attempt to load the class via synchronous loading.
853 * For example, all these three lines return the same result:
856 * var formPanel = Ext.create('widget.formpanel', { width: 600, height: 800 });
859 * var formPanel = Ext.create('Ext.form.FormPanel', { width: 600, height: 800 });
862 * var formPanel = Ext.create('Ext.form.Panel', { width: 600, height: 800 });
864 * @param {String} name
865 * @param {Mixed} args Additional arguments after the name will be passed to the class' constructor.
866 * @return {Object} instance
868 instantiate: function() {
869 var name
= arguments
[0],
870 args
= arraySlice
.call(arguments
, 1),
874 if (typeof name
!= 'function') {
876 if ((typeof name
!= 'string' || name
.length
< 1)) {
877 throw new Error("[Ext.create] Invalid class name or alias '" + name
+ "' specified, must be a non-empty string");
881 cls
= this.get(name
);
887 // No record of this class name, it's possibly an alias, so look it up
889 possibleName
= this.getNameByAlias(name
);
894 cls
= this.get(name
);
898 // Still no record of this class name, it's possibly an alternate name, so look it up
900 possibleName
= this.getNameByAlternate(name
);
905 cls
= this.get(name
);
909 // Still not existing at this point, try to load it via synchronous mode as the last resort
915 Ext
.Logger
.warn("[Ext.Loader] Synchronously loading '" + name
+ "'; consider adding '" +
916 ((possibleName
) ? alias
: name
) + "' explicitly as a require of the corresponding class");
919 Ext
.syncRequire(name
);
921 cls
= this.get(name
);
926 throw new Error("[Ext.create] Cannot create an instance of unrecognized class name / alias: " + alias
);
929 if (typeof cls
!= 'function') {
930 throw new Error("[Ext.create] '" + name
+ "' is a singleton and cannot be instantiated");
934 return this.getInstantiator(args
.length
)(cls
, args
);
939 * @param {String} name
940 * @param {Array} args
942 dynInstantiate: function(name
, args
) {
943 args
= arrayFrom(args
, true);
946 return this.instantiate
.apply(this, args
);
951 * @param {Number} length
953 getInstantiator: function(length
) {
954 var instantiators
= this.instantiators
,
957 instantiator
= instantiators
[length
];
963 for (i
= 0; i
< length
; i
++) {
964 args
.push('a[' + i
+ ']');
967 instantiator
= instantiators
[length
] = new Function('c', 'a', 'return new c(' + args
.join(',') + ')');
969 instantiator
.displayName
= "Ext.ClassManager.instantiate" + length
;
984 defaultPostprocessors
: [],
987 * Register a post-processor function.
990 registerPostprocessor: function(name
, fn
, properties
, position
, relativeTo
) {
999 this.postprocessors
[name
] = {
1001 properties
: properties
|| false,
1005 this.setDefaultPostprocessorPosition(name
, position
, relativeTo
);
1011 * Set the default post processors array stack which are applied to every class.
1014 * @param {String/Array} postprocessors The name of a registered post processor or an array of registered names.
1015 * @return {Ext.ClassManager} this
1017 setDefaultPostprocessors: function(postprocessors
) {
1018 this.defaultPostprocessors
= arrayFrom(postprocessors
);
1024 * Insert this post-processor at a specific position in the stack, optionally relative to
1025 * any existing post-processor
1028 * @param {String} name The post-processor name. Note that it needs to be registered with
1029 * {@link Ext.ClassManager#registerPostprocessor} before this
1030 * @param {String} offset The insertion position. Four possible values are:
1031 * 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument)
1032 * @param {String} relativeName
1033 * @return {Ext.ClassManager} this
1035 setDefaultPostprocessorPosition: function(name
, offset
, relativeName
) {
1036 var defaultPostprocessors
= this.defaultPostprocessors
,
1039 if (typeof offset
== 'string') {
1040 if (offset
=== 'first') {
1041 defaultPostprocessors
.unshift(name
);
1045 else if (offset
=== 'last') {
1046 defaultPostprocessors
.push(name
);
1051 offset
= (offset
=== 'after') ? 1 : -1;
1054 index
= Ext
.Array
.indexOf(defaultPostprocessors
, relativeName
);
1057 Ext
.Array
.splice(defaultPostprocessors
, Math
.max(0, index
+ offset
), 0, name
);
1064 * Converts a string expression to an array of matching class names. An expression can either refers to class aliases
1065 * or class names. Expressions support wildcards:
1067 * // returns ['Ext.window.Window']
1068 * var window = Ext.ClassManager.getNamesByExpression('widget.window');
1070 * // returns ['widget.panel', 'widget.window', ...]
1071 * var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*');
1073 * // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...]
1074 * var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*');
1076 * @param {String} expression
1077 * @return {Array} classNames
1079 getNamesByExpression: function(expression
) {
1080 var nameToAliasesMap
= this.maps
.nameToAliases
,
1082 name
, alias
, aliases
, possibleName
, regex
, i
, ln
;
1085 if (typeof expression
!= 'string' || expression
.length
< 1) {
1086 throw new Error("[Ext.ClassManager.getNamesByExpression] Expression " + expression
+ " is invalid, must be a non-empty string");
1090 if (expression
.indexOf('*') !== -1) {
1091 expression
= expression
.replace(/\*/g, '(.*?)');
1092 regex
= new RegExp('^' + expression
+ '$');
1094 for (name
in nameToAliasesMap
) {
1095 if (nameToAliasesMap
.hasOwnProperty(name
)) {
1096 aliases
= nameToAliasesMap
[name
];
1098 if (name
.search(regex
) !== -1) {
1102 for (i
= 0, ln
= aliases
.length
; i
< ln
; i
++) {
1105 if (alias
.search(regex
) !== -1) {
1115 possibleName
= this.getNameByAlias(expression
);
1118 names
.push(possibleName
);
1120 possibleName
= this.getNameByAlternate(expression
);
1123 names
.push(possibleName
);
1125 names
.push(expression
);
1134 //<feature classSystem.alias>
1136 * @cfg {String[]} alias
1138 * List of short aliases for class names. Most useful for defining xtypes for widgets:
1140 * Ext.define('MyApp.CoolPanel', {
1141 * extend: 'Ext.panel.Panel',
1142 * alias: ['widget.coolpanel'],
1149 * // Using Ext.create
1150 * Ext.create('widget.coolpanel');
1152 * // Using the shorthand for widgets and in xtypes
1153 * Ext.widget('panel', {
1155 * {xtype: 'coolpanel', html: 'Foo'},
1156 * {xtype: 'coolpanel', html: 'Bar'}
1160 * For {@link Ext.Component}, you can also use the {@link Ext.Component#xtype} property.
1163 * @cfg {String[]} xtype
1164 * @member Ext.Component
1165 * List of xtypes for {@link Ext.Component}. XTypes must not contain periods.
1167 * Ext.define('MyApp.CoolPanel', {
1168 * extend: 'Ext.panel.Panel',
1169 * xtype: 'coolpanel',
1176 * // Using Ext.create
1177 * Ext.create('widget.coolpanel');
1179 * // Using the shorthand for widgets and in xtypes
1180 * Ext.widget('panel', {
1182 * {xtype: 'coolpanel', html: 'Foo'},
1183 * {xtype: 'coolpanel', html: 'Bar'}
1187 Manager
.registerPostprocessor('alias', function(name
, cls
, data
) {
1188 var aliases
= data
.alias
,
1191 for (i
= 0,ln
= aliases
.length
; i
< ln
; i
++) {
1194 this.setAlias(cls
, alias
);
1197 }, ['xtype', 'alias']);
1200 //<feature classSystem.singleton>
1202 * @cfg {Boolean} singleton
1204 * When set to true, the class will be instantiated as singleton. For example:
1206 * Ext.define('Logger', {
1208 * log: function(msg) {
1213 * Logger.log('Hello');
1215 Manager
.registerPostprocessor('singleton', function(name
, cls
, data
, fn
) {
1216 fn
.call(this, name
, new cls(), data
);
1221 //<feature classSystem.alternateClassName>
1223 * @cfg {String/String[]} alternateClassName
1225 * Defines alternate names for this class. For example:
1228 * Ext.define('Developer', {
1229 * alternateClassName: ['Coder', 'Hacker'],
1230 * code: function(msg) {
1231 * alert('Typing... ' + msg);
1235 * var joe = Ext.create('Developer');
1236 * joe.code('stackoverflow');
1238 * var rms = Ext.create('Hacker');
1239 * rms.code('hack hack');
1241 Manager
.registerPostprocessor('alternateClassName', function(name
, cls
, data
) {
1242 var alternates
= data
.alternateClassName
,
1245 if (!(alternates
instanceof Array
)) {
1246 alternates
= [alternates
];
1249 for (i
= 0, ln
= alternates
.length
; i
< ln
; i
++) {
1250 alternate
= alternates
[i
];
1253 if (typeof alternate
!= 'string') {
1254 throw new Error("[Ext.define] Invalid alternate of: '" + alternate
+ "' for class: '" + name
+ "'; must be a valid string");
1258 this.set(alternate
, cls
);
1265 * Instantiate a class by either full name, alias or alternate name.
1267 * If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has not been defined yet, it will
1268 * attempt to load the class via synchronous loading.
1270 * For example, all these three lines return the same result:
1273 * var formPanel = Ext.create('widget.formpanel', { width: 600, height: 800 });
1276 * var formPanel = Ext.create('Ext.form.FormPanel', { width: 600, height: 800 });
1278 * // full class name
1279 * var formPanel = Ext.create('Ext.form.Panel', { width: 600, height: 800 });
1281 * @param {String} name
1282 * @param {Mixed} args Additional arguments after the name will be passed to the class' constructor.
1283 * @return {Object} instance
1286 create
: alias(Manager
, 'instantiate'),
1289 * Convenient shorthand to create a widget by its xtype, also see {@link Ext.ClassManager#instantiateByAlias}
1291 * var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button')
1292 * var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel')
1296 * @param {String} name
1297 * @return {Object} instance
1299 widget: function(name
) {
1300 var args
= arraySlice
.call(arguments
);
1301 args
[0] = 'widget.' + name
;
1303 return Manager
.instantiateByAlias
.apply(Manager
, args
);
1307 * Convenient shorthand, see {@link Ext.ClassManager#instantiateByAlias}.
1309 * @method createByAlias
1310 * @param {String} alias
1311 * @param {Mixed...} args Additional arguments after the alias will be passed to the class constructor.
1312 * @return {Object} instance
1314 createByAlias
: alias(Manager
, 'instantiateByAlias'),
1317 * Defines a class or override. A basic class is defined like this:
1319 * Ext.define('My.awesome.Class', {
1320 * someProperty: 'something',
1322 * someMethod: function(s) {
1323 * console.log(s + this.someProperty);
1327 * var obj = new My.awesome.Class();
1329 * obj.someMethod('Say '); // logs 'Say something' to the console
1331 * To defines an override, include the `override` property. The content of an
1332 * override is aggregated with the specified class in order to extend or modify
1333 * that class. This can be as simple as setting default property values or it can
1334 * extend and/or replace methods. This can also extend the statics of the class.
1336 * One use for an override is to break a large class into manageable pieces.
1338 * // File: /src/app/Panel.js
1339 * Ext.define('My.app.Panel', {
1340 * extend: 'Ext.panel.Panel',
1342 * 'My.app.PanelPart2',
1343 * 'My.app.PanelPart3'
1346 * constructor: function (config) {
1347 * this.callParent(arguments); // calls Ext.panel.Panel's constructor
1352 * method: function () {
1358 * // File: /src/app/PanelPart2.js
1359 * Ext.define('My.app.PanelPart2', {
1360 * override: 'My.app.Panel',
1362 * constructor: function (config) {
1363 * this.callParent(arguments); // calls My.app.Panel's constructor
1368 * Another use for an override is to provide optional parts of classes that can be
1369 * independently required. In this case, the class may even be unaware of the
1370 * override altogether.
1372 * Ext.define('My.ux.CoolTip', {
1373 * override: 'Ext.tip.ToolTip',
1375 * constructor: function (config) {
1376 * this.callParent(arguments); // calls Ext.tip.ToolTip's constructor
1381 * The above override can now be required as normal.
1383 * Ext.define('My.app.App', {
1389 * Overrides can also contain statics:
1391 * Ext.define('My.app.BarMod', {
1392 * override: 'Ext.foo.Bar',
1395 * method: function (x) {
1396 * return this.callParent([x * 2]); // call Ext.foo.Bar.method
1401 * __IMPORTANT:__ An override is only included in a build if the class it overrides is
1402 * required. Otherwise, the override, like the target class, is not included.
1404 * @param {String} className The class name to create in string dot-namespaced format, for example:
1405 * 'My.very.awesome.Class', 'FeedViewer.plugin.CoolPager'
1407 * It is highly recommended to follow this simple convention:
1408 * - The root and the class name are 'CamelCased'
1409 * - Everything else is lower-cased
1411 * @param {Object} data The key - value pairs of properties to apply to this class. Property names can be of
1412 * any valid strings, except those in the reserved listed below:
1418 * - `xtype` (for {@link Ext.Component}s only)
1421 * - `alternateClassName`
1424 * @param {Function} [createdFn] Optional callback to execute after the class (or override)
1425 * is created. The execution scope (`this`) will be the newly created class itself.
1426 * @return {Ext.Base}
1431 define: function (className
, data
, createdFn
) {
1432 if ('override' in data
) {
1433 return Manager
.createOverride
.apply(Manager
, arguments
);
1436 return Manager
.create
.apply(Manager
, arguments
);
1440 * Convenient shorthand for {@link Ext.ClassManager#getName}.
1442 * @method getClassName
1443 * @inheritdoc Ext.ClassManager#getName
1445 getClassName
: alias(Manager
, 'getName'),
1448 * Returns the display name for object. This name is looked for in order from the following places:
1450 * - `displayName` field of the object.
1451 * - `$name` and `$class` fields of the object.
1452 * - '$className` field of the object.
1454 * This method is used by {@link Ext.Logger#log} to display information about objects.
1456 * @param {Mixed} [object] The object who's display name to determine.
1457 * @return {String} The determined display name, or "Anonymous" if none found.
1460 getDisplayName: function(object
) {
1462 if (object
.displayName
) {
1463 return object
.displayName
;
1466 if (object
.$name
&& object
.$class) {
1467 return Ext
.getClassName(object
.$class) + '#' + object
.$name
;
1470 if (object
.$className
) {
1471 return object
.$className
;
1479 * Convenient shorthand, see {@link Ext.ClassManager#getClass}.
1483 getClass
: alias(Manager
, 'getClass'),
1486 * Creates namespaces to be used for scoping variables and classes so that they are not global.
1487 * Specifying the last node of a namespace implicitly creates all other nodes. Usage:
1489 * Ext.namespace('Company', 'Company.data');
1491 * // equivalent and preferable to the above syntax
1492 * Ext.namespace('Company.data');
1494 * Company.Widget = function() {
1498 * Company.data.CustomStore = function(config) {
1502 * @param {String} namespace1
1503 * @param {String} namespace2
1504 * @param {String} etc
1505 * @return {Object} The namespace object. If multiple arguments are passed, this will be the last namespace created.
1509 namespace: alias(Manager
, 'createNamespaces')
1513 * Old name for {@link Ext#widget}.
1514 * @deprecated 2.0.0 Please use {@link Ext#widget} instead.
1515 * @method createWidget
1518 Ext
.createWidget
= Ext
.widget
;
1521 * Convenient alias for {@link Ext#namespace Ext.namespace}.
1525 Ext
.ns
= Ext
.namespace;
1527 Class
.registerPreprocessor('className', function(cls
, data
) {
1528 if (data
.$className
) {
1529 cls
.$className
= data
.$className
;
1531 cls
.displayName
= cls
.$className
;
1536 Class
.registerPreprocessor('alias', function(cls
, data
) {
1537 var prototype = cls
.prototype,
1538 xtypes
= arrayFrom(data
.xtype
),
1539 aliases
= arrayFrom(data
.alias
),
1540 widgetPrefix
= 'widget.',
1541 widgetPrefixLength
= widgetPrefix
.length
,
1542 xtypesChain
= Array
.prototype.slice
.call(prototype.xtypesChain
|| []),
1543 xtypesMap
= Ext
.merge({}, prototype.xtypesMap
|| {}),
1544 i
, ln
, alias
, xtype
;
1546 for (i
= 0,ln
= aliases
.length
; i
< ln
; i
++) {
1550 if (typeof alias
!= 'string' || alias
.length
< 1) {
1551 throw new Error("[Ext.define] Invalid alias of: '" + alias
+ "' for class: '" + name
+ "'; must be a valid string");
1555 if (alias
.substring(0, widgetPrefixLength
) === widgetPrefix
) {
1556 xtype
= alias
.substring(widgetPrefixLength
);
1557 Ext
.Array
.include(xtypes
, xtype
);
1561 cls
.xtype
= data
.xtype
= xtypes
[0];
1562 data
.xtypes
= xtypes
;
1564 for (i
= 0,ln
= xtypes
.length
; i
< ln
; i
++) {
1567 if (!xtypesMap
[xtype
]) {
1568 xtypesMap
[xtype
] = true;
1569 xtypesChain
.push(xtype
);
1573 data
.xtypesChain
= xtypesChain
;
1574 data
.xtypesMap
= xtypesMap
;
1576 Ext
.Function
.interceptAfter(data
, 'onClassCreated', function() {
1577 var mixins
= prototype.mixins
,
1580 for (key
in mixins
) {
1581 if (mixins
.hasOwnProperty(key
)) {
1582 mixin
= mixins
[key
];
1584 xtypes
= mixin
.xtypes
;
1587 for (i
= 0,ln
= xtypes
.length
; i
< ln
; i
++) {
1590 if (!xtypesMap
[xtype
]) {
1591 xtypesMap
[xtype
] = true;
1592 xtypesChain
.push(xtype
);
1600 for (i
= 0,ln
= xtypes
.length
; i
< ln
; i
++) {
1604 if (typeof xtype
!= 'string' || xtype
.length
< 1) {
1605 throw new Error("[Ext.define] Invalid xtype of: '" + xtype
+ "' for class: '" + name
+ "'; must be a valid non-empty string");
1609 Ext
.Array
.include(aliases
, widgetPrefix
+ xtype
);
1612 data
.alias
= aliases
;
1614 }, ['xtype', 'alias']);
1616 })(Ext
.Class
, Ext
.Function
.alias
, Array
.prototype.slice
, Ext
.Array
.from, Ext
.global
);