Source: skin/ThemeParser.js

var Theme = require('./Theme');

/**
 * Load a theme from a .json file.
 *
 * @class Theme
 * @extends GOWN.Theme
 * @memberof GOWN
 * @constructor
 * @param jsonPath The path to the .json file {String}
 * @param [global=true] Set theme as the global GOWN.theme {bool}
 */
function ThemeParser(jsonPath, global) {
    Theme.call(this, global);

    /**
     * Components that show something and can be used as skin
     *
     * @see GOWN.shapes
     * @type Object
     */
    this.skinComponents = this.skinComponents || this.getSkinComponents();

    this.addThemeData(jsonPath);
}

ThemeParser.prototype = Object.create( Theme.prototype );
ThemeParser.prototype.constructor = ThemeParser;
module.exports = ThemeParser;

//TODO
ThemeParser.DATA_LOADED = 'data_loaded';

/**
 * Get the component classes that can create skins (in general all GOWN.shapes).
 * Note that image textures are not components
 *
 * @return Object
 */
ThemeParser.prototype.getSkinComponents = function () {
    var cmps = {};
    if (GOWN.shapes) {
        cmps.rect = GOWN.shapes.Rect;
        cmps.diamond = GOWN.shapes.Diamond;
        cmps.ellipse = GOWN.shapes.Ellipse;
        cmps.line = GOWN.shapes.Line;
    }
    return cmps;
};

/**
 * Executed when the image has been loaded.
 * Sets cache and and applies the theme.
 *
 * @see addImage
 * @see resource-loader https://github.com/englercj/resource-loader
 *
 * @param loader The loader {Loader}
 * @param resources The loaded resources {Object}
 */
ThemeParser.prototype.loadComplete = function(loader, resources) {
    this.setCache(resources);

    if (resources) {
        var res = resources[this._jsonPath];
        if (res) {
            this.themeData = res.data;
        }

        this.applyTheme();
        Theme.prototype.loadComplete.call(this, loader, resources);
    }
};

/**
 * @private
 */
ThemeParser.prototype.themeApplyTheme = Theme.prototype.applyTheme;

/**
 * Apply the theme to the controls
 * (normally executed only once after the texture has been loaded)
 */
ThemeParser.prototype.applyTheme = function() {
    if (!this.themeData) {
        return;
    }
    this.parseData(this.themeData);
    this.themeApplyTheme();
};

/**
 * Get the scale9 grid data from the theme data
 *
 * @param scale Rectangle position and size {Number[]}
 * @return {PIXI.Rectangle}
 */
ThemeParser.prototype.getScale9 = function(scale) {
    return new PIXI.Rectangle(
        parseInt(scale[0])*this.themeScale, parseInt(scale[1])*this.themeScale,
        parseInt(scale[2])*this.themeScale, parseInt(scale[3])*this.themeScale);
};

/**
 * Create a new skin from the theme data
 *
 * @param skinData The skin data {Object}
 * @param data The theme data {Object}
 * @returns {function} the skin function
 */
ThemeParser.prototype.skinFromData = function(skinData, data) {
    if (skinData.type === 'texture') {
        var scale9;
        if (skinData.scale9 && skinData.scale9 in data.grids) {
            scale9 = this.getScale9(data.grids[skinData.scale9]);
        } else {
            return this.getImage(skinData.texture);
        }
        if (!(skinData.texture in data.frames) && window.console) {
            window.console.error('texture not found in texture atlas: ' +
                skinData.texture + ' ' +
                'please check ' + this._jsonPath);
            return null;
        }

        return this.getScaleContainer(skinData.texture, scale9, skinData.middleWidth, skinData.centerHeight);
    } else if (skinData.type in this.skinComponents) {
        // keep component in scope
        var CmpClass = this.skinComponents[skinData.type];
        return function() {
            var cmp = new CmpClass();
            for (var key in skinData) {
                if (key === 'type') {
                    continue;
                }
                cmp[key] = skinData[key];
            }
            return cmp;
        };
    }
};

/**
 * Create a dictionary containing the skin data (including default values)
 *
 * @param stateName The name of the current state (e.g. GOWN.Button.UP) {String}
 * @param skinData The data gathered from previous runs {String}
 * @param data The new data that will be copied into skinData {Object}
 */
ThemeParser.prototype.getSkinData = function(stateName, skinData, data) {
    if (!data) {
        return;
    }

    var copyInto = function(source, target) {
        if (!source) {
            return;
        }
        for (var key in source) {
            target[key] = source[key];
        }
    };

    // get default skin for all states...
    copyInto(data.all, skinData);

    // ... override default values for current state
    copyInto(data[stateName], skinData);
};

/**
 * Parse the theme data
 *
 * @param data The theme data {Object}
 */
ThemeParser.prototype.parseData = function(data) {
    this.hoverSkin = data.hoverSkin;
    this.thumbSkin = data.thumbSkin;
    this.themeScale = data.themeScale || 1.0;

    if (data.textStyle) {
        this.textStyle.fill = data.textStyle.fill;
        this.textStyle.fontFamily = data.textStyle.fontFamily;
    }
    if (!data.skins) {
        return;
    }

    for (var componentName in data.skins) {
        if (componentName === 'default') {
            continue;
        }
        // create skin for componentName (e.g. button) from data

        var states = data.skins[componentName];
        //var skins = data.skins[componentName];
        for (var stateName in states) {
            if (stateName === 'all') {
                continue;
            }

            var skinData = {};
            // set defaults
            this.getSkinData(stateName, skinData, data.skins.default);

            // override defaults with component data
            if (componentName in data.skins) {
                this.getSkinData(stateName, skinData, data.skins[componentName]);
            }

            // create skin from skinData for current skin
            var skin = this.skinFromData(skinData, data);
            if (skin) {
                // skin.minWidth
                this.setSkin(componentName, stateName, skin);
            }
        }
    }
};

/**
 * Adds the theme data located at the specified path
 *
 * @param jsonPath The path the .json file
 */
ThemeParser.prototype.addThemeData = function(jsonPath) {
    this.addImage(jsonPath);
};