Show:

File: src\node\class.node.button.js

/*
 * Copyright (c) 2012  Capgemini Technology Services (hereinafter “Capgemini”)
 *
 * License/Terms of Use
 *
 * Permission is hereby granted, free of charge and for the term of intellectual property rights on the Software, to any
 * person obtaining a copy of this software and associated documentation files (the "Software"), to use, copy, modify
 * and propagate free of charge, anywhere in the world, all or part of the Software subject to the following mandatory conditions:
 *
 *   •    The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 *
 *  Any failure to comply with the above shall automatically terminate the license and be construed as a breach of these
 *  Terms of Use causing significant harm to Capgemini.
 *
 *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
 *  WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
 *  OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
 *  TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 *
 *  Except as contained in this notice, the name of Capgemini shall not be used in advertising or otherwise to promote
 *  the use or other dealings in this Software without prior written authorization from Capgemini.
 *
 *  These Terms of Use are subject to French law.
 */

/**
 * List the modes for a button : NORMAL, OVER, DEACTIVATED, SELECTED.
 * @class CGSGButtonMode
 * @type {Object}
 * @author Gwennael Buchet (gwennael.buchet@gmail.com)
 * @example
 *      myTextNode.setWrapMode(CGSGWrapMode.WORD, true);
 */
var CGSGButtonMode = {
    /**
     * @property NORMAL
     */
    NORMAL:{index:0, isClickable:true},
    /**
     * @property OVER
     */
    OVER:{index:1, isClickable:true},
    /**
     * @property DEACTIVATED
     */
    DEACTIVATED:{index:2, isClickable:false},

    /**
     * @property SELECTED
     */
    SELECTED:{index:1, isClickable:true}
};


var CGSGPositionMode = {
    /**
     * @property TOP
     */
    TOP:{index:0, decalX:0, decalY:-1, dt:1, dy:1,
        computeWidth:function (item1, item2) {
            return Math.max(item1, item2);
        },
        computeHeight:function (item1, item2) {
            return 0;
        }},

    /**
     * @property BOTTOM
     */
    BOTTOM:{index:1, decalX:0, decalY:1, dt:0, dy:1,
        computeWidth:function (item1, item2) {
            return Math.max(item1, item2);
        },
        computeHeight:function (item1, item2) {
            return 0;
        }},
    /**
     * @property LEFT
     */
    LEFT:{index:2, decalX:-1, decalY:0, dt:0, dy:0,
        computeWidth:function (item1, item2) {
            return 0;
        },
        computeHeight:function (item1, item2) {
            return Math.max(item1, item2);
        }},
    /**
     * @property RIGHT
     */
    RIGHT:{index:3, decalX:1, decalY:0, dt:1, dy:0,
        computeWidth:function (item1, item2) {
            return 0;
        },
        computeHeight:function (item1, item2) {
            return Math.max(item1, item2);
        }}
};


/**
 * A CGSGNodeButton represent a basic square
 *
 * @class CGSGNodeButton
 * @module Node
 * @extends CGSGNode
 * @constructor
 * @param {Number} x Relative position on X
 * @param {Number} y Relative position on X
 * @param {String} text
 * @type {CGSGNodeButton}
 * @author Gwennael Buchet (gwennael.buchet@gmail.com)
 */
var CGSGNodeButton = CGSGNode.extend(
    {
        initialize:function (x, y, text) {
            this._super(x, y, 0, 0);

            /**
             * High colors for the button in 3 mode : normal, over, deactivated
             * @property _firstColors
             * @default ["#858585", "#5F5F5F", "#9C9C9C"]
             * @type {Array}
             * @private
             */
            this._firstColors = ["#969696", "#5F5F5F", "#D8D8D8"];
            /**
             * Low color for the button in 3 mode : normal, over, deactivated
             * @property _lastColors
             * @default ["#606060", "#4B4B4B", "#747474"]
             * @type {Array}
             * @private
             */
            this._lastColors = ["#7F7F7F", "#4B4B4B", "#B5B5B5"];

            /**
             * Shadow color for the button in 3 mode : normal, over, deactivated. Can be null.
             * @property _shadowColors
             * @default [null, null, null]
             * @type {Array}
             * @private
             */
            this._shadowColors = [null, null, null];

            /**
             * Radius for the round corner in 3 mode : normal, over, deactivated
             * @property _radiuses
             * @type {Array}
             * @default [10, 10, 10]
             * @private
             */
            this._radiuses = [8, 8, 8];

            /**
             * Text for the button in 3 mode : normal, over, deactivated
             * @property _texts
             * @default [text, text, text]
             * @type {Array}
             * @private
             */
            this._texts = [text, text, text];

            /**
             * Text size for the button in 3 mode : normal, over, deactivated
             * @property _textSizes
             * @default [12, 12, 12]
             * @type {Array}
             * @private
             */
            this._textSizes = [12, 12, 12];

            /**
             * Color for the text in the 3 mode  : normal, over, deactivated
             * @property _textColors
             * @type {Array}
             * @default ["white", "white", "gray"]
             * @private
             */
            this._textColors = ["white", "white", "#999999"];

            /**
             * Text Node encapsulating the text rendering
             * @property _textsNode
             * @type {CGSGNodeText}
             */
            this.textNode = new CGSGNodeText(0, 0, "");
            this.textNode.setTextAlign("center", false);
            this.textNode.setTextBaseline("middle", false);

            this._strokeColor = null;
            this._lineWidth = 2;

            /**
             * @property _picto
             * @type {CGSGNodeImage}
             */
            this._picto = new CGSGNodeImage(0, 0, null);
            /**
             * @property _slices
             * @type {Array}
             */
            this._slices = [];

            /**
             * @property _pictoPosition
             * @default CGSGPositionMode.LEFT
             * @type {CGSGPositionMode}
             * @private
             */
            this._pictoPosition = CGSGPositionMode.LEFT;

            /**
             * Distance between the picto and the text
             * @property _distancePictoText
             * @default 10
             * @type {Number}
             * @private
             */
            this._distancePictoText = 10;

            /**
             * Fake canvases to pre-render static display
             * @property _tmpCanvas
             * @type {Array}
             * @private
             */
            this._tmpCanvas =
                [document.createElement('canvas'), document.createElement('canvas'), document.createElement('canvas')];

            /**
             * Padding applied to the left and right of the text
             * @property _horizontalPadding
             * @type {Number}
             * @default 15
             * @private
             */
            this._horizontalPadding = 15;
            /**
             * Padding applied to the top and bottom of the text
             * @property _verticalPadding
             * @type {Number}
             * @default 10
             * @private
             */
            this._verticalPadding = 10;

            /**
             * Computed dimensions of the button in the 3 modes.
             * Do not edit manually!
             * @property _dimensions
             * @type {Array}
             * @private
             */
            this._dimensions = [];

            /**
             * @property classType
             * @readonly
             * @type {String}
             * @default "CGSGNodeButton"
             */
            this.classType = "CGSGNodeButton";

            this._initShapes();

            /**
             * Current mode of the button
             * @property _currentMode
             * @type {CGSGButtonMode}
             * @default CGSGButtonMode.NORMAL
             * @private
             */
            this._currentMode = CGSGButtonMode.NORMAL;

            this.setMode(CGSGButtonMode.NORMAL);

            var that = this;
            this.onMouseOver = function (event) {
                if (that.getMode() == CGSGButtonMode.NORMAL) {
                    that.setMode(CGSGButtonMode.OVER);
                }
            };

            this.onMouseOut = function (event) {
                if (that.getMode() == CGSGButtonMode.OVER) {
                    that.setMode(CGSGButtonMode.NORMAL);
                }
            };
        },

        /**
         * Change the position of the picto : CGSGPositionMode.LEFT, CGSGPositionMode.TOP, CGSGPositionMode.RIGHT, CGSGPositionMode.BOTTOM
         * @method setPictoPosition
         * @param {CGSGPositionMode} p
         */
        setPictoPosition:function (p) {
            this._pictoPosition = p;
            this._needRedraw = true;
        },

        /**
         * Set the image for the picto
         * @method setImage
         * @param {Image} img
         */
        setImage:function (img) {
            this._picto.setImage(img);
            this._needRedraw = true;
        },

        /**
         * Set the URL for the picto
         * @method setImageURL
         * @param url
         */
        setImageURL:function (url) {
            this._picto.onLoadEnd = this._onLoadImageEnd.bind(this);
            this._picto.setURL(url);
        },

        _onLoadImageEnd:function () {
            this._needRedraw = true;
        },

        /**
         * Return the slices in the image for the 3 modes
         * @method getSlices
         * @return {Array}
         */
        getSlices:function () {
            return this._slices;
        },
        /**
         * Set the slices in the image for the 3 modes
         * @method setSlices
         * @param {Array} values
         */
        setSlices:function (values) {
            this._slices = values;
            this._needRedraw = true;
        },

        /**
         * Return the High color for the button
         * @method getFirstColor
         * @return {Array}
         */
        getFirstColors:function () {
            return this._firstColors;
        },
        /**
         * Set the values for the high color of the button
         * @method setFirstColor
         * @param values {Array}
         */
        setFirstColors:function (values) {
            this._firstColors = values;
            this._needRedraw = true;
        },

        /**
         * Return the Low color for the button
         * @method getLastColor
         * @return {Array}
         */
        getLastColors:function () {
            return this._lastColors;
        },
        /**
         * Set the values for the low color of the button
         * @method setLastColor
         * @param values {Array}
         */
        setLastColors:function (values) {
            this._lastColors = values;
            this._needRedraw = true;
        },

        /**
         * Return the Shadow color for the button
         * @method getShadowColor
         * @return {Array}
         */
        getShadowColors:function () {
            return this._shadowColors;
        },
        /**
         * Set the values for the shadow color of the button
         * @method setShadowColor
         * @param values {Array}
         */
        setShadowColors:function (values) {
            this._shadowColors = values;
            this._needRedraw = true;
        },

        /**
         * Return the Low color for the button
         * @method getRadius
         * @return {Array}
         */
        getRadiuses:function () {
            return this._radiuses;
        },
        /**
         * Set the values for the low color of the button
         * @method setRadius
         * @param values {Array}
         */
        setRadiuses:function (values) {
            this._radiuses = values;
            this._needRedraw = true;
        },

        /**
         * Return the text of the button
         * @method getText
         * @return {Array}
         */
        getTexts:function () {
            return this._texts;
        },
        /**
         * Set the values for text of the button
         * @method setText
         * @param valuess {Array}
         * @example
         *  button.setText(["normal", "over", "deactivated"]);
         */
        setTexts:function (valuess) {
            //if valuess is not an array, create an array of 3 times this values
            if (!cgsgIsArray(valuess)) {
                var v = valuess.toString();
                valuess = [v, v, v];
            }

            this._texts = valuess;
            this._needRedraw = true;
        },

        /**
         * Return the text sizes of the button
         * @method getTextSize
         * @return {Array}
         */
        getTextSizes:function () {
            return this._textSizes;
        },
        /**
         * Set the values for text sizes of the button
         * @method setTextSize
         * @param values {Array}
         */
        setTextSizes:function (values) {
            this._textSizes = values;
            this._needRedraw = true;
        },

        /**
         * Return the text of the button
         * @method getTextColor
         * @return {Array}
         */
        getTextColors:function () {
            return this._textColors;
        },
        /**
         * Set the color for text of the button
         * @method setTextColor
         * @param values {Array}
         * @example
         *  button.setTextColor(["white", "green", "yellow"]);
         */
        setTextColors:function (values) {
            this._textColors = values;
            this._needRedraw = true;
        },

        /**
         * Return the horizontal padding of the button
         * @method getHorizontalPadding
         * @return {Number}
         */
        getHorizontalPadding:function () {
            return this._horizontalPadding;
        },
        /**
         * Set the horizontal padding of the button
         * @method setHorizontalPadding
         * @param values {Number}
         */
        setHorizontalPadding:function (values) {
            this._horizontalPadding = values;
            this._needRedraw = true;
        },

        /**
         * Return the vertical padding of the button
         * @method getVerticalPadding
         * @return {Number}
         */
        getVerticalPadding:function () {
            return this._verticalPadding;
        },
        /**
         * Set the vertical padding of the button
         * @method setHorizontalPadding
         * @param values {Number}
         */
        setVerticalPadding:function (values) {
            this._verticalPadding = values;
            this._needRedraw = true;
        },

        /**
         * @method setFixedSize
         * @param {CGSGDimension} dim Can be null to remove fixed size
         */
        setFixedSize:function (dim) {
            this._dimensions[0] = dim;
            this._dimensions[1] = dim;
            this._dimensions[2] = dim;
            this._fixedSize = cgsgExist(dim);
            this._needRedraw = true;
        },

        /**
         * Pre-render the button into a temp canvas to optimize the perfs
         * @method _initShape
         * @private
         */
        _initShapes:function () {
            this._initShape(0);
            this._initShape(1);
            this._initShape(2);
            this._needRedraw = false;
        },

        /**
         * Pre-render the shape for normal rendering
         * @method _initShape
         * @param {Number} index
         * @private
         */
        _initShape:function (index) {
            this.textNode.setSize(this._textSizes[index], false);
            this.textNode.setText(this._texts[index], true);

            var dPT = this._distancePictoText;
            if (this._texts[index] === ""){
                dPT = 0;
            }

            var decalPictoX = 0, decalPictoY = 0;
            var wImg = 0;
            var hImg = 0;
            if (this._picto.isLoaded) {
                if (this._slices.length > 0) {
                    this._picto.setSlice(this._slices[index].position.x, this._slices[index].position.y,
                        this._slices[index].dimension.width, this._slices[index].dimension.height, true);
                }

                wImg = this._picto.slice.dimension.width;
                hImg = this._picto.slice.dimension.height;

                decalPictoX = (wImg + dPT) * Math.abs(this._pictoPosition.decalX);
                decalPictoY = (hImg + dPT) * Math.abs(this._pictoPosition.decalY);
            }

            if (this._fixedSize) {
                this.resizeTo(this._dimensions[index].width, this._dimensions[index].height);
            }
            else {
                this.resizeTo(
                    (2 * this._horizontalPadding) + decalPictoX + this.textNode.getWidth() * Math.abs(this._pictoPosition.decalX) +
                        this._pictoPosition.computeWidth(this.textNode.getWidth(), wImg),
                    (2 * this._verticalPadding) + decalPictoY + this.textNode.getHeight() * Math.abs(this._pictoPosition.decalY) +
                        this._pictoPosition.computeHeight(this.textNode.getHeight(), hImg));

                this._dimensions[index] = this.dimension.copy();
            }



            this._tmpCanvas[index].width = this.dimension.width + 2 * this._radiuses[index];
            this._tmpCanvas[index].height = this.dimension.height + 2 * this._radiuses[index];
            var tmpContext = this._tmpCanvas[index].getContext('2d');

            cgsgClearContext(tmpContext);

            //render the panel
            tmpContext.save();
            {
                var r = this._radiuses[index];
                tmpContext.translate(-r, -r);
                tmpContext.beginPath();

                tmpContext.moveTo(r, r);
                tmpContext.lineTo(r + this.dimension.width - r, r);
                tmpContext.quadraticCurveTo(r + this.dimension.width,
                    r,
                    r + this.dimension.width,
                    r + r);
                tmpContext.lineTo(r + this.dimension.width,
                    r + this.dimension.height - r);
                tmpContext.quadraticCurveTo(r + this.dimension.width,
                    r + this.dimension.height,
                    r + this.dimension.width - r,
                    r + this.dimension.height);
                tmpContext.lineTo(r + r,
                    r + this.dimension.height);
                tmpContext.quadraticCurveTo(r,
                    r + this.dimension.height,
                    r,
                    r + this.dimension.height - r);
                tmpContext.lineTo(r, r + r);
                tmpContext.quadraticCurveTo(r, r,
                    r + r,
                    r);
                tmpContext.closePath();

                var gradient = tmpContext.createLinearGradient(0, 0, 0, this.dimension.height);
                gradient.addColorStop(0, this._firstColors[index]);
                gradient.addColorStop(1, this._lastColors[index]);
                tmpContext.fillStyle = gradient;

                if (cgsgExist(this._shadowColors)) {
                    tmpContext.shadowColor = this._shadowColors[index];
                    tmpContext.shadowBlur = 10;
                    tmpContext.shadowOffsetX = 0;
                    tmpContext.shadowOffsetY = 0;
                }

                tmpContext.fill();

                if (cgsgExist(this._strokeColor)) {
                    tmpContext.strokeStyle = this._strokeColor;
                    tmpContext.lineWidth = this._lineWidth;
                    tmpContext.stroke();
                }
            }
            tmpContext.restore();

            this.textNode.color = this._textColors[index];

            var w = this.textNode.getWidth();
            var h = this.textNode.getHeight() - this.textNode._size;

            var textX = (-this._pictoPosition.decalX * decalPictoX + this.getWidth() - w) / 2;
            var textY = (-this._pictoPosition.decalY * decalPictoY + this.getHeight() - h) / 2;
            //(this.getHeight() - (this.textNode.getHeight() - this.textNode._size)) / 2;

            if (this._picto.isLoaded) {
                var ctX = w / 2;
                var ctY = h / 2;

                this._picto.translateTo(
                    textX + ctX + this._pictoPosition.decalX * (ctX + dPT + (1 - this._pictoPosition.dt) * wImg) - this._pictoPosition.dy * wImg / 2,
                    //textY + (h - hImg) / 2
                    (1 - this._pictoPosition.dy) * (textY + (h - hImg) / 2)
                        + this._pictoPosition.dy * (textY - this.textNode._size / 2
                        - this._pictoPosition.dt * (dPT + hImg) + (1 - this._pictoPosition.dt) * (this.textNode.getHeight() + dPT))
                    //+ this._pictoPosition.dy * (textY - this.textNode._size / 2 - ( this._pictoPosition.dt) * this.textNode.getHeight())
                );

                this._picto.render(tmpContext);
            }

            this.textNode.translateTo(textX, textY);
            this.textNode.render(tmpContext);
        },

        /**
         * Switch current mode
         * @method setMode
         * @param {CGSGButtonMode} mode
         */
        setMode:function (mode) {
            this._currentMode = mode;
            this.isClickable = mode.isClickable;
            this.resizeTo(this._dimensions[mode.index].width, this._dimensions[mode.index].height);
        },

        /**
         * @method getMode
         * @return {CGSGButtonMode}
         */
        getMode:function () {
            return this._currentMode;
        },

        /**
         * Custom rendering
         * @method render
         * @protected
         * @param {CanvasRenderingContext2D} context the context into render the node
         * */
        render:function (context) {
            //save current state
            this.beforeRender(context);

            context.globalAlpha = this.globalAlpha;

            if (this._needRedraw) {
                this._initShapes();
            }

            //render the pre-rendered canvas
            context.drawImage(this._tmpCanvas[this._currentMode.index], 0, 0);

            //restore state
            this.afterRender(context);
        },

        /**
         * @method copy
         * @return {CGSGNodeSquare} a copy of this node
         */
        copy:function () {
            var node = new CGSGNodeSquare(this.position.x, this.position.y, this.dimension.width,
                this.dimension.height);
            //call the super method
            node = this._super(node);

            node.color = this.color;
            node.lineColor = this.lineColor;
            node.lineWidth = this.lineWidth;
            return node;
        }
    }
);