    /** * A Profile represents a range of devices that fall under a common category. For the vast majority of apps that use * device profiles, the app defines a Phone profile and a Tablet profile. Doing this enables you to easily customize * the experience for the different sized screens offered by those device types. * * Only one Profile can be active at a time, and each Profile defines a simple {@link #isActive} function that should * return either true or false. The first Profile to return true from its isActive function is set as your Application's * {@link Ext.app.Application#currentProfile current profile}. * * A Profile can define any number of {@link #models}, {@link #views}, {@link #controllers} and {@link #stores} which * will be loaded if the Profile is activated. It can also define a {@link #launch} function that will be called after * all of its dependencies have been loaded, just before the {@link Ext.app.Application#launch application launch} * function is called. * * ## Sample Usage * * First you need to tell your Application about your Profile(s): * *     Ext.application({ *         name: 'MyApp', *         profiles: ['Phone', 'Tablet'] *     }); * * This will load app/profile/Phone.js and app/profile/Tablet.js. Here's how we might define the Phone profile: * *     Ext.define('MyApp.profile.Phone', { *         extend: 'Ext.app.Profile', * *         views: ['Main'], * *         isActive: function() { *             return Ext.os.is('Phone'); *         } *     }); * * The isActive function returns true if we detect that we are running on a phone device. If that is the case the * Application will set this Profile active and load the 'Main' view specified in the Profile's {@link #views} config. * * ## Class Specializations * * Because Profiles are specializations of an application, all of the models, views, controllers and stores defined * in a Profile are expected to be namespaced under the name of the Profile. Here's an expanded form of the example * above: * *     Ext.define('MyApp.profile.Phone', { *         extend: 'Ext.app.Profile', * *         views: ['Main'], *         controllers: ['Signup'], *         models: ['MyApp.model.Group'], * *         isActive: function() { *             return Ext.os.is('Phone'); *         } *     }); * * In this case, the Profile is going to load *app/view/phone/Main.js*, *app/controller/phone/Signup.js* and * *app/model/Group.js*. Notice that in each of the first two cases the name of the profile ('phone' in this case) was * injected into the class names. In the third case we specified the full Model name (for Group) so the Profile name * was not injected. * * For a fuller understanding of the ideas behind Profiles and how best to use them in your app, we suggest you read * the [device profiles guide](/touch/2.4/core_concepts/device_profiles.html). *  */Ext.define('Ext.app.Profile', {    mixins: [        'Ext.mixin.Observable'    ],     /**     * @property {Boolean}     * `true` to identify an object as an instance of `Ext.app.Profile`     */    isProfile: true,     /**     * @cfg {String} [namespace]     * The namespace that this Profile's classes can be found in. Defaults to the lowercase     * Profile {@link #name}, for example a Profile called MyApp.profile.Phone will by default have a 'phone'     * namespace, which means that this Profile's additional models, stores, views and controllers will be loaded     * from the MyApp.model.phone.*, MyApp.store.phone.*, MyApp.view.phone.* and MyApp.controller.phone.* namespaces     * respectively.     * @accessor     */     /**     * @cfg {String} [name]     * The name of this Profile. Defaults to the last section of the class name (e.g. a profile     * called MyApp.profile.Phone will default the name to 'Phone').     * @accessor     */     config: {        /**         * @cfg {String} mainView          */        mainView: {            $value: null,            lazy: true        },         /**         * @cfg {Ext.app.Application} application         * The {@link Ext.app.Application Application} instance to which this Profile is         * bound. This is set automatically.         * @accessor         * @readonly         */        application: null,         // @cmd-auto-dependency {aliasPrefix: "controller.", profile: true, blame: "all"}         /**         * @cfg {String[]} controllers         * Any additional {@link Ext.app.Controller Controllers} to load for this profile.         * Note that each item here will be prepended with the Profile namespace when loaded.         *         * Example usage:         *         *     controllers: [         *         'Users',         *         'MyApp.controller.Products'         *     ]         *         * This will load *MyApp.controller.tablet.Users* and *MyApp.controller.Products*.         * @accessor         */        controllers: [],         // @cmd-auto-dependency {aliasPrefix : "model.", profile: true, blame: "all"}         /**         * @cfg {String[]} models         * Any additional {@link Ext.app.Application#models Models} to load for this profile.         * Note that each item here will be prepended with the Profile namespace when loaded.         *         * Example usage:         *         *     models: [         *         'Group',         *         'MyApp.model.User'         *     ]         *         * This will load *MyApp.model.tablet.Group* and *MyApp.model.User*.         * @accessor         */        models: [],         // @cmd-auto-dependency {aliasPrefix: "view.", profile: true, isKeyedObject:true, blame: "all" }         /**         * @cfg {Object/String[]} views         * This config allows the active profile to define a set of `xtypes` and map them         * to desired classes and default configurations. Normally an `xtype` is statically         * declared by a {@link Ext.Component component} in its class definition. This         * mechanism allows the active profile to control a set of these types.         *         * Example:         *         *      views: {         *          // The "main" xtype maps to MyApp.view.tablet.Main         *          //         *          main: 'MyApp.view.tablet.Main',         *         *          // The "inbox" xtype maps to a subclass of MyApp.view.Inbox (created         *          // by this mechanism) that sets the "mode" config to "compact".         *          //         *          inbox: {         *              xclass: 'MyApp.view.Inbox',         *              mode: 'compact'         *          }         *      }         *         * Note that class names used in this form must be full class names, unlike the         * historical usage of `views`. Further, these views cannot be accessed using the         * `getView` method but rather via their assigned `xtype`.         *         * The historical usage of this config is enabled when an array is passed. In this         * case, these are simply additional {@link Ext.app.Application#views views} to         * load for this profile. Note that each item here will be prepended with the         * Profile namespace when loaded.         *         * Example usage:         *         *     views: [         *         'Main',         *         'MyApp.view.Login'         *     ]         *         * This will load *MyApp.view.tablet.Main* and *MyApp.view.Login*. While supported,         * this usage is discouraged in favor of `xtype` mapping.         * @accessor         */        views: [],         // @cmd-auto-dependency {aliasPrefix: "store.", profile: true, blame: "all"}         /**         * @cfg {String[]} stores         * Any additional {@link Ext.app.Application#stores Stores} to load for this profile.         * Note that each item here will be prepended with the Profile namespace when loaded.         *         * Example usage:         *         *     stores: [         *         'Users',         *         'MyApp.store.Products'         *     ]         *         * This will load *MyApp.store.tablet.Users* and *MyApp.store.Products*.         * @accessor         */        stores: []    },     /**     * Creates a new Profile instance     */    constructor: function (config) {        this.initConfig(config);         this.mixins.observable.constructor.apply(this, arguments);    },     /**     * Determines whether or not this Profile is active on the device isActive is executed on. Should return true if     * this profile is meant to be active on this device, false otherwise. Each Profile should implement this function     * (the default implementation just returns false).     * @return {Boolean} True if this Profile should be activated on the device it is running on, false otherwise     */    isActive: function () {        return false;    },     /**     * This method is called once the profile is determined to be the active profile. This     * initialization is performed before controllers are initialized and therefore also     * before launch.     * @protected     * @since 6.0.1     */    init: function () {        var views = this.getViews(),            xtype;         if (views && !(views instanceof Array)) {            for (xtype in views) {                Ext.ClassManager.setXType(views[xtype], xtype);            }        }    },     /**     * @method     * The launch function is called by the {@link Ext.app.Application Application} if this Profile's {@link #isActive}     * function returned true. This is typically the best place to run any profile-specific app launch code. Example     * usage:     *     *     launch: function() {     *         Ext.create('MyApp.view.tablet.Main');     *     }     */    launch: Ext.emptyFn,     onClassExtended: function (cls, data, hooks) {        var onBeforeClassCreated = hooks.onBeforeCreated;         hooks.onBeforeCreated = function(cls, data) {            var Controller = Ext.app.Controller,                className = cls.$className,                requires = [],                proto = cls.prototype,                views = data.views,                name, namespace;             // Process name and namespace configs here since we need to use the namespace             // in the dependency calculation             name = data.name;            if (name) {                delete data.name;            } else {                name = className.split('.');                name = name[name.length - 1];            }            cls._name = name;             cls._namespace = name = (data.namespace || name).toLowerCase();            delete data.namespace;             namespace = Controller.resolveNamespace(cls, data);             Controller.processDependencies(proto, requires, namespace, 'model', data.models, name);            Controller.processDependencies(proto, requires, namespace, 'store', data.stores, name);            Controller.processDependencies(proto, requires, namespace, 'controller', data.controllers, name);             if (views) {                if (views instanceof Array) {                    Controller.processDependencies(proto, requires, namespace, 'view', views, name);                } else {                    Ext.app.Profile.processViews(className, views, requires);                }            }             Ext.require(requires, Ext.Function.pass(onBeforeClassCreated, arguments, this));        };    },     getName: function () {        // This used to be a Config but is now processed in onClassExtended so we provide         // the getter for compat.         return this.self._name;    },     getNamespace: function () {        // This used to be a Config but is now processed in onClassExtended so we provide         // the getter for compat.         return this.self._namespace;    },     privates: {        statics: {            processViews: function (className, views, requires) {                var body, cls, s, xtype;                 for (xtype in views) {                    cls = views[xtype];                     if (typeof cls !== 'string') {                        s = cls.xclass;                         //<debug>                         if (!s) {                            Ext.raise('Views must specify an xclass');                        }                        //</debug>                          body = Ext.apply({                            extend: s                        }, cls);                         delete body.xclass;                         // Class names will be App.profile.Tablet$inbox for example                         Ext.define(views[xtype] = className + '$' + xtype, body);                        cls = s;                    }                     requires.push(cls);                }            }        }    }});