<!DOCTYPE html>
|
<html>
|
<head>
|
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
|
<title>The source code</title>
|
<link href="../resources/prettify/prettify.css" type="text/css" rel="stylesheet" />
|
<script type="text/javascript" src="../resources/prettify/prettify.js"></script>
|
<style type="text/css">
|
.highlight { display: block; background-color: #ddd; }
|
</style>
|
<script type="text/javascript">
|
function highlight() {
|
document.getElementById(location.hash.replace(/#/, "")).className = "highlight";
|
}
|
</script>
|
</head>
|
<body onload="prettyPrint(); highlight();">
|
<pre class="prettyprint lang-js">// @tag foundation,core
|
// @require Base.js
|
// @define Ext.Class
|
|
<span id='Ext-Class'>/**
|
</span> * @author Jacky Nguyen <jacky@sencha.com>
|
* @docauthor Jacky Nguyen <jacky@sencha.com>
|
* @class Ext.Class
|
*
|
* Handles class creation throughout the framework. This is a low level factory that is used by Ext.ClassManager and generally
|
* should not be used directly. If you choose to use Ext.Class you will lose out on the namespace, aliasing and depency loading
|
* features made available by Ext.ClassManager. The only time you would use Ext.Class directly is to create an anonymous class.
|
*
|
* If you wish to create a class you should use {@link Ext#define Ext.define} which aliases
|
* {@link Ext.ClassManager#create Ext.ClassManager.create} to enable namespacing and dynamic dependency resolution.
|
*
|
* Ext.Class is the factory and **not** the superclass of everything. For the base class that **all** Ext classes inherit
|
* from, see {@link Ext.Base}.
|
*/
|
(function() {
|
var ExtClass,
|
Base = Ext.Base,
|
baseStaticMembers = [],
|
baseStaticMember, baseStaticMemberLength;
|
|
for (baseStaticMember in Base) {
|
if (Base.hasOwnProperty(baseStaticMember)) {
|
baseStaticMembers.push(baseStaticMember);
|
}
|
}
|
|
baseStaticMemberLength = baseStaticMembers.length;
|
|
// Creates a constructor that has nothing extra in its scope chain.
|
function makeCtor (className) {
|
function constructor () {
|
// Opera has some problems returning from a constructor when Dragonfly isn't running. The || null seems to
|
// be sufficient to stop it misbehaving. Known to be required against 10.53, 11.51 and 11.61.
|
return this.constructor.apply(this, arguments) || null;
|
}
|
//<debug>
|
if (className) {
|
constructor.displayName = className;
|
}
|
//</debug>
|
return constructor;
|
}
|
|
<span id='Ext-Class-method-constructor'> /**
|
</span> * @method constructor
|
* Create a new anonymous class.
|
*
|
* @param {Object} data An object represent the properties of this class
|
* @param {Function} onCreated Optional, the callback function to be executed when this class is fully created.
|
* Note that the creation process can be asynchronous depending on the pre-processors used.
|
*
|
* @return {Ext.Base} The newly created class
|
*/
|
Ext.Class = ExtClass = function(Class, data, onCreated) {
|
if (typeof Class != 'function') {
|
onCreated = data;
|
data = Class;
|
Class = null;
|
}
|
|
if (!data) {
|
data = {};
|
}
|
|
Class = ExtClass.create(Class, data);
|
|
ExtClass.process(Class, data, onCreated);
|
|
return Class;
|
};
|
|
Ext.apply(ExtClass, {
|
<span id='Ext-Class-method-onBeforeCreated'> /**
|
</span> * @private
|
*/
|
onBeforeCreated: function(Class, data, hooks) {
|
//<debug>
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '>> Ext.Class#onBeforeCreated', arguments);
|
//</debug>
|
|
Class.addMembers(data);
|
|
hooks.onCreated.call(Class, Class);
|
|
//<debug>
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, '<< Ext.Class#onBeforeCreated', arguments);
|
//</debug>
|
},
|
|
<span id='Ext-Class-method-create'> /**
|
</span> * @private
|
*/
|
create: function(Class, data) {
|
var name, i;
|
|
if (!Class) {
|
Class = makeCtor(
|
//<debug>
|
data.$className
|
//</debug>
|
);
|
}
|
|
for (i = 0; i < baseStaticMemberLength; i++) {
|
name = baseStaticMembers[i];
|
Class[name] = Base[name];
|
}
|
|
return Class;
|
},
|
|
<span id='Ext-Class-method-process'> /**
|
</span> * @private
|
*/
|
process: function(Class, data, onCreated) {
|
var preprocessorStack = data.preprocessors || ExtClass.defaultPreprocessors,
|
registeredPreprocessors = this.preprocessors,
|
hooks = {
|
onBeforeCreated: this.onBeforeCreated
|
},
|
preprocessors = [],
|
preprocessor, preprocessorsProperties,
|
i, ln, j, subLn, preprocessorProperty;
|
|
delete data.preprocessors;
|
|
for (i = 0,ln = preprocessorStack.length; i < ln; i++) {
|
preprocessor = preprocessorStack[i];
|
|
if (typeof preprocessor == 'string') {
|
preprocessor = registeredPreprocessors[preprocessor];
|
preprocessorsProperties = preprocessor.properties;
|
|
if (preprocessorsProperties === true) {
|
preprocessors.push(preprocessor.fn);
|
}
|
else if (preprocessorsProperties) {
|
for (j = 0,subLn = preprocessorsProperties.length; j < subLn; j++) {
|
preprocessorProperty = preprocessorsProperties[j];
|
|
if (data.hasOwnProperty(preprocessorProperty)) {
|
preprocessors.push(preprocessor.fn);
|
break;
|
}
|
}
|
}
|
}
|
else {
|
preprocessors.push(preprocessor);
|
}
|
}
|
|
hooks.onCreated = onCreated ? onCreated : Ext.emptyFn;
|
hooks.preprocessors = preprocessors;
|
|
this.doProcess(Class, data, hooks);
|
},
|
|
doProcess: function(Class, data, hooks) {
|
var me = this,
|
preprocessors = hooks.preprocessors,
|
preprocessor = preprocessors.shift(),
|
doProcess = me.doProcess;
|
|
for ( ; preprocessor ; preprocessor = preprocessors.shift()) {
|
// Returning false signifies an asynchronous preprocessor - it will call doProcess when we can continue
|
if (preprocessor.call(me, Class, data, hooks, doProcess) === false) {
|
return;
|
}
|
}
|
hooks.onBeforeCreated.apply(me, arguments);
|
},
|
|
<span id='Ext-Class-property-preprocessors'> /** @private */
|
</span> preprocessors: {},
|
|
<span id='Ext-Class-static-method-registerPreprocessor'> /**
|
</span> * Register a new pre-processor to be used during the class creation process
|
*
|
* @param {String} name The pre-processor's name
|
* @param {Function} fn The callback function to be executed. Typical format:
|
*
|
* function(cls, data, fn) {
|
* // Your code here
|
*
|
* // Execute this when the processing is finished.
|
* // Asynchronous processing is perfectly ok
|
* if (fn) {
|
* fn.call(this, cls, data);
|
* }
|
* });
|
*
|
* @param {Function} fn.cls The created class
|
* @param {Object} fn.data The set of properties passed in {@link Ext.Class} constructor
|
* @param {Function} fn.fn The callback function that **must** to be executed when this
|
* pre-processor finishes, regardless of whether the processing is synchronous or aynchronous.
|
* @return {Ext.Class} this
|
* @private
|
* @static
|
*/
|
registerPreprocessor: function(name, fn, properties, position, relativeTo) {
|
if (!position) {
|
position = 'last';
|
}
|
|
if (!properties) {
|
properties = [name];
|
}
|
|
this.preprocessors[name] = {
|
name: name,
|
properties: properties || false,
|
fn: fn
|
};
|
|
this.setDefaultPreprocessorPosition(name, position, relativeTo);
|
|
return this;
|
},
|
|
<span id='Ext-Class-static-method-getPreprocessor'> /**
|
</span> * Retrieve a pre-processor callback function by its name, which has been registered before
|
*
|
* @param {String} name
|
* @return {Function} preprocessor
|
* @private
|
* @static
|
*/
|
getPreprocessor: function(name) {
|
return this.preprocessors[name];
|
},
|
|
<span id='Ext-Class-method-getPreprocessors'> /**
|
</span> * @private
|
*/
|
getPreprocessors: function() {
|
return this.preprocessors;
|
},
|
|
<span id='Ext-Class-property-defaultPreprocessors'> /**
|
</span> * @private
|
*/
|
defaultPreprocessors: [],
|
|
<span id='Ext-Class-static-method-getDefaultPreprocessors'> /**
|
</span> * Retrieve the array stack of default pre-processors
|
* @return {Function[]} defaultPreprocessors
|
* @private
|
* @static
|
*/
|
getDefaultPreprocessors: function() {
|
return this.defaultPreprocessors;
|
},
|
|
<span id='Ext-Class-static-method-setDefaultPreprocessors'> /**
|
</span> * Set the default array stack of default pre-processors
|
*
|
* @private
|
* @param {Array} preprocessors
|
* @return {Ext.Class} this
|
* @static
|
*/
|
setDefaultPreprocessors: function(preprocessors) {
|
this.defaultPreprocessors = Ext.Array.from(preprocessors);
|
|
return this;
|
},
|
|
<span id='Ext-Class-static-method-setDefaultPreprocessorPosition'> /**
|
</span> * Insert this pre-processor at a specific position in the stack, optionally relative to
|
* any existing pre-processor. For example:
|
*
|
* Ext.Class.registerPreprocessor('debug', function(cls, data, fn) {
|
* // Your code here
|
*
|
* if (fn) {
|
* fn.call(this, cls, data);
|
* }
|
* }).setDefaultPreprocessorPosition('debug', 'last');
|
*
|
* @private
|
* @param {String} name The pre-processor name. Note that it needs to be registered with
|
* {@link Ext.Class#registerPreprocessor registerPreprocessor} 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.Class} this
|
* @static
|
*/
|
setDefaultPreprocessorPosition: function(name, offset, relativeName) {
|
var defaultPreprocessors = this.defaultPreprocessors,
|
index;
|
|
if (typeof offset == 'string') {
|
if (offset === 'first') {
|
defaultPreprocessors.unshift(name);
|
|
return this;
|
}
|
else if (offset === 'last') {
|
defaultPreprocessors.push(name);
|
|
return this;
|
}
|
|
offset = (offset === 'after') ? 1 : -1;
|
}
|
|
index = Ext.Array.indexOf(defaultPreprocessors, relativeName);
|
|
if (index !== -1) {
|
Ext.Array.splice(defaultPreprocessors, Math.max(0, index + offset), 0, name);
|
}
|
|
return this;
|
},
|
|
configNameCache: {},
|
|
getConfigNameMap: function(name) {
|
var cache = this.configNameCache,
|
map = cache[name],
|
capitalizedName;
|
|
if (!map) {
|
capitalizedName = name.charAt(0).toUpperCase() + name.substr(1);
|
|
map = cache[name] = {
|
internal: name,
|
initialized: '_is' + capitalizedName + 'Initialized',
|
apply: 'apply' + capitalizedName,
|
update: 'update' + capitalizedName,
|
'set': 'set' + capitalizedName,
|
'get': 'get' + capitalizedName,
|
doSet : 'doSet' + capitalizedName,
|
changeEvent: name.toLowerCase() + 'change'
|
};
|
}
|
|
return map;
|
}
|
});
|
|
<span id='Ext-Class-cfg-extend'> /**
|
</span> * @cfg {String} extend
|
* The parent class that this class extends. For example:
|
*
|
* Ext.define('Person', {
|
* say: function(text) { alert(text); }
|
* });
|
*
|
* Ext.define('Developer', {
|
* extend: 'Person',
|
* say: function(text) { this.callParent(["print "+text]); }
|
* });
|
*/
|
ExtClass.registerPreprocessor('extend', function(Class, data, hooks) {
|
//<debug>
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extendPreProcessor', arguments);
|
//</debug>
|
|
var Base = Ext.Base,
|
basePrototype = Base.prototype,
|
extend = data.extend,
|
Parent, parentPrototype, i;
|
|
delete data.extend;
|
|
if (extend && extend !== Object) {
|
Parent = extend;
|
}
|
else {
|
Parent = Base;
|
}
|
|
parentPrototype = Parent.prototype;
|
|
if (!Parent.$isClass) {
|
for (i in basePrototype) {
|
if (!parentPrototype[i]) {
|
parentPrototype[i] = basePrototype[i];
|
}
|
}
|
}
|
|
Class.extend(Parent);
|
|
Class.triggerExtended.apply(Class, arguments);
|
|
if (data.onClassExtended) {
|
Class.onExtended(data.onClassExtended, Class);
|
delete data.onClassExtended;
|
}
|
|
}, true);
|
|
//<feature classSystem.statics>
|
<span id='Ext-Class-cfg-statics'> /**
|
</span> * @cfg {Object} statics
|
* List of static methods for this class. For example:
|
*
|
* 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');
|
*/
|
ExtClass.registerPreprocessor('statics', function(Class, data) {
|
//<debug>
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#staticsPreprocessor', arguments);
|
//</debug>
|
|
Class.addStatics(data.statics);
|
|
delete data.statics;
|
});
|
//</feature>
|
|
//<feature classSystem.inheritableStatics>
|
<span id='Ext-Class-cfg-inheritableStatics'> /**
|
</span> * @cfg {Object} inheritableStatics
|
* List of inheritable static methods for this class.
|
* Otherwise just like {@link #statics} but subclasses inherit these methods.
|
*/
|
ExtClass.registerPreprocessor('inheritableStatics', function(Class, data) {
|
//<debug>
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#inheritableStaticsPreprocessor', arguments);
|
//</debug>
|
|
Class.addInheritableStatics(data.inheritableStatics);
|
|
delete data.inheritableStatics;
|
});
|
//</feature>
|
|
//<feature classSystem.config>
|
<span id='Ext-Class-cfg-config'> /**
|
</span> * @cfg {Object} config
|
* List of configuration options with their default values, for which automatically
|
* accessor methods are generated. For example:
|
*
|
* Ext.define('SmartPhone', {
|
* config: {
|
* hasTouchScreen: false,
|
* operatingSystem: 'Other',
|
* price: 500
|
* },
|
* constructor: function(cfg) {
|
* this.initConfig(cfg);
|
* }
|
* });
|
*
|
* var iPhone = new SmartPhone({
|
* hasTouchScreen: true,
|
* operatingSystem: 'iOS'
|
* });
|
*
|
* iPhone.getPrice(); // 500;
|
* iPhone.getOperatingSystem(); // 'iOS'
|
* iPhone.getHasTouchScreen(); // true;
|
*
|
* NOTE for when configs are reference types, the getter and setter methods do not make copies.
|
*
|
* For example, when a config value is set, the reference is stored on the instance. All instances that set
|
* the same reference type will share it.
|
*
|
* In the case of the getter, the value with either come from the prototype if the setter was never called or from
|
* the instance as the last value passed to the setter.
|
*
|
* For some config properties, the value passed to the setter is transformed prior to being stored on the instance.
|
*/
|
ExtClass.registerPreprocessor('config', function(Class, data) {
|
//<debug>
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#configPreProcessor', arguments);
|
//</debug>
|
|
var config = data.config,
|
prototype = Class.prototype;
|
|
delete data.config;
|
|
Ext.Object.each(config, function(name, value) {
|
var nameMap = ExtClass.getConfigNameMap(name),
|
internalName = nameMap.internal,
|
initializedName = nameMap.initialized,
|
applyName = nameMap.apply,
|
updateName = nameMap.update,
|
setName = nameMap.set,
|
getName = nameMap.get,
|
hasOwnSetter = (setName in prototype) || data.hasOwnProperty(setName),
|
hasOwnApplier = (applyName in prototype) || data.hasOwnProperty(applyName),
|
hasOwnUpdater = (updateName in prototype) || data.hasOwnProperty(updateName),
|
optimizedGetter, customGetter;
|
|
if (value === null || (!hasOwnSetter && !hasOwnApplier && !hasOwnUpdater)) {
|
prototype[internalName] = value;
|
prototype[initializedName] = true;
|
}
|
else {
|
prototype[initializedName] = false;
|
}
|
|
if (!hasOwnSetter) {
|
data[setName] = function(value) {
|
var oldValue = this[internalName],
|
applier = this[applyName],
|
updater = this[updateName];
|
|
if (!this[initializedName]) {
|
this[initializedName] = true;
|
}
|
|
if (applier) {
|
value = applier.call(this, value, oldValue);
|
}
|
|
if (typeof value != 'undefined') {
|
this[internalName] = value;
|
|
if (updater && value !== oldValue) {
|
updater.call(this, value, oldValue);
|
}
|
}
|
|
return this;
|
};
|
}
|
|
if (!(getName in prototype) || data.hasOwnProperty(getName)) {
|
customGetter = data[getName] || false;
|
|
if (customGetter) {
|
optimizedGetter = function() {
|
return customGetter.apply(this, arguments);
|
};
|
}
|
else {
|
optimizedGetter = function() {
|
return this[internalName];
|
};
|
}
|
|
data[getName] = function() {
|
var currentGetter;
|
|
if (!this[initializedName]) {
|
this[initializedName] = true;
|
this[setName](this.config[name]);
|
}
|
|
currentGetter = this[getName];
|
|
if ('$previous' in currentGetter) {
|
currentGetter.$previous = optimizedGetter;
|
}
|
else {
|
this[getName] = optimizedGetter;
|
}
|
|
return optimizedGetter.apply(this, arguments);
|
};
|
}
|
});
|
|
Class.addConfig(config, true);
|
});
|
//</feature>
|
|
//<feature classSystem.mixins>
|
<span id='Ext-Class-cfg-mixins'> /**
|
</span> * @cfg {String[]/Object} mixins
|
* List of classes to mix into this class. For example:
|
*
|
* Ext.define('CanSing', {
|
* sing: function() {
|
* alert("I'm on the highway to hell...")
|
* }
|
* });
|
*
|
* Ext.define('Musician', {
|
* mixins: ['CanSing']
|
* })
|
*
|
* In this case the Musician class will get a `sing` method from CanSing mixin.
|
*
|
* But what if the Musician already has a `sing` method? Or you want to mix
|
* in two classes, both of which define `sing`? In such a cases it's good
|
* to define mixins as an object, where you assign a name to each mixin:
|
*
|
* Ext.define('Musician', {
|
* mixins: {
|
* canSing: 'CanSing'
|
* },
|
*
|
* sing: function() {
|
* // delegate singing operation to mixin
|
* this.mixins.canSing.sing.call(this);
|
* }
|
* })
|
*
|
* In this case the `sing` method of Musician will overwrite the
|
* mixed in `sing` method. But you can access the original mixed in method
|
* through special `mixins` property.
|
*/
|
ExtClass.registerPreprocessor('mixins', function(Class, data, hooks) {
|
//<debug>
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor', arguments);
|
//</debug>
|
|
var mixins = data.mixins,
|
name, mixin, i, ln;
|
|
delete data.mixins;
|
|
Ext.Function.interceptBefore(hooks, 'onCreated', function() {
|
//<debug>
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#mixinsPreprocessor#beforeCreated', arguments);
|
//</debug>
|
|
if (mixins instanceof Array) {
|
for (i = 0,ln = mixins.length; i < ln; i++) {
|
mixin = mixins[i];
|
name = mixin.prototype.mixinId || mixin.$className;
|
|
Class.mixin(name, mixin);
|
}
|
}
|
else {
|
for (var mixinName in mixins) {
|
if (mixins.hasOwnProperty(mixinName)) {
|
Class.mixin(mixinName, mixins[mixinName]);
|
}
|
}
|
}
|
});
|
});
|
//</feature>
|
|
//<feature classSystem.backwardsCompatible>
|
// Backwards compatible
|
Ext.extend = function(Class, Parent, members) {
|
//<debug>
|
Ext.classSystemMonitor && Ext.classSystemMonitor(Class, 'Ext.Class#extend-backwards-compatible', arguments);
|
//</debug>
|
|
if (arguments.length === 2 && Ext.isObject(Parent)) {
|
members = Parent;
|
Parent = Class;
|
Class = null;
|
}
|
|
var cls;
|
|
if (!Parent) {
|
throw new Error("[Ext.extend] Attempting to extend from a class which has not been loaded on the page.");
|
}
|
|
members.extend = Parent;
|
members.preprocessors = [
|
'extend'
|
//<feature classSystem.statics>
|
,'statics'
|
//</feature>
|
//<feature classSystem.inheritableStatics>
|
,'inheritableStatics'
|
//</feature>
|
//<feature classSystem.mixins>
|
,'mixins'
|
//</feature>
|
//<feature classSystem.config>
|
,'config'
|
//</feature>
|
];
|
|
if (Class) {
|
cls = new ExtClass(Class, members);
|
// The 'constructor' is given as 'Class' but also needs to be on prototype
|
cls.prototype.constructor = Class;
|
} else {
|
cls = new ExtClass(members);
|
}
|
|
cls.prototype.override = function(o) {
|
for (var m in o) {
|
if (o.hasOwnProperty(m)) {
|
this[m] = o[m];
|
}
|
}
|
};
|
|
return cls;
|
};
|
//</feature>
|
}());
|
</pre>
|
</body>
|
</html>
|