macoslinuxwindowsinboxwhatsappicloudtweetdeckhipchattelegramhangoutsslackgmailskypefacebook-workplaceoutlookemailmicrosoft-teamsdiscordmessengercustom-services
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2028 lines
67 KiB
2028 lines
67 KiB
// @tag class |
|
/** |
|
* @class Ext.ClassManager |
|
* |
|
* Ext.ClassManager manages all classes and handles mapping from string class name to |
|
* actual class objects throughout the whole framework. It is not generally accessed directly, rather through |
|
* these convenient shorthands: |
|
* |
|
* - {@link Ext#define Ext.define} |
|
* - {@link Ext#create Ext.create} |
|
* - {@link Ext#widget Ext.widget} |
|
* - {@link Ext#getClass Ext.getClass} |
|
* - {@link Ext#getClassName Ext.getClassName} |
|
* |
|
* # Basic syntax: |
|
* |
|
* Ext.define(className, properties); |
|
* |
|
* in which `properties` is an object represent a collection of properties that apply to the class. See |
|
* {@link Ext.ClassManager#create} for more detailed instructions. |
|
* |
|
* Ext.define('Person', { |
|
* name: 'Unknown', |
|
* |
|
* constructor: function(name) { |
|
* if (name) { |
|
* this.name = name; |
|
* } |
|
* }, |
|
* |
|
* eat: function(foodType) { |
|
* alert("I'm eating: " + foodType); |
|
* |
|
* return this; |
|
* } |
|
* }); |
|
* |
|
* var aaron = new Person("Aaron"); |
|
* aaron.eat("Sandwich"); // alert("I'm eating: Sandwich"); |
|
* |
|
* Ext.Class has a powerful set of extensible {@link Ext.Class#registerPreprocessor pre-processors} which takes care of |
|
* everything related to class creation, including but not limited to inheritance, mixins, configuration, statics, etc. |
|
* |
|
* # Inheritance: |
|
* |
|
* Ext.define('Developer', { |
|
* extend: 'Person', |
|
* |
|
* constructor: function(name, isGeek) { |
|
* this.isGeek = isGeek; |
|
* |
|
* // Apply a method from the parent class' prototype |
|
* this.callParent([name]); |
|
* }, |
|
* |
|
* code: function(language) { |
|
* alert("I'm coding in: " + language); |
|
* |
|
* this.eat("Bugs"); |
|
* |
|
* return this; |
|
* } |
|
* }); |
|
* |
|
* var jacky = new Developer("Jacky", true); |
|
* jacky.code("JavaScript"); // alert("I'm coding in: JavaScript"); |
|
* // alert("I'm eating: Bugs"); |
|
* |
|
* See {@link Ext.Base#callParent} for more details on calling superclass' methods |
|
* |
|
* # Mixins: |
|
* |
|
* Ext.define('CanPlayGuitar', { |
|
* playGuitar: function() { |
|
* alert("F#...G...D...A"); |
|
* } |
|
* }); |
|
* |
|
* Ext.define('CanComposeSongs', { |
|
* composeSongs: function() { ... } |
|
* }); |
|
* |
|
* Ext.define('CanSing', { |
|
* sing: function() { |
|
* alert("For he's a jolly good fellow...") |
|
* } |
|
* }); |
|
* |
|
* Ext.define('Musician', { |
|
* extend: 'Person', |
|
* |
|
* mixins: { |
|
* canPlayGuitar: 'CanPlayGuitar', |
|
* canComposeSongs: 'CanComposeSongs', |
|
* canSing: 'CanSing' |
|
* } |
|
* }) |
|
* |
|
* Ext.define('CoolPerson', { |
|
* extend: 'Person', |
|
* |
|
* mixins: { |
|
* canPlayGuitar: 'CanPlayGuitar', |
|
* canSing: 'CanSing' |
|
* }, |
|
* |
|
* sing: function() { |
|
* alert("Ahem...."); |
|
* |
|
* this.mixins.canSing.sing.call(this); |
|
* |
|
* alert("[Playing guitar at the same time...]"); |
|
* |
|
* this.playGuitar(); |
|
* } |
|
* }); |
|
* |
|
* var me = new CoolPerson("Jacky"); |
|
* |
|
* me.sing(); // alert("Ahem..."); |
|
* // alert("For he's a jolly good fellow..."); |
|
* // alert("[Playing guitar at the same time...]"); |
|
* // alert("F#...G...D...A"); |
|
* |
|
* # Config: |
|
* |
|
* Ext.define('SmartPhone', { |
|
* config: { |
|
* hasTouchScreen: false, |
|
* operatingSystem: 'Other', |
|
* price: 500 |
|
* }, |
|
* |
|
* isExpensive: false, |
|
* |
|
* constructor: function(config) { |
|
* this.initConfig(config); |
|
* }, |
|
* |
|
* applyPrice: function(price) { |
|
* this.isExpensive = (price > 500); |
|
* |
|
* return price; |
|
* }, |
|
* |
|
* applyOperatingSystem: function(operatingSystem) { |
|
* if (!(/^(iOS|Android|BlackBerry)$/i).test(operatingSystem)) { |
|
* return 'Other'; |
|
* } |
|
* |
|
* return operatingSystem; |
|
* } |
|
* }); |
|
* |
|
* var iPhone = new SmartPhone({ |
|
* hasTouchScreen: true, |
|
* operatingSystem: 'iOS' |
|
* }); |
|
* |
|
* iPhone.getPrice(); // 500; |
|
* iPhone.getOperatingSystem(); // 'iOS' |
|
* iPhone.getHasTouchScreen(); // true; |
|
* |
|
* iPhone.isExpensive; // false; |
|
* iPhone.setPrice(600); |
|
* iPhone.getPrice(); // 600 |
|
* iPhone.isExpensive; // true; |
|
* |
|
* iPhone.setOperatingSystem('AlienOS'); |
|
* iPhone.getOperatingSystem(); // 'Other' |
|
* |
|
* # Statics: |
|
* |
|
* Ext.define('Computer', { |
|
* statics: { |
|
* factory: function(brand) { |
|
* // 'this' in static methods refer to the class itself |
|
* return new this(brand); |
|
* } |
|
* }, |
|
* |
|
* constructor: function() { ... } |
|
* }); |
|
* |
|
* var dellComputer = Computer.factory('Dell'); |
|
* |
|
* Also see {@link Ext.Base#statics} and {@link Ext.Base#self} for more details on accessing |
|
* static properties within class methods |
|
* |
|
* @singleton |
|
*/ |
|
Ext.ClassManager = (function(Class, alias, arraySlice, arrayFrom, global) { |
|
// @define Ext.ClassManager |
|
// @require Ext.Inventory |
|
// @require Ext.Class |
|
// @require Ext.Function |
|
// @require Ext.Array |
|
|
|
var makeCtor = Ext.Class.makeCtor, |
|
//<if nonBrowser> |
|
isNonBrowser = typeof window === 'undefined', |
|
//</if> |
|
|
|
Manager = Ext.apply(new Ext.Inventory(), { |
|
/** |
|
* @property {Object} classes |
|
* All classes which were defined through the ClassManager. Keys are the |
|
* name of the classes and the values are references to the classes. |
|
* @private |
|
*/ |
|
classes: {}, |
|
|
|
classState: { |
|
/* |
|
* 'Ext.foo.Bar': <state enum> |
|
* |
|
* 10 = Ext.define called |
|
* 20 = Ext.define/override called |
|
* 30 = Manager.existCache[<name>] == true for define |
|
* 40 = Manager.existCache[<name>] == true for define/override |
|
* 50 = Manager.isCreated(<name>) == true for define |
|
* 60 = Manager.isCreated(<name>) == true for define/override |
|
* |
|
*/ |
|
}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
existCache: {}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
namespaceRewrites: [{ |
|
from: 'Ext.', |
|
to: Ext |
|
}], |
|
|
|
/** @private */ |
|
enableNamespaceParseCache: true, |
|
|
|
/** @private */ |
|
namespaceParseCache: {}, |
|
|
|
/** @private */ |
|
instantiators: [], |
|
|
|
/** |
|
* Checks if a class has already been created. |
|
* |
|
* @param {String} className |
|
* @return {Boolean} exist |
|
*/ |
|
isCreated: function(className) { |
|
var i, ln, part, root, parts; |
|
|
|
//<debug> |
|
if (typeof className !== 'string' || className.length < 1) { |
|
throw new Error("[Ext.ClassManager] Invalid classname, must be a string and must not be empty"); |
|
} |
|
//</debug> |
|
|
|
if (Manager.classes[className] || Manager.existCache[className]) { |
|
return true; |
|
} |
|
|
|
root = global; |
|
parts = Manager.parseNamespace(className); |
|
|
|
for (i = 0, ln = parts.length; i < ln; i++) { |
|
part = parts[i]; |
|
|
|
if (typeof part !== 'string') { |
|
root = part; |
|
} else { |
|
if (!root || !root[part]) { |
|
return false; |
|
} |
|
|
|
root = root[part]; |
|
} |
|
} |
|
|
|
Manager.triggerCreated(className); |
|
|
|
return true; |
|
}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
createdListeners: [], |
|
|
|
/** |
|
* @private |
|
*/ |
|
nameCreatedListeners: {}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
existsListeners: [], |
|
|
|
/** |
|
* @private |
|
*/ |
|
nameExistsListeners: {}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
overrideMap: {}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
triggerCreated: function (className, state) { |
|
Manager.existCache[className] = state || 1; |
|
Manager.classState[className] += 40; |
|
Manager.notify(className, Manager.createdListeners, Manager.nameCreatedListeners); |
|
}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
onCreated: function(fn, scope, className) { |
|
Manager.addListener(fn, scope, className, Manager.createdListeners, Manager.nameCreatedListeners); |
|
}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
notify: function (className, listeners, nameListeners) { |
|
var alternateNames = Manager.getAlternatesByName(className), |
|
names = [className], |
|
i, ln, j, subLn, listener, name; |
|
|
|
for (i = 0,ln = listeners.length; i < ln; i++) { |
|
listener = listeners[i]; |
|
listener.fn.call(listener.scope, className); |
|
} |
|
|
|
while (names) { |
|
for (i = 0,ln = names.length; i < ln; i++) { |
|
name = names[i]; |
|
listeners = nameListeners[name]; |
|
|
|
if (listeners) { |
|
for (j = 0,subLn = listeners.length; j < subLn; j++) { |
|
listener = listeners[j]; |
|
listener.fn.call(listener.scope, name); |
|
} |
|
delete nameListeners[name]; |
|
} |
|
} |
|
|
|
names = alternateNames; // for 2nd pass (if needed) |
|
alternateNames = null; // no 3rd pass |
|
} |
|
}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
addListener: function(fn, scope, className, listeners, nameListeners) { |
|
if (Ext.isArray(className)) { |
|
fn = Ext.Function.createBarrier(className.length, fn, scope); |
|
for (i = 0; i < className.length; i++) { |
|
this.addListener(fn, null, className[i], listeners, nameListeners); |
|
} |
|
return; |
|
} |
|
var i, |
|
listener = { |
|
fn: fn, |
|
scope: scope |
|
}; |
|
|
|
if (className) { |
|
if (this.isCreated(className)) { |
|
fn.call(scope, className); |
|
return; |
|
} |
|
|
|
if (!nameListeners[className]) { |
|
nameListeners[className] = []; |
|
} |
|
|
|
nameListeners[className].push(listener); |
|
} |
|
else { |
|
listeners.push(listener); |
|
} |
|
}, |
|
|
|
/** |
|
* Supports namespace rewriting. |
|
* @private |
|
*/ |
|
parseNamespace: function(namespace) { |
|
//<debug> |
|
if (typeof namespace !== 'string') { |
|
throw new Error("[Ext.ClassManager] Invalid namespace, must be a string"); |
|
} |
|
//</debug> |
|
|
|
var cache = this.namespaceParseCache, |
|
parts, |
|
rewrites, |
|
root, |
|
name, |
|
rewrite, from, to, i, ln; |
|
|
|
if (this.enableNamespaceParseCache) { |
|
if (cache.hasOwnProperty(namespace)) { |
|
return cache[namespace]; |
|
} |
|
} |
|
|
|
parts = []; |
|
rewrites = this.namespaceRewrites; |
|
root = global; |
|
name = namespace; |
|
|
|
for (i = 0, ln = rewrites.length; i < ln; i++) { |
|
rewrite = rewrites[i]; |
|
from = rewrite.from; |
|
to = rewrite.to; |
|
|
|
if (name === from || name.substring(0, from.length) === from) { |
|
name = name.substring(from.length); |
|
|
|
if (typeof to !== 'string') { |
|
root = to; |
|
} else { |
|
parts = parts.concat(to.split('.')); |
|
} |
|
|
|
break; |
|
} |
|
} |
|
|
|
parts.push(root); |
|
|
|
parts = parts.concat(name.split('.')); |
|
|
|
if (this.enableNamespaceParseCache) { |
|
cache[namespace] = parts; |
|
} |
|
|
|
return parts; |
|
}, |
|
|
|
/** |
|
* Creates a namespace and assign the `value` to the created object. |
|
* |
|
* Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject); |
|
* |
|
* alert(MyCompany.pkg.Example === someObject); // alerts true |
|
* |
|
* @param {String} name |
|
* @param {Object} value |
|
*/ |
|
setNamespace: function(name, value) { |
|
var root = global, |
|
parts = this.parseNamespace(name), |
|
ln = parts.length - 1, |
|
leaf = parts[ln], |
|
i, part; |
|
|
|
for (i = 0; i < ln; i++) { |
|
part = parts[i]; |
|
|
|
if (typeof part !== 'string') { |
|
root = part; |
|
} else { |
|
if (!root[part]) { |
|
root[part] = {}; |
|
} |
|
|
|
root = root[part]; |
|
} |
|
} |
|
|
|
root[leaf] = value; |
|
|
|
return root[leaf]; |
|
}, |
|
|
|
/** |
|
* The new Ext.ns, supports namespace rewriting. |
|
* @private |
|
*/ |
|
createNamespaces: function() { |
|
var root = global, |
|
parts, part, i, j, ln, subLn; |
|
|
|
for (i = 0, ln = arguments.length; i < ln; i++) { |
|
parts = this.parseNamespace(arguments[i]); |
|
|
|
for (j = 0, subLn = parts.length; j < subLn; j++) { |
|
part = parts[j]; |
|
|
|
if (typeof part !== 'string') { |
|
root = part; |
|
} else { |
|
if (!root[part]) { |
|
root[part] = {}; |
|
} |
|
|
|
root = root[part]; |
|
} |
|
} |
|
} |
|
|
|
return root; |
|
}, |
|
|
|
/** |
|
* Sets a name reference to a class. |
|
* |
|
* @param {String} name |
|
* @param {Object} value |
|
* @return {Ext.ClassManager} this |
|
*/ |
|
set: function (name, value) { |
|
var me = this, |
|
targetName = me.getName(value); |
|
|
|
me.classes[name] = me.setNamespace(name, value); |
|
|
|
if (targetName && targetName !== name) { |
|
me.addAlternate(targetName, name); |
|
} |
|
|
|
return this; |
|
}, |
|
|
|
/** |
|
* Retrieve a class by its name. |
|
* |
|
* @param {String} name |
|
* @return {Ext.Class} class |
|
*/ |
|
get: function(name) { |
|
var classes = this.classes, |
|
root, |
|
parts, |
|
part, i, ln; |
|
|
|
if (classes[name]) { |
|
return classes[name]; |
|
} |
|
|
|
root = global; |
|
parts = this.parseNamespace(name); |
|
|
|
for (i = 0, ln = parts.length; i < ln; i++) { |
|
part = parts[i]; |
|
|
|
if (typeof part !== 'string') { |
|
root = part; |
|
} else { |
|
if (!root || !root[part]) { |
|
return null; |
|
} |
|
|
|
root = root[part]; |
|
} |
|
} |
|
|
|
return root; |
|
}, |
|
|
|
/** |
|
* Adds a batch of class name to alias mappings. |
|
* @param {Object} aliases The set of mappings of the form. |
|
* className : [values...] |
|
*/ |
|
addNameAliasMappings: function(aliases) { |
|
this.addAlias(aliases); |
|
}, |
|
|
|
/** |
|
* |
|
* @param {Object} alternates The set of mappings of the form |
|
* className : [values...] |
|
*/ |
|
addNameAlternateMappings: function (alternates) { |
|
this.addAlternate(alternates); |
|
}, |
|
|
|
/** |
|
* Get a reference to the class by its alias. |
|
* |
|
* @param {String} alias |
|
* @return {Ext.Class} class |
|
*/ |
|
getByAlias: function(alias) { |
|
return this.get(this.getNameByAlias(alias)); |
|
}, |
|
|
|
/** |
|
* Get the name of the class by its reference or its instance. This is |
|
* usually invoked by the shorthand {@link Ext#getClassName}. |
|
* |
|
* Ext.ClassManager.getName(Ext.Action); // returns "Ext.Action" |
|
* |
|
* @param {Ext.Class/Object} object |
|
* @return {String} className |
|
*/ |
|
getName: function(object) { |
|
return object && object.$className || ''; |
|
}, |
|
|
|
/** |
|
* Get the class of the provided object; returns null if it's not an instance |
|
* of any class created with Ext.define. This is usually invoked by the |
|
* shorthand {@link Ext#getClass}. |
|
* |
|
* var component = new Ext.Component(); |
|
* |
|
* Ext.getClass(component); // returns Ext.Component |
|
* |
|
* @param {Object} object |
|
* @return {Ext.Class} class |
|
*/ |
|
getClass: function(object) { |
|
return object && object.self || null; |
|
}, |
|
|
|
/** |
|
* Defines a class. |
|
* @deprecated Use {@link Ext#define} instead, as that also supports creating overrides. |
|
* @private |
|
*/ |
|
create: function(className, data, createdFn) { |
|
//<debug> |
|
if (className != null && typeof className !== 'string') { |
|
throw new Error("[Ext.define] Invalid class name '" + className + "' specified, must be a non-empty string"); |
|
} |
|
//</debug> |
|
|
|
var ctor = makeCtor(className); |
|
if (typeof data === 'function') { |
|
data = data(ctor); |
|
} |
|
|
|
//<debug> |
|
if (className) { |
|
if(Manager.classes[className]) { |
|
Ext.log.warn("[Ext.define] Duplicate class name '" + className + "' specified, must be a non-empty string"); |
|
} |
|
ctor.name = className; |
|
} |
|
//</debug> |
|
|
|
data.$className = className; |
|
|
|
return new Class(ctor, data, function() { |
|
var postprocessorStack = data.postprocessors || Manager.defaultPostprocessors, |
|
registeredPostprocessors = Manager.postprocessors, |
|
postprocessors = [], |
|
postprocessor, i, ln, j, subLn, postprocessorProperties, postprocessorProperty; |
|
|
|
delete data.postprocessors; |
|
|
|
for (i = 0,ln = postprocessorStack.length; i < ln; i++) { |
|
postprocessor = postprocessorStack[i]; |
|
|
|
if (typeof postprocessor === 'string') { |
|
postprocessor = registeredPostprocessors[postprocessor]; |
|
postprocessorProperties = postprocessor.properties; |
|
|
|
if (postprocessorProperties === true) { |
|
postprocessors.push(postprocessor.fn); |
|
} |
|
else if (postprocessorProperties) { |
|
for (j = 0,subLn = postprocessorProperties.length; j < subLn; j++) { |
|
postprocessorProperty = postprocessorProperties[j]; |
|
|
|
if (data.hasOwnProperty(postprocessorProperty)) { |
|
postprocessors.push(postprocessor.fn); |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
else { |
|
postprocessors.push(postprocessor); |
|
} |
|
} |
|
|
|
data.postprocessors = postprocessors; |
|
data.createdFn = createdFn; |
|
Manager.processCreate(className, this, data); |
|
}); |
|
}, |
|
|
|
processCreate: function(className, cls, clsData){ |
|
var me = this, |
|
postprocessor = clsData.postprocessors.shift(), |
|
createdFn = clsData.createdFn; |
|
|
|
if (!postprocessor) { |
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'Ext.ClassManager#classCreated', arguments); |
|
//</debug> |
|
|
|
if (className) { |
|
me.set(className, cls); |
|
} |
|
|
|
delete cls._classHooks; |
|
|
|
if (createdFn) { |
|
createdFn.call(cls, cls); |
|
} |
|
|
|
if (className) { |
|
me.triggerCreated(className); |
|
} |
|
return; |
|
} |
|
|
|
if (postprocessor.call(me, className, cls, clsData, me.processCreate) !== false) { |
|
me.processCreate(className, cls, clsData); |
|
} |
|
}, |
|
|
|
createOverride: function (className, data, createdFn) { |
|
var me = this, |
|
overriddenClassName = data.override, |
|
requires = data.requires, |
|
uses = data.uses, |
|
mixins = data.mixins, |
|
mixinsIsArray, |
|
compat = data.compatibility, |
|
depedenciesLoaded, |
|
classReady = function () { |
|
var cls, dependencies, i, key, temp; |
|
|
|
if (!depedenciesLoaded) { |
|
dependencies = requires ? requires.slice(0) : []; |
|
|
|
if (mixins) { |
|
if (!(mixinsIsArray = mixins instanceof Array)) { |
|
for (key in mixins) { |
|
if (Ext.isString(cls = mixins[key])) { |
|
dependencies.push(cls); |
|
} |
|
} |
|
} else { |
|
for (i = 0, temp = mixins.length; i < temp; ++i) { |
|
if (Ext.isString(cls = mixins[i])) { |
|
dependencies.push(cls); |
|
} |
|
} |
|
} |
|
} |
|
|
|
depedenciesLoaded = true; |
|
if (dependencies.length) { |
|
// Since the override is going to be used (its target class is |
|
// now created), we need to fetch the required classes for the |
|
// override and call us back once they are loaded: |
|
Ext.require(dependencies, classReady); |
|
return; |
|
} |
|
// else we have no dependencies, so proceed |
|
} |
|
|
|
// transform mixin class names into class references, This |
|
// loop can handle both the array and object forms of |
|
// mixin definitions |
|
if (mixinsIsArray) { |
|
for (i = 0, temp = mixins.length; i < temp; ++i) { |
|
if (Ext.isString(cls = mixins[i])) { |
|
mixins[i] = Ext.ClassManager.get(cls); |
|
} |
|
} |
|
} else if (mixins) { |
|
for (key in mixins) { |
|
if (Ext.isString(cls = mixins[key])) { |
|
mixins[key] = Ext.ClassManager.get(cls); |
|
} |
|
} |
|
} |
|
|
|
// The target class and the required classes for this override are |
|
// ready, so we can apply the override now: |
|
cls = me.get(overriddenClassName); |
|
|
|
// We don't want to apply these: |
|
delete data.override; |
|
delete data.compatibility; |
|
delete data.requires; |
|
delete data.uses; |
|
|
|
Ext.override(cls, data); |
|
|
|
// This pushes the overriding file itself into Ext.Loader.history |
|
// Hence if the target class never exists, the overriding file will |
|
// never be included in the build. |
|
Ext.Loader.history.push(className); |
|
|
|
if (uses) { |
|
// This "hides" from the Cmd auto-dependency scanner since |
|
// the reference is circular (Loader requires us). |
|
Ext['Loader'].addUsedClasses(uses); // get these classes too! |
|
} |
|
|
|
if (createdFn) { |
|
createdFn.call(cls, cls); // last but not least! |
|
} |
|
}; |
|
|
|
Manager.overrideMap[className] = true; |
|
|
|
if (!compat || Ext.checkVersion(compat)) { |
|
// Override the target class right after it's created |
|
me.onCreated(classReady, me, overriddenClassName); |
|
} |
|
|
|
me.triggerCreated(className, 2); |
|
return me; |
|
}, |
|
|
|
/** |
|
* Instantiate a class by its alias. This is usually invoked by the |
|
* shorthand {@link Ext#createByAlias}. |
|
* |
|
* If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class |
|
* has not been defined yet, it will attempt to load the class via synchronous |
|
* loading. |
|
* |
|
* var window = Ext.createByAlias('widget.window', { width: 600, height: 800 }); |
|
* |
|
* @param {String} alias |
|
* @param {Object...} args Additional arguments after the alias will be passed to the |
|
* class constructor. |
|
* @return {Object} instance |
|
*/ |
|
instantiateByAlias: function() { |
|
var alias = arguments[0], |
|
args = arraySlice.call(arguments), |
|
className = this.getNameByAlias(alias); |
|
|
|
//<debug> |
|
if (!className) { |
|
throw new Error("[Ext.createByAlias] Unrecognized alias: " + alias); |
|
} |
|
//</debug> |
|
|
|
args[0] = className; |
|
|
|
return Ext.create.apply(Ext, args); |
|
}, |
|
|
|
//<deprecated since=5.0> |
|
/** |
|
* Instantiate a class by either full name, alias or alternate name |
|
* @param {String} name |
|
* @param {Mixed} args Additional arguments after the name will be passed to the class' constructor. |
|
* @return {Object} instance |
|
* @deprecated 5.0 Use Ext.create() instead. |
|
*/ |
|
instantiate: function() { |
|
//<debug> |
|
Ext.log.warn('Ext.ClassManager.instantiate() is deprecated. Use Ext.create() instead.'); |
|
//</debug> |
|
return Ext.create.apply(Ext, arguments); |
|
}, |
|
//</deprecated> |
|
|
|
/** |
|
* @private |
|
* @param name |
|
* @param args |
|
*/ |
|
dynInstantiate: function(name, args) { |
|
args = arrayFrom(args, true); |
|
args.unshift(name); |
|
|
|
return Ext.create.apply(Ext, args); |
|
}, |
|
|
|
/** |
|
* @private |
|
* @param length |
|
*/ |
|
getInstantiator: function(length) { |
|
var instantiators = this.instantiators, |
|
instantiator, |
|
i, |
|
args; |
|
|
|
instantiator = instantiators[length]; |
|
|
|
if (!instantiator) { |
|
i = length; |
|
args = []; |
|
|
|
for (i = 0; i < length; i++) { |
|
args.push('a[' + i + ']'); |
|
} |
|
|
|
instantiator = instantiators[length] = new Function('c', 'a', 'return new c(' + args.join(',') + ')'); |
|
//<debug> |
|
instantiator.name = "Ext.create" + length; |
|
//</debug> |
|
} |
|
|
|
return instantiator; |
|
}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
postprocessors: {}, |
|
|
|
/** |
|
* @private |
|
*/ |
|
defaultPostprocessors: [], |
|
|
|
/** |
|
* Register a post-processor function. |
|
* |
|
* @private |
|
* @param {String} name |
|
* @param {Function} postprocessor |
|
*/ |
|
registerPostprocessor: function(name, fn, properties, position, relativeTo) { |
|
if (!position) { |
|
position = 'last'; |
|
} |
|
|
|
if (!properties) { |
|
properties = [name]; |
|
} |
|
|
|
this.postprocessors[name] = { |
|
name: name, |
|
properties: properties || false, |
|
fn: fn |
|
}; |
|
|
|
this.setDefaultPostprocessorPosition(name, position, relativeTo); |
|
|
|
return this; |
|
}, |
|
|
|
/** |
|
* Set the default post processors array stack which are applied to every class. |
|
* |
|
* @private |
|
* @param {String/Array} postprocessors The name of a registered post processor or an array of registered names. |
|
* @return {Ext.ClassManager} this |
|
*/ |
|
setDefaultPostprocessors: function(postprocessors) { |
|
this.defaultPostprocessors = arrayFrom(postprocessors); |
|
|
|
return this; |
|
}, |
|
|
|
/** |
|
* Insert this post-processor at a specific position in the stack, optionally relative to |
|
* any existing post-processor |
|
* |
|
* @private |
|
* @param {String} name The post-processor name. Note that it needs to be registered with |
|
* {@link Ext.ClassManager#registerPostprocessor} before this |
|
* @param {String} offset The insertion position. Four possible values are: |
|
* 'first', 'last', or: 'before', 'after' (relative to the name provided in the third argument) |
|
* @param {String} relativeName |
|
* @return {Ext.ClassManager} this |
|
*/ |
|
setDefaultPostprocessorPosition: function(name, offset, relativeName) { |
|
var defaultPostprocessors = this.defaultPostprocessors, |
|
index; |
|
|
|
if (typeof offset === 'string') { |
|
if (offset === 'first') { |
|
defaultPostprocessors.unshift(name); |
|
|
|
return this; |
|
} |
|
else if (offset === 'last') { |
|
defaultPostprocessors.push(name); |
|
|
|
return this; |
|
} |
|
|
|
offset = (offset === 'after') ? 1 : -1; |
|
} |
|
|
|
index = Ext.Array.indexOf(defaultPostprocessors, relativeName); |
|
|
|
if (index !== -1) { |
|
Ext.Array.splice(defaultPostprocessors, Math.max(0, index + offset), 0, name); |
|
} |
|
|
|
return this; |
|
} |
|
}); |
|
|
|
/** |
|
* @cfg xtype |
|
* @member Ext.Class |
|
* @inheritdoc Ext.Component#cfg-xtype |
|
*/ |
|
|
|
/** |
|
* @cfg {String} override |
|
* @member Ext.Class |
|
* Overrides members of the specified `target` class. |
|
* |
|
* **NOTE:** the overridden class must have been defined using |
|
* {@link #define Ext.define} in order to use the `override` config. |
|
* |
|
* Methods defined on the overriding class will not automatically call the methods of |
|
* the same name in the ancestor class chain. To call the parent's method of the |
|
* same name you must call {@link Ext.Base#callParent callParent}. To skip the |
|
* method of the overridden class and call its parent you will instead call |
|
* {@link Ext.Base#callSuper callSuper}. |
|
* |
|
* See {@link Ext#define Ext.define} for additional usage examples. |
|
*/ |
|
|
|
//<feature classSystem.alias> |
|
/** |
|
* @cfg {String/String[]} alias |
|
* @member Ext.Class |
|
* List of short aliases for class names. An alias consists of a namespace and a name concatenated by a period as <namespace>.<name> |
|
* |
|
* - **namespace** - The namespace describes what kind of alias this is and must be all lowercase. |
|
* - **name** - The name of the alias which allows the lazy-instantiation via the alias. The name shouldn't contain any periods. |
|
* |
|
* A list of namespaces and the usages are: |
|
* |
|
* - **feature** - {@link Ext.grid.Panel Grid} features |
|
* - **plugin** - Plugins |
|
* - **store** - {@link Ext.data.Store} |
|
* - **widget** - Components |
|
* |
|
* Most useful for defining xtypes for widgets: |
|
* |
|
* Ext.define('MyApp.CoolPanel', { |
|
* extend: 'Ext.panel.Panel', |
|
* alias: ['widget.coolpanel'], |
|
* title: 'Yeah!' |
|
* }); |
|
* |
|
* // Using Ext.create |
|
* Ext.create('widget.coolpanel'); |
|
* |
|
* // Using the shorthand for defining widgets by xtype |
|
* Ext.widget('panel', { |
|
* items: [ |
|
* {xtype: 'coolpanel', html: 'Foo'}, |
|
* {xtype: 'coolpanel', html: 'Bar'} |
|
* ] |
|
* }); |
|
*/ |
|
Manager.registerPostprocessor('alias', function(name, cls, data) { |
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#aliasPostProcessor', arguments); |
|
//</debug> |
|
|
|
var aliases = Ext.Array.from(data.alias), |
|
i, ln; |
|
|
|
for (i = 0,ln = aliases.length; i < ln; i++) { |
|
alias = aliases[i]; |
|
|
|
this.addAlias(cls, alias); |
|
} |
|
|
|
}, ['xtype', 'alias']); |
|
//</feature> |
|
|
|
//<feature classSystem.singleton> |
|
/** |
|
* @cfg {Boolean} singleton |
|
* @member Ext.Class |
|
* When set to true, the class will be instantiated as singleton. For example: |
|
* |
|
* Ext.define('Logger', { |
|
* singleton: true, |
|
* log: function(msg) { |
|
* console.log(msg); |
|
* } |
|
* }); |
|
* |
|
* Logger.log('Hello'); |
|
*/ |
|
Manager.registerPostprocessor('singleton', function(name, cls, data, fn) { |
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#singletonPostProcessor', arguments); |
|
//</debug> |
|
|
|
if (data.singleton) { |
|
fn.call(this, name, new cls(), data); |
|
} |
|
else { |
|
return true; |
|
} |
|
return false; |
|
}); |
|
//</feature> |
|
|
|
//<feature classSystem.alternateClassName> |
|
/** |
|
* @cfg {String/String[]} alternateClassName |
|
* @member Ext.Class |
|
* Defines alternate names for this class. For example: |
|
* |
|
* Ext.define('Developer', { |
|
* alternateClassName: ['Coder', 'Hacker'], |
|
* code: function(msg) { |
|
* alert('Typing... ' + msg); |
|
* } |
|
* }); |
|
* |
|
* var joe = Ext.create('Developer'); |
|
* joe.code('stackoverflow'); |
|
* |
|
* var rms = Ext.create('Hacker'); |
|
* rms.code('hack hack'); |
|
*/ |
|
Manager.registerPostprocessor('alternateClassName', function(name, cls, data) { |
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(name, 'Ext.ClassManager#alternateClassNamePostprocessor', arguments); |
|
//</debug> |
|
|
|
var alternates = data.alternateClassName, |
|
i, ln, alternate; |
|
|
|
if (!(alternates instanceof Array)) { |
|
alternates = [alternates]; |
|
} |
|
|
|
for (i = 0, ln = alternates.length; i < ln; i++) { |
|
alternate = alternates[i]; |
|
|
|
//<debug> |
|
if (typeof alternate !== 'string') { |
|
throw new Error("[Ext.define] Invalid alternate of: '" + alternate + "' for class: '" + name + "'; must be a valid string"); |
|
} |
|
//</debug> |
|
|
|
this.set(alternate, cls); |
|
} |
|
}); |
|
//</feature> |
|
|
|
/** |
|
* @cfg {Object} debugHooks |
|
* A collection of diagnostic methods to decorate the real methods of the class. These |
|
* methods are applied as an `override` if this class has debug enabled as defined by |
|
* `Ext.isDebugEnabled`. |
|
* |
|
* These will be automatically removed by the Sencha Cmd compiler for production builds. |
|
* |
|
* Example usage: |
|
* |
|
* Ext.define('Foo.bar.Class', { |
|
* foo: function (a, b, c) { |
|
* ... |
|
* }, |
|
* |
|
* bar: function (a, b) { |
|
* ... |
|
* return 42; |
|
* }, |
|
* |
|
* debugHooks: { |
|
* foo: function (a, b, c) { |
|
* // check arguments... |
|
* return this.callParent(arguments); |
|
* } |
|
* } |
|
* }); |
|
* |
|
* If you specify a `$enabled` property in the `debugHooks` object that will be used |
|
* as the default enabled state for the hooks. If the `{@link Ext#manifest}` contains |
|
* a `debug` object of if `{@link Ext#debugConfig}` is specified, the `$enabled` flag |
|
* will override its "*" value. |
|
*/ |
|
Manager.registerPostprocessor('debugHooks', function(name, Class, data) { |
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#debugHooks', arguments); |
|
|
|
if (Ext.isDebugEnabled(Class.$className, data.debugHooks.$enabled)) { |
|
delete data.debugHooks.$enabled; |
|
Ext.override(Class, data.debugHooks); |
|
} |
|
//</debug> |
|
|
|
// may already have an instance here in the case of singleton |
|
var target = Class.isInstance ? Class.self : Class; |
|
|
|
delete target.prototype.debugHooks; |
|
}); |
|
|
|
/** |
|
* @cfg {Object} deprecated |
|
* The object given has properties that describe the versions at which the deprecations |
|
* apply. |
|
* |
|
* The purpose of the `deprecated` declaration is to enable development mode to give |
|
* suitable error messages when deprecated methods or properties are used. Methods can |
|
* always be injected to provide this feedback, but properties can only be handled on |
|
* some browsers (those that support `Object.defineProperty`). |
|
* |
|
* In some cases, deprecated methods can be restored to their previous behavior or |
|
* added back if they have been removed. |
|
* |
|
* The structure of a `deprecated` declaration is this: |
|
* |
|
* Ext.define('Foo.bar.Class', { |
|
* ... |
|
* |
|
* deprecated: { |
|
* // Optional package name - default is the framework (ext or touch) |
|
* name: 'foobar', |
|
* |
|
* '5.0': { |
|
* methods: { |
|
* // Throws: '"removedMethod" is deprecated.' |
|
* removedMethod: null, |
|
* |
|
* // Throws: '"oldMethod" is deprecated. Please use "newMethod" instead.' |
|
* oldMethod: 'newMethod', |
|
* |
|
* // When this block is enabled, this method is applied as an |
|
* // override. Otherwise you get same as "removeMethod". |
|
* method: function () { |
|
* // Do what v5 "method" did. If "method" exists in newer |
|
* // versions callParent can call it. If 5.1 has "method" |
|
* // then it would be next in line, otherwise 5.2 and last |
|
* // would be the current class. |
|
* }, |
|
* |
|
* moreHelpful: { |
|
* message: 'Something helpful to do instead.', |
|
* fn: function () { |
|
* // The v5 "moreHelpful" method to use when enabled. |
|
* } |
|
* } |
|
* }, |
|
* properties: { |
|
* // Throws: '"removedProp" is deprecated.' |
|
* removedProp: null, |
|
* |
|
* // Throws: '"oldProp" is deprecated. Please use "newProp" instead.' |
|
* oldProp: 'newProp', |
|
* |
|
* helpful: { |
|
* message: 'Something helpful message about what to do.' |
|
* } |
|
* ... |
|
* }, |
|
* statics: { |
|
* methods: { |
|
* ... |
|
* }, |
|
* properties: { |
|
* ... |
|
* }, |
|
* } |
|
* }, |
|
* |
|
* '5.1': { |
|
* ... |
|
* }, |
|
* |
|
* '5.2': { |
|
* ... |
|
* } |
|
* } |
|
* }); |
|
* |
|
* The primary content of `deprecated` are the version number keys. These indicate |
|
* a version number where methods or properties were deprecated. These versions are |
|
* compared to the version reported by `Ext.getCompatVersion` to determine the action |
|
* to take for each "block". |
|
* |
|
* When the compatibility version is set to a value less than a version number key, |
|
* that block is said to be "enabled". For example, if a method was deprecated in |
|
* version 5.0 but the desired compatibility level is 4.2 then the block is used to |
|
* patch methods and (to some degree) restore pre-5.0 compatibility. |
|
* |
|
* When multiple active blocks have the same method name, each method is applied as |
|
* an override in reverse order of version. In the above example, if a method appears |
|
* in the "5.0", "5.1" and "5.2" blocks then the "5.2" method is applied as an override |
|
* first, followed by the "5.1" method and finally the "5.0" method. This means that |
|
* the `callParent` from the "5.0" method calls the "5.1" method which calls the |
|
* "5.2" method which can (if applicable) call the current version. |
|
*/ |
|
Manager.registerPostprocessor('deprecated', function(name, Class, data) { |
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#deprecated', arguments); |
|
//</debug> |
|
|
|
// may already have an instance here in the case of singleton |
|
var target = Class.isInstance ? Class.self : Class; |
|
target.addDeprecations(data.deprecated); |
|
|
|
delete target.prototype.deprecated; |
|
}); |
|
|
|
Ext.apply(Ext, { |
|
/** |
|
* Instantiate a class by either full name, alias or alternate name. |
|
* |
|
* If {@link Ext.Loader} is {@link Ext.Loader#setConfig enabled} and the class has |
|
* not been defined yet, it will attempt to load the class via synchronous loading. |
|
* |
|
* For example, all these three lines return the same result: |
|
* |
|
* // xtype |
|
* var window = Ext.create({ |
|
* xtype: 'window', |
|
* width: 600, |
|
* height: 800, |
|
* ... |
|
* }); |
|
* |
|
* // alias |
|
* var window = Ext.create('widget.window', { |
|
* width: 600, |
|
* height: 800, |
|
* ... |
|
* }); |
|
* |
|
* // alternate name |
|
* var window = Ext.create('Ext.Window', { |
|
* width: 600, |
|
* height: 800, |
|
* ... |
|
* }); |
|
* |
|
* // full class name |
|
* var window = Ext.create('Ext.window.Window', { |
|
* width: 600, |
|
* height: 800, |
|
* ... |
|
* }); |
|
* |
|
* // single object with xclass property: |
|
* var window = Ext.create({ |
|
* xclass: 'Ext.window.Window', // any valid value for 'name' (above) |
|
* width: 600, |
|
* height: 800, |
|
* ... |
|
* }); |
|
* |
|
* @param {String} [name] The class name or alias. Can be specified as `xclass` |
|
* property if only one object parameter is specified. |
|
* @param {Object...} [args] Additional arguments after the name will be passed to |
|
* the class' constructor. |
|
* @return {Object} instance |
|
* @member Ext |
|
* @method create |
|
*/ |
|
create: function () { |
|
var name = arguments[0], |
|
nameType = typeof name, |
|
args = arraySlice.call(arguments, 1), |
|
cls; |
|
|
|
if (nameType === 'function') { |
|
cls = name; |
|
} else { |
|
if (nameType !== 'string' && args.length === 0) { |
|
args = [name]; |
|
if (!(name = name.xclass)) { |
|
name = args[0].xtype; |
|
if (name) { |
|
name = 'widget.' + name; |
|
} |
|
} |
|
} |
|
|
|
//<debug> |
|
if (typeof name !== 'string' || name.length < 1) { |
|
throw new Error("[Ext.create] Invalid class name or alias '" + name + |
|
"' specified, must be a non-empty string"); |
|
} |
|
//</debug> |
|
|
|
name = Manager.resolveName(name); |
|
cls = Manager.get(name); |
|
} |
|
|
|
// Still not existing at this point, try to load it via synchronous mode as the last resort |
|
if (!cls) { |
|
//<debug> |
|
//<if nonBrowser> |
|
!isNonBrowser && |
|
//</if> |
|
Ext.log.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding " + |
|
"Ext.require('" + name + "') above Ext.onReady"); |
|
//</debug> |
|
|
|
Ext.syncRequire(name); |
|
|
|
cls = Manager.get(name); |
|
} |
|
|
|
//<debug> |
|
if (!cls) { |
|
throw new Error("[Ext.create] Unrecognized class name / alias: " + name); |
|
} |
|
|
|
if (typeof cls !== 'function') { |
|
throw new Error("[Ext.create] Singleton '" + name + "' cannot be instantiated."); |
|
} |
|
//</debug> |
|
|
|
return Manager.getInstantiator(args.length)(cls, args); |
|
}, |
|
|
|
/** |
|
* Convenient shorthand to create a widget by its xtype or a config object. |
|
* |
|
* var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button'); |
|
* |
|
* var panel = Ext.widget('panel', { // Equivalent to Ext.create('widget.panel') |
|
* title: 'Panel' |
|
* }); |
|
* |
|
* var grid = Ext.widget({ |
|
* xtype: 'grid', |
|
* ... |
|
* }); |
|
* |
|
* If a {@link Ext.Component component} instance is passed, it is simply returned. |
|
* |
|
* @member Ext |
|
* @param {String} [name] The xtype of the widget to create. |
|
* @param {Object} [config] The configuration object for the widget constructor. |
|
* @return {Object} The widget instance |
|
*/ |
|
widget: function(name, config) { |
|
// forms: |
|
// 1: (xtype) |
|
// 2: (xtype, config) |
|
// 3: (config) |
|
// 4: (xtype, component) |
|
// 5: (component) |
|
// |
|
var xtype = name, |
|
alias, className, T; |
|
|
|
if (typeof xtype !== 'string') { // if (form 3 or 5) |
|
// first arg is config or component |
|
config = name; // arguments[0] |
|
xtype = config.xtype; |
|
className = config.xclass; |
|
} else { |
|
config = config || {}; |
|
} |
|
|
|
if (config.isComponent) { |
|
return config; |
|
} |
|
|
|
if (!className) { |
|
alias = 'widget.' + xtype; |
|
className = Manager.getNameByAlias(alias); |
|
} |
|
|
|
// this is needed to support demand loading of the class |
|
if (className) { |
|
T = Manager.get(className); |
|
} |
|
|
|
if (!T) { |
|
return Ext.create(className || alias, config); |
|
} |
|
return new T(config); |
|
}, |
|
|
|
/** |
|
* @inheritdoc Ext.ClassManager#instantiateByAlias |
|
* @member Ext |
|
* @method createByAlias |
|
*/ |
|
createByAlias: alias(Manager, 'instantiateByAlias'), |
|
|
|
/** |
|
* Defines a class or override. A basic class is defined like this: |
|
* |
|
* Ext.define('My.awesome.Class', { |
|
* someProperty: 'something', |
|
* |
|
* someMethod: function(s) { |
|
* alert(s + this.someProperty); |
|
* } |
|
* |
|
* ... |
|
* }); |
|
* |
|
* var obj = new My.awesome.Class(); |
|
* |
|
* obj.someMethod('Say '); // alerts 'Say something' |
|
* |
|
* To create an anonymous class, pass `null` for the `className`: |
|
* |
|
* Ext.define(null, { |
|
* constructor: function () { |
|
* // ... |
|
* } |
|
* }); |
|
* |
|
* In some cases, it is helpful to create a nested scope to contain some private |
|
* properties. The best way to do this is to pass a function instead of an object |
|
* as the second parameter. This function will be called to produce the class |
|
* body: |
|
* |
|
* Ext.define('MyApp.foo.Bar', function () { |
|
* var id = 0; |
|
* |
|
* return { |
|
* nextId: function () { |
|
* return ++id; |
|
* } |
|
* }; |
|
* }); |
|
* |
|
* _Note_ that when using override, the above syntax will not override successfully, because |
|
* the passed function would need to be executed first to determine whether or not the result |
|
* is an override or defining a new object. As such, an alternative syntax that immediately |
|
* invokes the function can be used: |
|
* |
|
* Ext.define('MyApp.override.BaseOverride', function () { |
|
* var counter = 0; |
|
* |
|
* return { |
|
* override: 'Ext.Component', |
|
* logId: function () { |
|
* console.log(++counter, this.id); |
|
* } |
|
* }; |
|
* }()); |
|
* |
|
* |
|
* When using this form of `Ext.define`, the function is passed a reference to its |
|
* class. This can be used as an efficient way to access any static properties you |
|
* may have: |
|
* |
|
* Ext.define('MyApp.foo.Bar', function (Bar) { |
|
* return { |
|
* statics: { |
|
* staticMethod: function () { |
|
* // ... |
|
* } |
|
* }, |
|
* |
|
* method: function () { |
|
* return Bar.staticMethod(); |
|
* } |
|
* }; |
|
* }); |
|
* |
|
* To define an override, include the `override` property. The content of an |
|
* override is aggregated with the specified class in order to extend or modify |
|
* that class. This can be as simple as setting default property values or it can |
|
* extend and/or replace methods. This can also extend the statics of the class. |
|
* |
|
* One use for an override is to break a large class into manageable pieces. |
|
* |
|
* // File: /src/app/Panel.js |
|
* |
|
* Ext.define('My.app.Panel', { |
|
* extend: 'Ext.panel.Panel', |
|
* requires: [ |
|
* 'My.app.PanelPart2', |
|
* 'My.app.PanelPart3' |
|
* ] |
|
* |
|
* constructor: function (config) { |
|
* this.callParent(arguments); // calls Ext.panel.Panel's constructor |
|
* //... |
|
* }, |
|
* |
|
* statics: { |
|
* method: function () { |
|
* return 'abc'; |
|
* } |
|
* } |
|
* }); |
|
* |
|
* // File: /src/app/PanelPart2.js |
|
* Ext.define('My.app.PanelPart2', { |
|
* override: 'My.app.Panel', |
|
* |
|
* constructor: function (config) { |
|
* this.callParent(arguments); // calls My.app.Panel's constructor |
|
* //... |
|
* } |
|
* }); |
|
* |
|
* Another use of overrides is to provide optional parts of classes that can be |
|
* independently required. In this case, the class may even be unaware of the |
|
* override altogether. |
|
* |
|
* Ext.define('My.ux.CoolTip', { |
|
* override: 'Ext.tip.ToolTip', |
|
* |
|
* constructor: function (config) { |
|
* this.callParent(arguments); // calls Ext.tip.ToolTip's constructor |
|
* //... |
|
* } |
|
* }); |
|
* |
|
* The above override can now be required as normal. |
|
* |
|
* Ext.define('My.app.App', { |
|
* requires: [ |
|
* 'My.ux.CoolTip' |
|
* ] |
|
* }); |
|
* |
|
* Overrides can also contain statics, inheritableStatics, or privates: |
|
* |
|
* Ext.define('My.app.BarMod', { |
|
* override: 'Ext.foo.Bar', |
|
* |
|
* statics: { |
|
* method: function (x) { |
|
* return this.callParent([x * 2]); // call Ext.foo.Bar.method |
|
* } |
|
* } |
|
* }); |
|
* |
|
* Starting in version 4.2.2, overrides can declare their `compatibility` based |
|
* on the framework version or on versions of other packages. For details on the |
|
* syntax and options for these checks, see `Ext.checkVersion`. |
|
* |
|
* The simplest use case is to test framework version for compatibility: |
|
* |
|
* Ext.define('App.overrides.grid.Panel', { |
|
* override: 'Ext.grid.Panel', |
|
* |
|
* compatibility: '4.2.2', // only if framework version is 4.2.2 |
|
* |
|
* //... |
|
* }); |
|
* |
|
* An array is treated as an OR, so if any specs match, the override is |
|
* compatible. |
|
* |
|
* Ext.define('App.overrides.some.Thing', { |
|
* override: 'Foo.some.Thing', |
|
* |
|
* compatibility: [ |
|
* '4.2.2', |
|
* '[email protected]' |
|
* ], |
|
* |
|
* //... |
|
* }); |
|
* |
|
* To require that all specifications match, an object can be provided: |
|
* |
|
* Ext.define('App.overrides.some.Thing', { |
|
* override: 'Foo.some.Thing', |
|
* |
|
* compatibility: { |
|
* and: [ |
|
* '4.2.2', |
|
* '[email protected]' |
|
* ] |
|
* }, |
|
* |
|
* //... |
|
* }); |
|
* |
|
* Because the object form is just a recursive check, these can be nested: |
|
* |
|
* Ext.define('App.overrides.some.Thing', { |
|
* override: 'Foo.some.Thing', |
|
* |
|
* compatibility: { |
|
* and: [ |
|
* '4.2.2', // exactly version 4.2.2 of the framework *AND* |
|
* { |
|
* // either (or both) of these package specs: |
|
* or: [ |
|
* '[email protected]', |
|
* '[email protected]+' |
|
* ] |
|
* } |
|
* ] |
|
* }, |
|
* |
|
* //... |
|
* }); |
|
* |
|
* IMPORTANT: An override is only included in a build if the class it overrides is |
|
* required. Otherwise, the override, like the target class, is not included. In |
|
* Sencha Cmd v4, the `compatibility` declaration can likewise be used to remove |
|
* incompatible overrides from a build. |
|
* |
|
* @param {String} className The class name to create in string dot-namespaced format, for example: |
|
* 'My.very.awesome.Class', 'FeedViewer.plugin.CoolPager' |
|
* It is highly recommended to follow this simple convention: |
|
* - The root and the class name are 'CamelCased' |
|
* - Everything else is lower-cased |
|
* Pass `null` to create an anonymous class. |
|
* @param {Object} data The key - value pairs of properties to apply to this class. Property names can be of any valid |
|
* strings, except those in the reserved listed below: |
|
* |
|
* - {@link Ext.Class#cfg-alias alias} |
|
* - {@link Ext.Class#cfg-alternateClassName alternateClassName} |
|
* - {@link Ext.Class#cfg-cachedConfig cachedConfig} |
|
* - {@link Ext.Class#cfg-config config} |
|
* - {@link Ext.Class#cfg-extend extend} |
|
* - {@link Ext.Class#cfg-inheritableStatics inheritableStatics} |
|
* - {@link Ext.Class#cfg-mixins mixins} |
|
* - {@link Ext.Class#cfg-override override} |
|
* - {@link Ext.Class#cfg-platformConfig platformConfig} |
|
* - {@link Ext.Class#cfg-privates privates} |
|
* - {@link Ext.Class#cfg-requires requires} |
|
* - `self` |
|
* - {@link Ext.Class#cfg-singleton singleton} |
|
* - {@link Ext.Class#cfg-statics statics} |
|
* - {@link Ext.Class#cfg-uses uses} |
|
* - {@link Ext.Class#cfg-xtype xtype} (for {@link Ext.Component Components} only) |
|
* |
|
* @param {Function} [createdFn] Callback to execute after the class is created, the execution scope of which |
|
* (`this`) will be the newly created class itself. |
|
* @return {Ext.Base} |
|
* @member Ext |
|
*/ |
|
define: function (className, data, createdFn) { |
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'ClassManager#define', arguments); |
|
//</debug> |
|
|
|
if (data.override) { |
|
Manager.classState[className] = 20; |
|
return Manager.createOverride.apply(Manager, arguments); |
|
} |
|
|
|
Manager.classState[className] = 10; |
|
return Manager.create.apply(Manager, arguments); |
|
}, |
|
|
|
/** |
|
* Undefines a class defined using the #define method. Typically used |
|
* for unit testing where setting up and tearing down a class multiple |
|
* times is required. For example: |
|
* |
|
* // define a class |
|
* Ext.define('Foo', { |
|
* ... |
|
* }); |
|
* |
|
* // run test |
|
* |
|
* // undefine the class |
|
* Ext.undefine('Foo'); |
|
* @param {String} className The class name to undefine in string dot-namespaced format. |
|
* @private |
|
*/ |
|
undefine: function(className) { |
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(className, 'Ext.ClassManager#undefine', arguments); |
|
//</debug> |
|
|
|
var classes = Manager.classes, |
|
parts, partCount, namespace, i; |
|
|
|
delete Manager.namespaceParseCache[className]; |
|
delete classes[className]; |
|
delete Manager.existCache[className]; |
|
delete Manager.classState[className]; |
|
|
|
Manager.removeName(className); |
|
|
|
parts = Manager.parseNamespace(className); |
|
partCount = parts.length - 1; |
|
namespace = parts[0]; |
|
|
|
for (i = 1; i < partCount; i++) { |
|
namespace = namespace[parts[i]]; |
|
if (!namespace) { |
|
return; |
|
} |
|
} |
|
|
|
// Old IE blows up on attempt to delete window property |
|
try { |
|
delete namespace[parts[partCount]]; |
|
} |
|
catch (e) { |
|
namespace[parts[partCount]] = undefined; |
|
} |
|
}, |
|
|
|
/** |
|
* @inheritdoc Ext.ClassManager#getName |
|
* @member Ext |
|
* @method getClassName |
|
*/ |
|
getClassName: alias(Manager, 'getName'), |
|
|
|
/** |
|
* Returns the displayName property or className or object. When all else fails, returns "Anonymous". |
|
* @param {Object} object |
|
* @return {String} |
|
*/ |
|
getDisplayName: function(object) { |
|
if (object) { |
|
if (object.displayName) { |
|
return object.displayName; |
|
} |
|
|
|
if (object.$name && object.$class) { |
|
return Ext.getClassName(object.$class) + '#' + object.$name; |
|
} |
|
|
|
if (object.$className) { |
|
return object.$className; |
|
} |
|
} |
|
|
|
return 'Anonymous'; |
|
}, |
|
|
|
/** |
|
* @inheritdoc Ext.ClassManager#getClass |
|
* @member Ext |
|
* @method getClass |
|
*/ |
|
getClass: alias(Manager, 'getClass'), |
|
|
|
/** |
|
* Creates namespaces to be used for scoping variables and classes so that they are not global. |
|
* Specifying the last node of a namespace implicitly creates all other nodes. Usage: |
|
* |
|
* Ext.namespace('Company', 'Company.data'); |
|
* |
|
* // equivalent and preferable to the above syntax |
|
* Ext.ns('Company.data'); |
|
* |
|
* Company.Widget = function() { ... }; |
|
* |
|
* Company.data.CustomStore = function(config) { ... }; |
|
* |
|
* @param {String...} namespaces |
|
* @return {Object} The namespace object. |
|
* (If multiple arguments are passed, this will be the last namespace created) |
|
* @member Ext |
|
* @method namespace |
|
*/ |
|
namespace: alias(Manager, 'createNamespaces') |
|
}); |
|
|
|
/** |
|
* Old name for {@link Ext#widget}. |
|
* @deprecated Use {@link Ext#widget} instead. |
|
* @method createWidget |
|
* @member Ext |
|
* @private |
|
*/ |
|
Ext.createWidget = Ext.widget; |
|
|
|
/** |
|
* Convenient alias for {@link Ext#namespace Ext.namespace}. |
|
* @inheritdoc Ext#namespace |
|
* @member Ext |
|
* @method ns |
|
*/ |
|
Ext.ns = Ext.namespace; |
|
|
|
Class.registerPreprocessor('className', function(cls, data) { |
|
if ('$className' in data) { |
|
cls.$className = data.$className; |
|
//<debug> |
|
cls.displayName = cls.$className; |
|
//</debug> |
|
} |
|
|
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#classNamePreprocessor', arguments); |
|
//</debug> |
|
}, true, 'first'); |
|
|
|
Class.registerPreprocessor('alias', function(cls, data) { |
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#aliasPreprocessor', arguments); |
|
//</debug> |
|
|
|
var prototype = cls.prototype, |
|
xtypes = arrayFrom(data.xtype), |
|
aliases = arrayFrom(data.alias), |
|
widgetPrefix = 'widget.', |
|
widgetPrefixLength = widgetPrefix.length, |
|
xtypesChain = Array.prototype.slice.call(prototype.xtypesChain || []), |
|
xtypesMap = Ext.merge({}, prototype.xtypesMap || {}), |
|
i, ln, alias, xtype; |
|
|
|
for (i = 0,ln = aliases.length; i < ln; i++) { |
|
alias = aliases[i]; |
|
|
|
//<debug> |
|
if (typeof alias !== 'string' || alias.length < 1) { |
|
throw new Error("[Ext.define] Invalid alias of: '" + alias + "' for class: '" + name + "'; must be a valid string"); |
|
} |
|
//</debug> |
|
|
|
if (alias.substring(0, widgetPrefixLength) === widgetPrefix) { |
|
xtype = alias.substring(widgetPrefixLength); |
|
Ext.Array.include(xtypes, xtype); |
|
} |
|
} |
|
|
|
cls.xtype = data.xtype = xtypes[0]; |
|
data.xtypes = xtypes; |
|
|
|
for (i = 0,ln = xtypes.length; i < ln; i++) { |
|
xtype = xtypes[i]; |
|
|
|
if (!xtypesMap[xtype]) { |
|
xtypesMap[xtype] = true; |
|
xtypesChain.push(xtype); |
|
} |
|
} |
|
|
|
data.xtypesChain = xtypesChain; |
|
data.xtypesMap = xtypesMap; |
|
|
|
Ext.Function.interceptAfter(data, 'onClassCreated', function() { |
|
//<debug> |
|
Ext.classSystemMonitor && Ext.classSystemMonitor(cls, 'Ext.ClassManager#aliasPreprocessor#afterClassCreated', arguments); |
|
//</debug> |
|
|
|
var mixins = prototype.mixins, |
|
key, mixin; |
|
|
|
for (key in mixins) { |
|
if (mixins.hasOwnProperty(key)) { |
|
mixin = mixins[key]; |
|
|
|
xtypes = mixin.xtypes; |
|
|
|
if (xtypes) { |
|
for (i = 0,ln = xtypes.length; i < ln; i++) { |
|
xtype = xtypes[i]; |
|
|
|
if (!xtypesMap[xtype]) { |
|
xtypesMap[xtype] = true; |
|
xtypesChain.push(xtype); |
|
} |
|
} |
|
} |
|
} |
|
} |
|
}); |
|
|
|
for (i = 0,ln = xtypes.length; i < ln; i++) { |
|
xtype = xtypes[i]; |
|
|
|
//<debug> |
|
if (typeof xtype !== 'string' || xtype.length < 1) { |
|
throw new Error("[Ext.define] Invalid xtype of: '" + xtype + "' for class: '" + name + "'; must be a valid non-empty string"); |
|
} |
|
//</debug> |
|
|
|
Ext.Array.include(aliases, widgetPrefix + xtype); |
|
} |
|
|
|
data.alias = aliases; |
|
|
|
}, ['xtype', 'alias']); |
|
|
|
// load the cmd-5 style app manifest metadata now, if available... |
|
if(Ext.manifest) { |
|
var manifest = Ext.manifest, |
|
classes = manifest.classes, |
|
paths = manifest.paths, |
|
aliases = {}, |
|
alternates = {}, |
|
className, obj, name, path, baseUrl; |
|
|
|
if(paths) { |
|
// if the manifest paths were calculated as relative to the |
|
// bootstrap file, then we need to prepend Boot.baseUrl to the |
|
// paths before processing |
|
if(manifest.bootRelative) { |
|
baseUrl = Ext.Boot.baseUrl; |
|
for(path in paths) { |
|
if(paths.hasOwnProperty(path)) { |
|
paths[path] = baseUrl + paths[path]; |
|
} |
|
} |
|
} |
|
Manager.setPath(paths); |
|
} |
|
|
|
if(classes) { |
|
for(className in classes) { |
|
alternates[className] = []; |
|
aliases[className] = []; |
|
obj = classes[className]; |
|
if(obj.alias) { |
|
aliases[className] = obj.alias; |
|
} |
|
if(obj.alternates) { |
|
alternates[className] = obj.alternates; |
|
} |
|
} |
|
} |
|
|
|
Manager.addAlias(aliases); |
|
Manager.addAlternate(alternates); |
|
} |
|
|
|
return Manager; |
|
}(Ext.Class, Ext.Function.alias, Array.prototype.slice, Ext.Array.from, Ext.global)); |
|
|
|
|