//@tag foundation,core //@define Ext.ClassManager //@require Ext.Class /** * @class Ext.ClassManager * * @author Jacky Nguyen <[email protected]> * @aside guide class_system * @aside video class-system * * Ext.ClassManagerでは、すべてのクラスが管理され、フレームワーク全体において文字列クラス名から実クラスオブジェクトまでのマッピングが処理されます。アクセスは通常、直接ではなく、以下の便利なショートカットから行われます。 * * - {@link Ext#define Ext.define} * - {@link Ext.ClassManager#create Ext.create} * - {@link Ext#widget Ext.widget} * - {@link Ext#getClass Ext.getClass} * - {@link Ext#getClassName Ext.getClassName} * * ##基本的なシンタックス * * Ext.define(className, properties); * * `properties`がオブジェクトである場所では、クラスに適用するプロパティのコレクションを表しています。詳細は、{@link Ext.ClassManager#create}を参照してください。 * * @example * Ext.define('Person', { * name: 'Unknown', * * constructor: function(name) { * if (name) { * this.name = name; * } * * return this; * }, * * 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には、強力な{@link Ext.Class#registerPreprocessor プリプロセッサ}のセットが備わっており、継承、ミックスイン、設定、静的(これらのみではない)などのクラス作成に関係するすべてを処理します。 * * ##継承: * * Ext.define('Developer', { * extend: 'Person', * * constructor: function(name, isGeek) { * this.isGeek = isGeek; * * // Apply a method from the parent class' prototype * this.callParent([name]); * * return this; * * }, * * 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"); * * スーパークラスのメソッドの呼び出しについての詳細は、{@link Ext.Base#callParent}を参照してください。 * * ##ミックスイン: * * Ext.define('CanPlayGuitar', { * playGuitar: function() { * alert("F#...G...D...A"); * } * }); * * Ext.define('CanComposeSongs', { * composeSongs: function() { } * }); * * Ext.define('CanSing', { * sing: function() { * alert("I'm on the highway to hell..."); * } * }); * * 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("I'm on the highway to hell..."); * // alert("[Playing guitar at the same time...]"); * // alert("F#...G...D...A"); * * ##コンフィグ: * * Ext.define('SmartPhone', { * config: { * hasTouchScreen: false, * operatingSystem: 'Other', * price: 500 * }, * * isExpensive: false, * * constructor: function(config) { * this.initConfig(config); * * return this; * }, * * 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' * * ##スタティック: * * 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'); * * また、クラスメソッド内の静的プロパティへのアクセスについての詳細は、{@link Ext.Base#statics}および{@link Ext.Base#self}を参照してください。 * * @singleton */ (function(Class, alias, arraySlice, arrayFrom, global) { //<if nonBrowser> var isNonBrowser = typeof window == 'undefined'; //</if> var Manager = Ext.ClassManager = { /** * @property classes * @type Object * ClassManagerを介して定義されたすべてのクラス。各キーは各クラスの名前で、各値は各クラスの参照となっています。 * @private */ classes: {}, /** * @private */ existCache: {}, /** * @private */ namespaceRewrites: [{ from: 'Ext.', to: Ext }], /** * @private */ maps: { alternateToName: {}, aliasToName: {}, nameToAliases: {}, nameToAlternates: {} }, /** @private */ enableNamespaceParseCache: true, /** @private */ namespaceParseCache: {}, /** @private */ instantiators: [], /** * クラスがすでに作成されているかチェックします。 * * @param {String} className * @return {Boolean} exist */ isCreated: function(className) { var existCache = this.existCache, i, ln, part, root, parts; //<debug error> 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 (this.classes[className] || existCache[className]) { return true; } root = global; parts = this.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]; } } existCache[className] = true; this.triggerCreated(className); return true; }, /** * @private */ createdListeners: [], /** * @private */ nameCreatedListeners: {}, /** * @private */ triggerCreated: function(className) { var listeners = this.createdListeners, nameListeners = this.nameCreatedListeners, alternateNames = this.maps.nameToAlternates[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); } if (alternateNames) { names.push.apply(names, alternateNames); } 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]; } } }, /** * @private */ onCreated: function(fn, scope, className) { var listeners = this.createdListeners, nameListeners = this.nameCreatedListeners, 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); } }, /** * 名前空間の書き換えをサポートします。 * @private */ parseNamespace: function(namespace) { //<debug error> if (typeof namespace != 'string') { throw new Error("[Ext.ClassManager] Invalid namespace, must be a string"); } //</debug> var cache = this.namespaceParseCache; if (this.enableNamespaceParseCache) { if (cache.hasOwnProperty(namespace)) { return cache[namespace]; } } var parts = [], rewrites = this.namespaceRewrites, root = global, name = namespace, rewrite, from, to, i, ln; 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; }, /** * 名前空間を作成し、作成したオブジェクトに`value`を代入します。 * * Ext.ClassManager.setNamespace('MyCompany.pkg.Example', someObject); * alert(MyCompany.pkg.Example === someObject); // alerts true * * @param {String} name * @param {Mixed} 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]; }, /** * 新しいExt.nsは、名前空間の書き直しをサポートします。 * @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; }, /** * クラスに名前の参照を設定します。 * * @param {String} name * @param {Object} value * @return {Ext.ClassManager} this */ set: function(name, value) { var me = this, maps = me.maps, nameToAlternates = maps.nameToAlternates, targetName = me.getName(value), alternates; me.classes[name] = me.setNamespace(name, value); if (targetName && targetName !== name) { maps.alternateToName[name] = targetName; alternates = nameToAlternates[targetName] || (nameToAlternates[targetName] = []); alternates.push(name); } return this; }, /** * その名前のクラスを取得します。 * * @param {String} name * @return {Ext.Class} クラス */ get: function(name) { var classes = this.classes; if (classes[name]) { return classes[name]; } var root = global, parts = this.parseNamespace(name), part, i, ln; 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; }, /** * クラスにエイリアス名を登録します。 * * @param {Ext.Class/String} cls クラスの参照、または、`className` * @param {String} alias このクラスの参照として使用するエイリアス名 */ setAlias: function(cls, alias) { var aliasToNameMap = this.maps.aliasToName, nameToAliasesMap = this.maps.nameToAliases, className; if (typeof cls == 'string') { className = cls; } else { className = this.getName(cls); } if (alias && aliasToNameMap[alias] !== className) { //<debug info> if (aliasToNameMap[alias]) { Ext.Logger.info("[Ext.ClassManager] Overriding existing alias: '" + alias + "' " + "of: '" + aliasToNameMap[alias] + "' with: '" + className + "'. Be sure it's intentional."); } //</debug> aliasToNameMap[alias] = className; } if (!nameToAliasesMap[className]) { nameToAliasesMap[className] = []; } if (alias) { Ext.Array.include(nameToAliasesMap[className], alias); } return this; }, /** * クラス名のバッチをエイリアスのマッピングに追加します。 * @param {Object} aliases className形式のマッピングのセット:[値...] */ addNameAliasMappings: function(aliases){ var aliasToNameMap = this.maps.aliasToName, nameToAliasesMap = this.maps.nameToAliases, className, aliasList, alias, i; for (className in aliases) { aliasList = nameToAliasesMap[className] || (nameToAliasesMap[className] = []); for (i = 0; i < aliases[className].length; i++) { alias = aliases[className][i]; if (!aliasToNameMap[alias]) { aliasToNameMap[alias] = className; aliasList.push(alias); } } } return this; }, /** * * @param {Object} alternates className形式のマッピングのセット:[値...] */ addNameAlternateMappings: function(alternates) { var alternateToName = this.maps.alternateToName, nameToAlternates = this.maps.nameToAlternates, className, aliasList, alternate, i; for (className in alternates) { aliasList = nameToAlternates[className] || (nameToAlternates[className] = []); for (i = 0; i < alternates[className].length; i++) { alternate = alternates[className]; if (!alternateToName[alternate]) { alternateToName[alternate] = className; aliasList.push(alternate); } } } return this; }, /** * エイリアスからクラスの参照を取得します。 * * @param {String} alias * @return {Ext.Class} クラス */ getByAlias: function(alias) { return this.get(this.getNameByAlias(alias)); }, /** * エイリアスからクラスの名前を取得します。 * * @param {String} alias * @return {String} className */ getNameByAlias: function(alias) { return this.maps.aliasToName[alias] || ''; }, /** * クラスの代替名からクラスの名前を取得します。 * * @param {String} alternate * @return {String} className */ getNameByAlternate: function(alternate) { return this.maps.alternateToName[alternate] || ''; }, /** * クラス名からクラスの別名を取得します。 * * @param {String} name * @return {Array} エイリアス */ getAliasesByName: function(name) { return this.maps.nameToAliases[name] || []; }, /** * その参照やインスタンスからクラスの名前を取得します。 通常、省略形の{@link Ext#getClassName 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 || ''; }, /** * 提供されたオブジェクトのクラスを取得します。Ext.defineによって作成された任意のクラスのインスタンスではない場合、nullを返します。通常は、省略形の{@link Ext#getClass Ext.getClass}によって呼び出されます。 * * var component = new Ext.Component(); * * Ext.ClassManager.getClass(component); // returns Ext.Component * * @param {Object} object * @return {Ext.Class} クラス */ getClass: function(object) { return object && object.self || null; }, /** * @private */ create: function(className, data, createdFn) { //<debug error> if (typeof className != 'string') { throw new Error("[Ext.define] Invalid class name '" + className + "' specified, must be a non-empty string"); } //</debug> data.$className = className; return new Class(data, function() { var postprocessorStack = data.postprocessors || Manager.defaultPostprocessors, registeredPostprocessors = Manager.postprocessors, index = 0, postprocessors = [], postprocessor, process, 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); } } process = function(clsName, cls, clsData) { postprocessor = postprocessors[index++]; if (!postprocessor) { Manager.set(className, cls); if (createdFn) { createdFn.call(cls, cls); } Manager.triggerCreated(className); return; } if (postprocessor.call(this, clsName, cls, clsData, process) !== false) { process.apply(this, arguments); } }; process.call(Manager, className, this, data); }); }, createOverride: function(className, data, createdFn) { var overriddenClassName = data.override, requires = Ext.Array.from(data.requires); delete data.override; delete data.requires; this.existCache[className] = true; Ext.require(requires, function() { // Override the target class right after it's created this.onCreated(function() { var overridenClass = this.get(overriddenClassName); if (overridenClass.singleton) { overridenClass.self.override(data); } else { overridenClass.override(data); } if (createdFn) { createdFn.call(overridenClass, overridenClass); } // This push the overridding file itself into Ext.Loader.history // Hence if the target class never exists, the overriding file will // never be included in the build this.triggerCreated(className); }, this, overriddenClassName); }, this); return this; }, /** * クラスをエイリアスからインスタンス化します。通常は簡便な短縮形の{@link Ext#createByAlias Ext.createByAlias}を使って呼び出されます。クラスがまだ定義されおらず、{@link Ext.Loader}が{@link Ext.Loader#setConfig 有効}な場合には、同期ローディングでクラスのロードを試みます。 * * var window = Ext.ClassManager.instantiateByAlias('widget.window', { width: 600, height: 800 }); * * @param {String} alias * @param {Mixed...} args クラスのコンストラクタにエイリアスが渡された後の、追加の引数。 * @return {Object} instance */ instantiateByAlias: function() { var alias = arguments[0], args = arraySlice.call(arguments), className = this.getNameByAlias(alias); if (!className) { className = this.maps.aliasToName[alias]; //<debug error> if (!className) { throw new Error("[Ext.createByAlias] Cannot create an instance of unrecognized alias: " + alias); } //</debug> //<debug warn> Ext.Logger.warn("[Ext.Loader] Synchronously loading '" + className + "'; consider adding " + "Ext.require('" + alias + "') above Ext.onReady"); //</debug> Ext.syncRequire(className); } args[0] = className; return this.instantiate.apply(this, args); }, /** * 完全な名前、エイリアス、別名のいずれかから、クラスをインスタンス化します。通常は簡便な短縮形{@link Ext.ClassManager#create Ext.create}で呼び出されます。 * * {@link Ext.Loader}が{@link Ext.Loader#setConfig 有効}で、クラスがまだ定義されていない場合、同期ローディング経由でクラスをロードします。 * * たとえば、これらの3行は同じ結果を返します: * * // alias * var formPanel = Ext.create('widget.formpanel', { width: 600, height: 800 }); * * // alternate name * var formPanel = Ext.create('Ext.form.FormPanel', { width: 600, height: 800 }); * * // full class name * var formPanel = Ext.create('Ext.form.Panel', { width: 600, height: 800 }); * * @param {String} name * @param {Mixed} args クラスのコンストラクタに名前が渡された後の、追加の引数。 * @return {Object} instance */ instantiate: function() { var name = arguments[0], args = arraySlice.call(arguments, 1), alias = name, possibleName, cls; if (typeof name != 'function') { //<debug error> 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> cls = this.get(name); } else { cls = name; } // No record of this class name, it's possibly an alias, so look it up if (!cls) { possibleName = this.getNameByAlias(name); if (possibleName) { name = possibleName; cls = this.get(name); } } // Still no record of this class name, it's possibly an alternate name, so look it up if (!cls) { possibleName = this.getNameByAlternate(name); if (possibleName) { name = possibleName; cls = this.get(name); } } // Still not existing at this point, try to load it via synchronous mode as the last resort if (!cls) { //<debug warn> //<if nonBrowser> !isNonBrowser && //</if> Ext.Logger.warn("[Ext.Loader] Synchronously loading '" + name + "'; consider adding '" + ((possibleName) ? alias : name) + "' explicitly as a require of the corresponding class"); //</debug> Ext.syncRequire(name); cls = this.get(name); } //<debug error> if (!cls) { throw new Error("[Ext.create] Cannot create an instance of unrecognized class name / alias: " + alias); } if (typeof cls != 'function') { throw new Error("[Ext.create] '" + name + "' is a singleton and cannot be instantiated"); } //</debug> return this.getInstantiator(args.length)(cls, args); }, /** * @private * @param {String} name * @param {Array} args */ dynInstantiate: function(name, args) { args = arrayFrom(args, true); args.unshift(name); return this.instantiate.apply(this, args); }, /** * @private * @param {Number} length */ getInstantiator: function(length) { var instantiators = this.instantiators, instantiator; instantiator = instantiators[length]; if (!instantiator) { var 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.displayName = "Ext.ClassManager.instantiate" + length; //</debug> } return instantiator; }, /** * @private */ postprocessors: {}, /** * @private */ defaultPostprocessors: [], /** * post-processor関数を登録します。 * @private */ 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; }, /** * すべてのクラスに適用される、デフォルトのpost-processor配列スタックを設定します。 * * @private * @param {String/Array} postprocessors 登録されているポストプロセッサの名前、または、登録された名前の配列。 * @return {Ext.ClassManager} this */ setDefaultPostprocessors: function(postprocessors) { this.defaultPostprocessors = arrayFrom(postprocessors); return this; }, /** * スタック内の特定の位置にこのpost-processorを挿入します。 必要に応じて、既存のpost-processorからの相対位置を指定できます。 * * @private * @param {String} name ポストプロセッサの名前。これに先立って、{@link Ext.ClassManager#registerPostprocessor}に登録しておく必要があります。 * @param {String} offset 挿入位置。4つの使用可能な値:'first' 、'last'、または:'before'、'after'(3番目の引数内に用意されている名前に関連する) * @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; }, /** * 文字列式を一致するクラス名を配列の配列に変換します。式は、クラス名、またはエイリアス名を指すことができます。式はワイルドカードをサポートしています: * * // returns ['Ext.window.Window'] * var window = Ext.ClassManager.getNamesByExpression('widget.window'); * * // returns ['widget.panel', 'widget.window', ...] * var allWidgets = Ext.ClassManager.getNamesByExpression('widget.*'); * * // returns ['Ext.data.Store', 'Ext.data.ArrayProxy', ...] * var allData = Ext.ClassManager.getNamesByExpression('Ext.data.*'); * * @param {String} expression * @return {Array} クラス名 */ getNamesByExpression: function(expression) { var nameToAliasesMap = this.maps.nameToAliases, names = [], name, alias, aliases, possibleName, regex, i, ln; //<debug error> if (typeof expression != 'string' || expression.length < 1) { throw new Error("[Ext.ClassManager.getNamesByExpression] Expression " + expression + " is invalid, must be a non-empty string"); } //</debug> if (expression.indexOf('*') !== -1) { expression = expression.replace(/\*/g, '(.*?)'); regex = new RegExp('^' + expression + '$'); for (name in nameToAliasesMap) { if (nameToAliasesMap.hasOwnProperty(name)) { aliases = nameToAliasesMap[name]; if (name.search(regex) !== -1) { names.push(name); } else { for (i = 0, ln = aliases.length; i < ln; i++) { alias = aliases[i]; if (alias.search(regex) !== -1) { names.push(name); break; } } } } } } else { possibleName = this.getNameByAlias(expression); if (possibleName) { names.push(possibleName); } else { possibleName = this.getNameByAlternate(expression); if (possibleName) { names.push(possibleName); } else { names.push(expression); } } } return names; } }; //<feature classSystem.alias> /** * @cfg {String[]} alias * @member Ext.Class * クラス名の短いエイリアス一覧を設定します。ウィジェットのためにxtypeを定義する場合に最も役立ちます。 * * Ext.define('MyApp.CoolPanel', { * extend: 'Ext.panel.Panel', * alias: ['widget.coolpanel'], * * config: { * html : 'Yeah!' * } * }); * * // Using Ext.create * Ext.create('widget.coolpanel'); * * // Using the shorthand for widgets and in xtypes * Ext.widget('panel', { * items: [ * {xtype: 'coolpanel', html: 'Foo'}, * {xtype: 'coolpanel', html: 'Bar'} * ] * }); * * {@link Ext.Component}の場合には、{@link Ext.Component#xtype}プロパティも利用できます。 */ /** * @cfg {String[]} xtype * @member Ext.Component * {@link Ext.Component}のxtypeのリスト。xTypeにはピリオドを入れないで下さい。 * * Ext.define('MyApp.CoolPanel', { * extend: 'Ext.panel.Panel', * xtype: 'coolpanel', * * config: { * html : 'Yeah!' * } * }); * * // Using Ext.create * Ext.create('widget.coolpanel'); * * // Using the shorthand for widgets and in xtypes * Ext.widget('panel', { * items: [ * {xtype: 'coolpanel', html: 'Foo'}, * {xtype: 'coolpanel', html: 'Bar'} * ] * }); */ Manager.registerPostprocessor('alias', function(name, cls, data) { var aliases = data.alias, i, ln; for (i = 0,ln = aliases.length; i < ln; i++) { alias = aliases[i]; this.setAlias(cls, alias); } }, ['xtype', 'alias']); //</feature> //<feature classSystem.singleton> /** * @cfg {Boolean} singleton * @member Ext.Class * trueを設定すると、そのクラスはシングルトンとしてインスタンス化されます。例えば、 * * Ext.define('Logger', { * singleton: true, * log: function(msg) { * console.log(msg); * } * }); * * Logger.log('Hello'); */ Manager.registerPostprocessor('singleton', function(name, cls, data, fn) { fn.call(this, name, new cls(), data); return false; }); //</feature> //<feature classSystem.alternateClassName> /** * @cfg {String/String[]} alternateClassName * @member Ext.Class * このクラスの代替名を定義します。例えば、 * * @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) { 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 error> 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> Ext.apply(Ext, { /** * クラス名や、エイリアス、変数の定義によりクラスをインスタンス化します。 * * {@link Ext.Loader}が{@link Ext.Loader#setConfig 有効}で、クラスがまだ定義されていない場合、同期ローディング経由でクラスをロードします。 * * たとえば、これらの3行は同じ結果を返します: * * // alias * var formPanel = Ext.create('widget.formpanel', { width: 600, height: 800 }); * * // alternate name * var formPanel = Ext.create('Ext.form.FormPanel', { width: 600, height: 800 }); * * // full class name * var formPanel = Ext.create('Ext.form.Panel', { width: 600, height: 800 }); * * @param {String} name * @param {Mixed} args クラスのコンストラクタに名前が渡された後の、追加の引数。 * @return {Object} instance * @member Ext */ create: alias(Manager, 'instantiate'), /** * xtypeからウィジェットを作成するための簡便な短縮形。{@link Ext.ClassManager#instantiateByAlias}を参照してください。 * * var button = Ext.widget('button'); // Equivalent to Ext.create('widget.button') * var panel = Ext.widget('panel'); // Equivalent to Ext.create('widget.panel') * * @member Ext * @method widget * @param {String} name * @return {Object} instance */ widget: function(name) { var args = arraySlice.call(arguments); args[0] = 'widget.' + name; return Manager.instantiateByAlias.apply(Manager, args); }, /** * 簡便な短縮形。{@link Ext.ClassManager#instantiateByAlias}を参照してください。 * @member Ext * @method createByAlias * @param {String} alias * @param {Mixed...} args クラスのコンストラクタにエイリアスが渡された後の、追加の引数。 * @return {Object} instance */ createByAlias: alias(Manager, 'instantiateByAlias'), /** * クラスまたはオーバライドを定義します。基底クラスは以下のように定義します。 * * Ext.define('My.awesome.Class', { * someProperty: 'something', * * someMethod: function(s) { * console.log(s + this.someProperty); * } * }); * * var obj = new My.awesome.Class(); * * obj.someMethod('Say '); // logs 'Say something' to the console * * オーバーライドを定義するには、`override`プロパティをインクルードします。オーバーライドのコンテンツを拡張、またはそのクラスを変更するために、指定したクラスで統合されます。これは既定のプロパティ値を設定することができます。またはメソッドを拡張または置換することができます。これは、クラスの静的変数を拡張することもできます。 * * オーバーライドは大きなクラスを管理しやすいように作り直すために使うのも1つです。 * * // 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 * // ... * } * }); * * 他に、オーバーライドは独立でrequireできるクラスのオプションパーツを提供することに使います。この場合、クラスが完全にオーバーライドしたということが認識できないかもしれません。 * * Ext.define('My.ux.CoolTip', { * override: 'Ext.tip.ToolTip', * * constructor: function (config) { * this.callParent(arguments); // calls Ext.tip.ToolTip's constructor * // ... * } * }); * * 上のオーバーライドを、リクエストすることもできます。 * * Ext.define('My.app.App', { * requires: [ * 'My.ux.CoolTip' * ] * }); * * オーバーライドには静的メソッドなどを含ませることもできます。 * * Ext.define('My.app.BarMod', { * override: 'Ext.foo.Bar', * * statics: { * method: function (x) { * return this.callParent([x * 2]); // call Ext.foo.Bar.method * } * } * }); * * 重要: オーバーライドがビルドに含まれるのは、オーバーライド対象のクラスが必要な場合だけです。それ以外の場合は、ターゲット クラスのような上書きは含まれていません。 * * @param {String} className 以下のように、ドット名前空間形式の文字列で作成されたクラス名。'My.very.awesome.Class', 'FeedViewer.plugin.CoolPager' * * 以下の簡単な規約に従うことを強く推奨します。 * - ルートおよびクラス名は'CamelCased'です * - それ以外はすべて小文字 * * @param {Object} data このクラスに適用するプロパティのキーと値のペアです。プロパティ名は、以下に表記されているもの以外のすべての有効な文字列に属することができます。 * * - `mixins` * - `statics` * - `config` * - `alias` * - `xtype`({@link Ext.Component}のみ) * - `self` * - `singleton` * - `alternateClassName` * - `override` * * @param {Function} [createdFn] クラス(またはオーバーライド)の生成後に実行されるオプションのコールバック。実行スコープ(`this`)は、新たに生成されたクラス自体。 * @return {Ext.Base} * * @member Ext * @method define */ define: function (className, data, createdFn) { if ('override' in data) { return Manager.createOverride.apply(Manager, arguments); } return Manager.create.apply(Manager, arguments); }, /** * {@link Ext.ClassManager#getName}の簡便な短縮形。 * @member Ext * @method getClassName * @inheritdoc Ext.ClassManager#getName */ getClassName: alias(Manager, 'getName'), /** * オブジェクトの表示名を返します。この名前は、以下の場所から順番に検索されます。 * * - オブジェクトの`displayName`フィールド。 * - オブジェクトの`$name`および`$class` フィールド。 * - オブジェクトの'$className`フィールド。 * * このメソッドは、{@link Ext.Logger#log}がオブジェクトに関する情報を表示する際に使われます。 * * @param {Mixed} [object] 表示名を取得するオブジェクト * @return {String} 取得した表示名。見つからない場合には"Anonymous"。 * @member Ext */ 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'; }, /** * 簡便な短縮形。{@link Ext.ClassManager#getClass}を参照してください。 * @member Ext * @method getClass */ getClass: alias(Manager, 'getClass'), /** * 変数やクラスのための名前空間(namespace)を生成します。 名前空間内の変数やクラスはグローバルではありません。namespaceの最後のノードを指定した場合、その途中の全てのノードを暗黙的に生成します。使用例: * * Ext.namespace('Company', 'Company.data'); * * // equivalent and preferable to the above syntax * Ext.namespace('Company.data'); * * Company.Widget = function() { * // ... * }; * * Company.data.CustomStore = function(config) { * // ... * }; * * @param {String} namespace1 * @param {String} namespace2 * @param {String} etc * @return {Object} 名前空間オブジェクト渡された引数が複数ある場合には、最後に生成された名前空間オブジェクト。 * @member Ext * @method namespace */ namespace: alias(Manager, 'createNamespaces') }); /** * {@link Ext#widget}の旧名。 * @deprecated 2.0.0 代わりに{@link Ext#widget}を使用してください。 * @method createWidget * @member Ext */ Ext.createWidget = Ext.widget; /** * {@link Ext#namespace Ext.namespace}用の便利なエイリアス。 * @member Ext * @method ns */ Ext.ns = Ext.namespace; Class.registerPreprocessor('className', function(cls, data) { if (data.$className) { cls.$className = data.$className; //<debug> cls.displayName = cls.$className; //</debug> } }, true, 'first'); Class.registerPreprocessor('alias', function(cls, data) { 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 error> 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() { 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 error> 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']); })(Ext.Class, Ext.Function.alias, Array.prototype.slice, Ext.Array.from, Ext.global);