API Docs for: v2.1.0
Show:

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

/*
 * Copyright (c) 2014 Gwennael Buchet
 *
 * 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 Gwennael Buchet.
 *
 *  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 Gwennael Buchet shall not be used in advertising or otherwise to promote
 *  the use or other dealings in this Software without prior written authorization from Gwennael Buchet.
 *
 *  These Terms of Use are subject to French law.
 */

var CGSGNodeButtonProps = CGSGObject.extend(
    {
        initialize : function() {
            this.cls = [];
            this.firstColor = "";
            this.lastColor = "";
            this.shadowColor = "";
            this.radius = 0;

            this.lineWidth = 0;
            this.lineColor = "";
            this.paddingV = 10;
            this.paddingH = 10;

            /**
             * @property txtNode
             * @type {CGSGNodeText}
             */
            this.txtNode = new CGSGNodeText(0, 0, "");
            /**
             * Slice in the image for the icon
             * @property slice
             * @type {CGSGRegion}
             * @default null
             */
            this.slice = null;
            /**
             * @property icon
             * @type {CGSGNodeImage}
             * @default null
             */
            this.icon = null;
            /**
             * Computed dimensions of the button in the 3 modes.
             * Do not edit manually!
             * @property _dimensions
             * @type {Array}
             * @private
             */
            this.dimension = new CGSGDimension(10, 10);

            this._invalSize = true;
        }

    }
);

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

    /**
     * @property SELECTED
     */
    SELECTED : {
        id          : 3,
        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 Y
 * @param {String} text
 * @type {CGSGNodeButton}
 * @author Gwennael Buchet (gwennael.buchet@gmail.com)
 */
var CGSGNodeButton = CGSGNode.extend(
    {
        initialize : function(x, y, text) {
            /**
             * A properties group for each mode.
             * Index in this array corresponds to the id of the mode
             * @property _props
             * @type {CGSGNodeButtonProps[]}
             * @private
             */
            this._props = [
                new CGSGNodeButtonProps(),
                new CGSGNodeButtonProps(),
                new CGSGNodeButtonProps(),
                new CGSGNodeButtonProps()
            ];

            this._super(x, y);

            this._needRedraw = false;

            /**
             * @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._tmpCanvases = [document.createElement('canvas'),
                                 document.createElement('canvas'),
                                 document.createElement('canvas'),
                                 document.createElement('canvas')];

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

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

            this.setMode(this._currentMode);

            this.setClassFor("cgsg-button", CGSGButtonMode.NORMAL);
            this.setClassFor("cgsg-button-over", CGSGButtonMode.OVER);
            this.setClassFor("cgsg-button-deactivated", CGSGButtonMode.DEACTIVATED);
            this.setClassFor("cgsg-button-selected", CGSGButtonMode.SELECTED);
            this._props[0].txtNode.addClass("cgsg-button-text");
            this._props[1].txtNode.addClass("cgsg-button-over-text");
            this._props[2].txtNode.addClass("cgsg-button-deactivated-text");
            this._props[3].txtNode.addClass("cgsg-button-selected-text");

            this.setTexts(text);

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

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

            /*CGSG.eventManager.bindHandler(this, cgsgEventTypes.ON_FREE, (function(e) {
             this.setMode(CGSGButtonMode.SELECTED);
             }).bind(this));*/

            this._isInitialized = true;
        },

        /**
         * 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.forceRedraw();
        },

        /**
         * Because a button got 4 modes it also got 4 CSS management
         * @method setClassFor
         * @param cls {String}
         * @param mode {CGSGButtonMode}
         */
        setClassFor : function(cls, mode) {
            this._props[mode.id].cls = [];
            this._props[mode.id].cls.push(cls);

            this._props[mode.id]._invalSize = false;
            this._needRedraw = false;
            this._loadAttrs(mode);
            this._initShape(mode);
        },

        /**
         * Set the same CSS class for all 4 modes
         * @method setClass
         * @param cls {String}
         */
        setClass : function(cls) {
            this._clearAllCls();
            this._props[CGSGButtonMode.NORMAL.id].cls.push(cls);
            this._props[CGSGButtonMode.OVER.id].cls.push(cls);
            this._props[CGSGButtonMode.DEACTIVATED.id].cls.push(cls);
            this._props[CGSGButtonMode.SELECTED.id].cls.push(cls);

            this.invalidateTheme();
        },

        /**
         * @method setClasses
         * @param clss {Array} Array of CSS class name
         */
        setClasses : function(clss) {
            this._clearAllCls();
            this._props[CGSGButtonMode.NORMAL.id].cls.push(clss[0]);
            this._props[CGSGButtonMode.OVER.id].cls.push(clss[1]);
            this._props[CGSGButtonMode.DEACTIVATED.id].cls.push(clss[2]);
            this._props[CGSGButtonMode.SELECTED.id].cls.push(clss[3]);
            this.invalidateTheme();
        },

        _clearAllCls : function() {
            this._props[CGSGButtonMode.NORMAL.id].cls = [];
            this._props[CGSGButtonMode.OVER.id].cls = [];
            this._props[CGSGButtonMode.DEACTIVATED.id].cls = [];
            this._props[CGSGButtonMode.SELECTED.id].cls = [];
        },

        /**
         * Add CSS class for this node and for this mode only (not for bounding box, use 'setClassBBox' instead).
         * CSS class must define attributes used by this node.
         * @method addClassFor
         * @param {String} cls
         * @param mode {CGSGButtonMode}
         */
        addClassFor : function(cls, mode) {
            this._props[mode.id].cls.push(cls);

            this._props[mode.id]._invalSize = false;
            this._needRedraw = false;
            this._loadAttrs(mode);
            this._initShape(mode);
        },

        /**
         * Add CSS class for this node for all 4 modes (not for bounding box, use 'setClassBBox' instead).
         * CSS class must define attributes used by this node.
         * @method addClass
         * @param {String} cls
         */
        addClass : function(cls) {
            this._props[CGSGButtonMode.NORMAL.id].cls.push(cls);
            this._props[CGSGButtonMode.OVER.id].cls.push(cls);
            this._props[CGSGButtonMode.DEACTIVATED.id].cls.push(cls);
            this._props[CGSGButtonMode.SELECTED.id].cls.push(cls);

            this.invalidateTheme();
        },

        /**
         * Add CSS class for this node for all 4 modes (not for bounding box, use 'setClassBBox' instead).
         * CSS class must define attributes used by this node.
         * @method addClasses
         * @param {Array} clss
         */
        addClasses : function(clss) {
            this._props[CGSGButtonMode.NORMAL.id].cls.push(clss[0]);
            this._props[CGSGButtonMode.OVER.id].cls.push(clss[1]);
            this._props[CGSGButtonMode.DEACTIVATED.id].cls.push(clss[2]);
            this._props[CGSGButtonMode.SELECTED.id].cls.push(clss[3]);

            this.invalidateTheme();
        },

        /**
         * remove CSS class for this node and for this mode only (not for bounding box, use 'setClassBBox' instead).
         * @method removeClassFor
         * @param {String} cls
         * @param {CGSGButtonMode} mode
         */
        removeClassFor : function(cls, mode) {
            this._props[mode.id].cls = this._props[mode.id].cls.without(cls);
            this.forceRedraw();
            this._loadAttrs(mode);
            this._initShape(mode);
        },

        /**
         * remove CSS class for this node for all 4 modes (not for bounding box, use 'setClassBBox' instead).
         * @method removeClass
         * @param {String} cls
         */
        removeClass : function(cls) {
            this._props[CGSGButtonMode.NORMAL.id].cls = this._props[CGSGButtonMode.NORMAL.id].without(cls);
            this._props[CGSGButtonMode.OVER.id].cls = this._props[CGSGButtonMode.OVER.id].without(cls);
            this._props[CGSGButtonMode.DEACTIVATED.id].cls = this._props[CGSGButtonMode.DEACTIVATED.id].without(cls);
            this._props[CGSGButtonMode.SELECTED.id].cls = this._props[CGSGButtonMode.SELECTED.id].without(cls);

            this.invalidateTheme();
        },

        /**
         * @method setImageFor
         * @param img {Image}
         * @param mode {CGSGButtonMode}
         */
        setImageFor : function(img, mode) {
            this._props[mode.id].icon.setImage(img);
        },

        /**
         * Set the image for the picto
         * @method setImage
         * @param {Image} img
         */
        setImage : function(img) {
            this._checkIcon();

            this.setImageFor(img, CGSGButtonMode.NORMAL);
            this.setImageFor(img, CGSGButtonMode.OVER);
            this.setImageFor(img, CGSGButtonMode.DEACTIVATED);
            this.setImageFor(img, CGSGButtonMode.SELECTED);

            this._needRedraw = true;
        },

        /**
         * Set the URL for the icon for all modes
         * @method setImageURL
         * @param url
         */
        setImageURL : function(url) {
            this._checkIcon();

            this._props[CGSGButtonMode.NORMAL.id].icon.onLoadEnd = this._onLoadImageEndAll.bind(this);
            this._props[CGSGButtonMode.NORMAL.id].icon.setURL(url);
        },

        /**
         * Set the URL for the icon for all modes
         * @method setImageURL
         * @param url
         * @param mode {CGSGButtonMode}
         */
        setImageURLFor : function(url, mode) {
            this._props[mode.id].icon.onLoadEnd = this._onLoadImageEndFor.bind(this);
            this._props[mode.id].icon.setURL(url);
        },

        _onLoadImageEndFor : function() {
            this.forceRedraw();
        },

        _onLoadImageEndAll : function() {
            var img = this._props[CGSGButtonMode.NORMAL.id].icon.getImage();
            this._props[CGSGButtonMode.OVER.id].icon.setImage(img);
            this._props[CGSGButtonMode.DEACTIVATED.id].icon.setImage(img);
            this._props[CGSGButtonMode.SELECTED.id].icon.setImage(img);

            this.forceRedraw();
        },

        /**
         * Check if icon object already exist for each mode.
         * If not, create them
         * @method _checkIcon
         * @private
         */
        _checkIcon : function() {
            this._checkIconFor(CGSGButtonMode.NORMAL);
            this._checkIconFor(CGSGButtonMode.OVER);
            this._checkIconFor(CGSGButtonMode.DEACTIVATED);
            this._checkIconFor(CGSGButtonMode.SELECTED);
        },

        /**
         * @method _checkIconFor
         * @param mode
         * @private
         */
        _checkIconFor : function(mode) {
            if (!cgsgExist(this._props[mode.id].icon))
                this._props[mode.id].icon = new CGSGNodeImage(0, 0, null);
        },

        /**
         * Return the text of the button
         * @method getText
         * @return {Array}
         */
        getTexts : function() {
            return [CGSGButtonMode.NORMAL.props.txtNode._text, CGSGButtonMode.OVER.props.txtNode._text,
                    CGSGButtonMode.DEACTIVATED.props.txtNode._text, CGSGButtonMode.SELECTED.props.txtNode._text];
        },

        /**
         * Force this node to be fully recomputed during next frame
         * @method forceRedraw
         */
        forceRedraw : function() {
            this._needRedraw = true;

            this._props[CGSGButtonMode.NORMAL.id]._invalSize = true;
            this._props[CGSGButtonMode.OVER.id]._invalSize = true;
            this._props[CGSGButtonMode.DEACTIVATED.id]._invalSize = true;
            this._props[CGSGButtonMode.SELECTED.id]._invalSize = true;
        },

        /**
         * 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 4 times this values
            if (!cgsgIsArray(valuess)) {
                var v = valuess.toString();
                valuess = [v, v, v, v];
            }

            this._props[CGSGButtonMode.NORMAL.id].txtNode.setText(valuess[0], true);
            this._props[CGSGButtonMode.OVER.id].txtNode.setText(valuess[1], true);
            this._props[CGSGButtonMode.DEACTIVATED.id].txtNode.setText(valuess[2], true);
            this._props[CGSGButtonMode.SELECTED.id].txtNode.setText(valuess[3], true);

            this.forceRedraw();
        },

        /**
         * @method setTextClass
         * @param cls {String} CSS class name
         */
        setTextClass : function(cls) {
            this._props[CGSGButtonMode.NORMAL.id].txtNode.setClass(cls);
            this._props[CGSGButtonMode.OVER.id].txtNode.setClass(cls);
            this._props[CGSGButtonMode.DEACTIVATED.id].txtNode.setClass(cls);
            this._props[CGSGButtonMode.SELECTED.id].txtNode.setClass(cls);
            this.invalidate();
        },

        /**
         * @method setTextClasses
         * @param clss {Array} Array of CSS class name
         */
        setTextClasses : function(clss) {
            this._props[CGSGButtonMode.NORMAL.id].txtNode.setClass(clss[0]);
            this._props[CGSGButtonMode.OVER.id].txtNode.setClass(clss[1]);
            this._props[CGSGButtonMode.DEACTIVATED.id].txtNode.setClass(clss[2]);
            this._props[CGSGButtonMode.SELECTED.id].txtNode.setClass(clss[3]);
            this.invalidate();
        },

        /**
         * @method setTextClassFor
         * @param mode {CGSGButtonMode}
         * @param cls {String} CSS class name
         */
        setTextClassFor : function(cls, mode) {
            this._props[mode.id].txtNode.setClass(cls);
            this.invalidate();
        },

        /**
         * @method setFixedSize
         * @param {CGSGDimension} dim Can be null to remove fixed size
         */
        setFixedSize : function(dim) {
            this._props[CGSGButtonMode.NORMAL.id].dimension = dim;
            this._props[CGSGButtonMode.OVER.id].dimension = dim;
            this._props[CGSGButtonMode.DEACTIVATED.id].dimension = dim;
            this._props[CGSGButtonMode.SELECTED.id].dimension = dim;

            this._fixedSize = cgsgExist(dim);
            this.forceRedraw();
        },

        /**
         * Set the slices in the image for the 3 modes
         * @method setSlices
         * @param slices {Array} array of region for all 4 modes
         */
        setSlices : function(slices) {
            this._props[CGSGButtonMode.NORMAL.id].slice = slices[0];
            this._props[CGSGButtonMode.OVER.id].slice = slices[1];
            this._props[CGSGButtonMode.DEACTIVATED.id].slice = slices[2];
            this._props[CGSGButtonMode.SELECTED.id].slice = slices[3];
            this.forceRedraw();
        },

        /**
         * @method setSliceFor
         * @param slice {CGSGRegion}
         * @param mode {CGSGButtonMode}
         */
        setSliceFor : function(slice, mode) {
            this._props[mode.id].slice = slice;
            this._props[mode.id]._invalSize = true;

            this._initShape(mode);
        },

        /**
         * Reload theme (colors, ...) from loaded CSS file
         * @method invalidateTheme
         */
        invalidateTheme : function() {
            this._super();

            this._props[CGSGButtonMode.NORMAL.id].txtNode.invalidateTheme();
            this._props[CGSGButtonMode.OVER.id].txtNode.invalidateTheme();
            this._props[CGSGButtonMode.DEACTIVATED.id].txtNode.invalidateTheme();
            this._props[CGSGButtonMode.SELECTED.id].txtNode.invalidateTheme();

            //Use of "this._cls" class name which define the current CSS class used by this object.
            this._loadAttrs(CGSGButtonMode.NORMAL);
            this._loadAttrs(CGSGButtonMode.OVER);
            this._loadAttrs(CGSGButtonMode.DEACTIVATED);
            this._loadAttrs(CGSGButtonMode.SELECTED);

            this.invalidate();
        },

        invalidate : function() {
            if (cgsgExist(this._isInitialized) && this._isInitialized === true) {
                this._initShapes();
            }
        },

        /**
         * @method _loadAttrs
         * @param mode {CGSGButtonMode}
         * @private
         */
        _loadAttrs : function(mode) {
            var cls = this._props[mode.id].cls;
            var id = mode.id;
            var prop = this._props[id];

            var fillStyle = CGSG.cssManager.getAttrInArray(cls, "background");
            var lineWidth = CGSG.cssManager.getAttrInArray(cls, "border-width");
            var lineColor = CGSG.cssManager.getAttrInArray(cls, "border-color");
            var paddingV = CGSG.cssManager.getAttrInArray(cls, "padding-top");
            var paddingH = CGSG.cssManager.getAttrInArray(cls, "padding-left");
            var radius = CGSG.cssManager.getAttrInArray(cls, "border-radius");
            var icon = CGSG.cssManager.getAttrInArray(cls, "icon");
            if (!cgsgExist(icon))
                icon = CGSG.cssManager.getAttrInArray(cls, "list-style-image");

            //Avoid to override previous value if no one is defined now. So check existence of new one first.
            if (cgsgExist(fillStyle)) {
                //value is given as "linear-gradient(rgb(150, 150, 150), rgb(127, 127, 127))".
                // Let's convert it to 2 hex.
                var srgb1 = fillStyle.substring(fillStyle.indexOf("rgb"), fillStyle.indexOf(")") + 1);
                var srgb2 = fillStyle.substring(fillStyle.lastIndexOf("rgb"), fillStyle.lastIndexOf(")"));
                var rgb1 = CGSGColor.fromString(srgb1);
                var rgb2 = CGSGColor.fromString(srgb2);
                prop.firstColor = CGSGColor.rgb2hex(rgb1.r, rgb1.g, rgb1.b);
                prop.lastColor = CGSGColor.rgb2hex(rgb2.r, rgb2.g, rgb2.b);
            }

            if (cgsgExist(lineColor))
                prop.lineColor = lineColor;

            if (cgsgExist(radius))
                prop.radius = CGSG.cssManager.getNumber(radius);

            if (cgsgExist(lineWidth))
                prop.lineWidth = CGSG.cssManager.getNumber(lineWidth);

            if (cgsgExist(lineColor))
                prop.lineColor = lineColor;

            if (cgsgExist(paddingV))
                prop.paddingV = CGSG.cssManager.getNumber(paddingV);
            if (cgsgExist(paddingH))
                prop.paddingH = CGSG.cssManager.getNumber(paddingH);

            if (cgsgExist(icon)) {
                this._checkIconFor(mode);
                this.setImageURLFor(CGSG.cssManager.getURL(icon), mode);
            }
        },

        /**
         * Increase/decrease current dimension with adding values
         * @method resizeWith
         * @param {Number} width
         * @param {Number} height
         * */
        resizeWith : function(width, height) {
            this._needRedraw = true;

            this._super(width, height);
        },

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

        /**
         * Pre-render the shape for normal rendering
         * @method _initShape
         * @param mode
         * @private
         */
        _initShape : function(mode) {
            var id = mode.id;
            var prop = this._props[id];
            var txtNode = prop.txtNode;
            var tW = txtNode.getWidth(), tH = txtNode.getHeight();

            var dPT = this._distancePictoText;
            if (prop.text === "") {
                dPT = 0;
            }

            var decalPictoX = 0, decalPictoY = 0;
            var wImg = 0;
            var hImg = 0;
            if (cgsgExist(prop.icon) && prop.icon.isLoaded) {
                if (cgsgExist(prop.slice)) {
                    prop.icon.setSlice(prop.slice.position.x, prop.slice.position.y,
                                       prop.slice.dimension.width, prop.slice.dimension.height,
                                       true);
                }

                wImg = prop.icon.slice.dimension.width;
                hImg = prop.icon.slice.dimension.height;

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

            if (prop._invalSize) {
                if (this._fixedSize) {
                    this.dimension.resizeTo(prop.dimension.width, prop.dimension.height);
                }
                else {
                    this.dimension.resizeTo(
                            (2 * prop.paddingH) + decalPictoX +
                            tW * Math.abs(this._pictoPosition.decalX) +
                            this._pictoPosition.computeWidth(tW, wImg),
                            (2 * prop.paddingV) + decalPictoY +
                            tH * Math.abs(this._pictoPosition.decalY) +
                            this._pictoPosition.computeHeight(tH, hImg));
                }
                prop._invalSize = false;
                this._isDimensionChanged = true;
            }
            prop.dimension = this.dimension.copy();

            this._tmpCanvases[id].width = this.dimension.width + 2 * prop.radius;
            this._tmpCanvases[id].height = this.dimension.height + 2 * prop.radius;
            var tmpContext = this._tmpCanvases[id].getContext('2d');

            cgsgClearContext(tmpContext);

            //render the panel
            tmpContext.save();
            {
                var r = prop.radius;
                tmpContext.translate(-r, -r);
                tmpContext.beginPath();

                tmpContext.moveTo(2 * 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, prop.firstColor);
                gradient.addColorStop(1, prop.lastColor);
                tmpContext.fillStyle = gradient;*/
                if (!cgsgExist(prop.lastColor)) {
                    tmpContext.fillStyle = prop.firstColor;
                }
                else {
                    var gradient = tmpContext.createLinearGradient(0, 0, 0, this.dimension.height);
                    gradient.addColorStop(0, prop.firstColor);
                    gradient.addColorStop(1, prop.lastColor);
                    tmpContext.fillStyle = gradient;
                }

                if (cgsgExist(prop.shadowColor)) {
                    tmpContext.shadowColor = prop.shadowColor;
                    tmpContext.shadowBlur = 10;
                    tmpContext.shadowOffsetX = 0;
                    tmpContext.shadowOffsetY = 0;
                }

                tmpContext.fill();

                if (cgsgExist(prop.lineColor) && prop.lineColor > 0) {
                    tmpContext.strokeStyle = prop.lineColor;
                    tmpContext.lineWidth = prop.lineWidth;
                    tmpContext.stroke();
                }
            }
            tmpContext.restore();

            var textX = (-this._pictoPosition.decalX * decalPictoX + this.getWidth() - tW) / 2;
            var textY = (-this._pictoPosition.decalY * decalPictoY + this.getHeight() - tH) / 2;

            if (cgsgExist(prop.icon) && prop.icon.isLoaded) {
                var ctX = tW / 2;

                prop.icon.translateTo(
                        textX + ctX + this._pictoPosition.decalX * (ctX + dPT + (1 - this._pictoPosition.dt) * wImg) -
                        this._pictoPosition.dy * wImg / 2,
                        (1 - this._pictoPosition.dy) * (textY + (tH - hImg) / 2)
                            + this._pictoPosition.dy * (textY - txtNode._size / 2
                                                            - this._pictoPosition.dt * (dPT + hImg) +
                                                        (1 - this._pictoPosition.dt) * (tH + dPT))
                );

                prop.icon.doRender(tmpContext);
            }

            txtNode.translateTo(textX, textY, false);
            txtNode.doRender(tmpContext);
        },

        /**
         * Switch current mode
         * @method setMode
         * @param mode
         */
        setMode : function(mode) {
            this._currentMode = mode;
            this.isClickable = mode.isClickable;
            this.dimension.resizeTo(this._props[mode.id].dimension.width, this._props[mode.id].dimension.height);
            this._isDimensionChanged = true;
        },

        /**
         * @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) {
            if (this._needRedraw) {
                this._initShapes();
            }

            //render the pre-rendered canvas
            context.drawImage(this._tmpCanvases[this._currentMode.id], 0, 0);
        },

        /**
         * @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.bkgcolors = this.bkgcolors;
            node.lineColor = this.lineColor;
            node.lineWidth = this.lineWidth;
            return node;
        }
    }
);