// @cmd.optimizer.requires.async
/**
 * This mixin allows users to easily require external scripts in their classes. This load
 * process delays application launch (`Ext.onReady`) until all such scripts are loaded
 * ensuring that your class will have access to its required scripts from the start.
 *
 * For example:
 *
 *      Ext.define('Feed', {
 *          mixins: ['Ext.mixin.Mashup'],
 *
 *          requiredScripts: [
 *              '//www.foo.com/code/bar.js' // preserve http/s
 *          ],
 *
 *          // The code in "bar.js" will be available at application launch
 *      });
 *
 * If your required script needs to be dynamic, for example it contains a key for an API, you can
 * use the {@link Ext.Manifest#mashup mashup} object.
 *
 * @since 5.0.0
 */
Ext.define('Ext.mixin.Mashup', function(Mashup) { return { // eslint-disable-line brace-style
    extend: 'Ext.Mixin',
 
    mixinConfig: {
        id: 'mashup',
 
        extended: function(baseClass, derivedClass) {
            Mashup.process(derivedClass);
        }
    },
 
    /**
     * @cfg {String/String[]} requiredScripts
     * Scripts that are required to be loaded in order for your Mashup to work.
     * These scripts can also use {@link Ext.Template template} syntax if there is data in the
     * {@link Ext.Manifest#mashup mashup} object for the class `xtype`.
     *
     * For example, `Ext.ux.google.Map` needs an API key and so its `requiredScripts` looks
     * like this:
     *
     *      requiredScripts: {
     *          '//maps.googleapis.com/maps/api/js?key={key}'
     *      }
     *
     * The `key` property is provided in `app.json` or directly in `Ext.manifest`:
     *
     *      "mashup": {
     *          "map": {  // the xtype
     *              "key": "[GOOGLE_MAPS_KEY]"
     *          }
     *      }
     *
     * For non-components (classes that have no `xtype`), the `mashupConfig` can be used
     * to supply the configuration key:
     *
     *      mashupConfig: {
     *          key: 'map'
     *      }
     */
 
    /**
     * @cfg {Object} mashup
     * An object that allows developers to pass values into templates used to populate
     * {@link Ext.Mashup#requiredScripts requiredScripts}.
     *
     * Here is an example of passing a key along to `Ext.ux.google.Map`.
     *
     *      "mashup": {
     *          "map": {  // the xtype
     *              "key": "[GOOGLE_MAPS_KEY]"
     *          }
     *      }
     *
     * @member Ext.Manifest
     */
 
    statics: {
        process: function(targetClass) {
            var body = targetClass.prototype,
                requiredScripts = body.requiredScripts,
                hooks = targetClass._classHooks,
                onCreated = hooks.onCreated,
                xtypes = targetClass.prototype.xtypes,
                mashup = Ext.manifest.mashup || {},
                options = body.mashupConfig,
                i, script;
 
            if (requiredScripts) {
                delete body.requiredScripts;
 
                hooks.onCreated = function() {
                    var me = this,
                        scripts = [],
                        args = Ext.Array.slice(arguments),
                        redirect = mashup.redirect || {};
 
                    requiredScripts = scripts.concat(requiredScripts);
 
                    options = options && mashup[options.key];
 
                    if (xtypes) {
                        for (= 0; !options && i < xtypes.length; ++i) {
                            options = mashup[xtypes[i]];
                        }
                    }
 
                    for (= 0; i < requiredScripts.length; i++) {
                        script = requiredScripts[i];
 
                        if (redirect[script] === false) {
                            continue;
                        }
 
                        script = redirect[script] || script;
 
                        if (script.indexOf('{') > -1) {
                            if (options) {
                                script = new Ext.Template(script).apply(options);
                            }
                            //<debug>
                            else {
                                Ext.log.error('Missing mashup options for ' +
                                    body.$className + ' script "' + script + '"');
                            }
                            //</debug>
                        }
 
                        scripts.push(script);
                    }
 
                    if (!scripts.length) {
                        hooks.onCreated = onCreated;
                        hooks.onCreated.call(me, args);
 
                        return;
                    }
 
                    Ext.Loader.loadScripts({
                        url: scripts,
                        cache: true, // no cache busting
                        onError: function(opts, error) {
                            targetClass.scriptError = targetClass.prototype.scriptError = error;
                            hooks.onCreated = onCreated;
                            hooks.onCreated.call(me, args);
                        },
                        onLoad: function() {
                            hooks.onCreated = onCreated;
                            hooks.onCreated.call(me, args);
                        }
                    });
                };
            }
        }
    },
 
    onClassMixedIn: function(targetClass) {
        Mashup.process(targetClass);
    }
};
});