/**
* A button with three states with out, over and down.
*
* @constructor FORGE.Button
* @param {FORGE.Viewer} viewer - {@link FORGE.Viewer} reference.
* @param {ButtonConfig=} config - The button config.
* @extends {FORGE.DisplayObjectContainer}
*
* @todo Add tween for properties
* @todo Ability to set image size to the defined width/height/padding/margin of the button
*/
FORGE.Button = function(viewer, config)
{
/**
* Button configuration object.
* @name FORGE.Button#_config
* @type {?ButtonConfig}
* @private
*/
this._config = config || null;
/**
* Object that handles the button skins.
* @name FORGE.Button#_skins
* @type {Object}
* @private
*/
this._skins = {};
/**
* Current skin name.
* @name FORGE.Button#_skin
* @type {string}
* @private
*/
this._skin = "";
/**
* Current state of the button.<br>
* Available states are in {link FORGE.Button.states}.
* @type {string}
* @private
*/
this._state = "";
/**
* Name of the default skin.
* @name FORGE.Button#_defaultSkin
* @type {string}
* @private
*/
this._defaultSkin = "";
/**
* Button image component.
* @name FORGE.Button#_image
* @type {FORGE.Image}
* @private
*/
this._image = null;
/**
* Button label component.
* @name FORGE.Button#_label
* @type {FORGE.TextField}
* @private
*/
this._label = null;
/**
* Is this button is on auto width?
* @name FORGE.Button#_autoWidth
* @type {boolean}
* @private
*/
this._autoWidth = false;
/**
* Is this button is on auto height?
* @name FORGE.Button#_autoHeight
* @type {boolean}
* @private
*/
this._autoHeight = false;
/**
* The number of items to load.
* @name FORGE.Button#_itemToLoad
* @type {number}
* @private
*/
//this._itemsToLoad = 0;
/**
* Item that are loaded.
* @name FORGE.Button#_itemToLoad
* @type {number}
* @private
*/
this._itemsLoaded = 0;
/**
* Is this button loaded?
* @name FORGE.Button#_loaded
* @type {boolean}
* @private
*/
this._loaded = false;
FORGE.DisplayObjectContainer.call(this, viewer, null, "Button");
};
FORGE.Button.prototype = Object.create(FORGE.DisplayObjectContainer.prototype);
FORGE.Button.prototype.constructor = FORGE.Button;
/**
* Button possible states
* @name FORGE.Button.states
* @type {Object}
* @property {string} OUT
* @property {string} OVER
* @property {string} DOWN
* @const
*/
FORGE.Button.states =
{
OUT: "out",
OVER: "over",
DOWN: "down"
};
/**
* Boot sequence.
* @method FORGE.Button#_boot
* @private
*/
FORGE.Button.prototype._boot = function()
{
FORGE.DisplayObjectContainer.prototype._boot.call(this);
this._state = FORGE.Button.states.OUT;
this._image = new FORGE.Image(this._viewer);
this._image.onLoadComplete.add(this._itemLoadComplete, this);
this._image.onResize.add(this._itemResizeHandler, this);
this.addChild(this._image);
this._label = new FORGE.TextField(this._viewer);
this._label.onLoadComplete.add(this._itemLoadComplete, this);
this._label.onResize.add(this._itemResizeHandler, this);
this._label.autoWidth = true;
this._label.autoHeight = true;
this.addChild(this._label);
this.pointer.enabled = true;
this.pointer.cursor = FORGE.Pointer.cursors.POINTER;
this.pointer.onEnter.add(this._mouseEnterHandler, this);
this.pointer.onLeave.add(this._mouseLeaveHandler, this);
this.pointer.onPressStart.add(this._pressStartHandler, this);
this.pointer.onPressEnd.add(this._pressEndHandler, this);
this._viewer.display.register(this);
this._notifyReady();
this._applyPending(false);
this._viewer.i18n.onLocaleChangeComplete.add(this._localeChangeCompleteHandler, this);
this.load(this._config);
};
/**
* Handler for mouse enter.
* @method FORGE.Button#_mouseEnterHandler
* @private
*/
FORGE.Button.prototype._mouseEnterHandler = function()
{
this._applyState(FORGE.Button.states.OVER);
};
/**
* Handler for mouse leave.
* @method FORGE.Button#_mouseLeaveHandler
* @private
*/
FORGE.Button.prototype._mouseLeaveHandler = function()
{
this._applyState(FORGE.Button.states.OUT);
};
/**
* Handler for mouse down.
* @method FORGE.Button#_mouseDownHandler
* @private
*/
FORGE.Button.prototype._pressStartHandler = function()
{
this._applyState(FORGE.Button.states.DOWN);
};
/**
* Handler for mouse up.
* @method FORGE.Button#_mouseUpHandler
* @private
*/
FORGE.Button.prototype._pressEndHandler = function()
{
this._applyState(FORGE.Button.states.OVER);
};
/**
* Handler for localeChangeComplete.
* @method FORGE.Button#_localeChangeCompleteHandler
* @private
*/
FORGE.Button.prototype._localeChangeCompleteHandler = function()
{
this._applyState(this._state);
};
/**
* Image or Label load complete callback.
* @method FORGE.Button._imageLoadComplete
* @private
*/
FORGE.Button.prototype._itemLoadComplete = function(event)
{
this.log("_itemLoadComplete "+event.emitter.className);
this._itemsLoaded++;
//if(this._itemsLoaded === this._itemsToLoad)
if(this._itemsLoaded === 2)
{
this._allItemsLoaded();
}
};
/**
* This method is called after all items are loaded. (image and label).
* @method FORGE.Button#_allItemsLoaded
* @private
*/
FORGE.Button.prototype._allItemsLoaded = function()
{
this.log("_allItemsLoaded");
this._updateLayout();
this._updateAutoSize();
this._loaded = true;
};
/**
* Event handler for items resize.
* @method FORGE.Button#_itemResizeHandler
* @private
*/
FORGE.Button.prototype._itemResizeHandler = function()
{
this._updateLayout();
this._updateAutoSize();
};
/**
* Apply a skin to the button.
* @method FORGE.Button#_applySkin
* @private
* @param {string} name - The of the skin you want to apply.
*/
FORGE.Button.prototype._applySkin = function(name)
{
this._skin = name;
this._applyState(this._state);
};
/**
* Apply a specified state of the current skin.
* @method FORGE.Button._applyState
* @private
* @param {string} state - The name of the state you want to apply (listed on FORGE.Button.states)
*/
FORGE.Button.prototype._applyState = function(state)
{
//The button is not loaded the time to apply its skin and load its resources
this._loaded = false;
//Set the current state (out, over, down)
this._state = state;
//Get the current skin
var skin = this._skins[this._skin];
//Reset the load count
this._itemsLoaded = 0;
//this._itemsToLoad = 0;
var hasImage = skin.hasImage(this._state);
// if(hasImage === true)
// {
// this._itemsToLoad ++;
// }
//var hasLabel = skin.hasLabel(this._state);
//if(hasLabel === true)
// {
// this._itemsToLoad ++;
// }
// var width = skin.getProperty("width", state);
// var height = skin.getProperty("height", state);
// if(width !== this.width)
// {
// this.width = width;
// }
// if(height !== this.height)
// {
// this.height = height;
// }
this.background = /** @type {string} */ (skin.getProperty("background", this._state));
this.borderStyle = /** @type {string} */ (skin.getProperty("borderStyle", this._state));
this.borderColor = /** @type {string} */ (skin.getProperty("borderColor", this._state));
this.borderRadius = /** @type {number} */ (skin.getProperty("borderRadius", this._state));
this.borderWidth = /** @type {number} */ (skin.getProperty("borderWidth", this._state));
this._autoWidth = /** @type {boolean} */ (skin.getProperty("autoWidth", this._state));
this._autoHeight = /** @type {boolean} */ (skin.getProperty("autoHeight", this._state));
var label = /** @type {(string|TextFieldConfig)} */ (skin.getProperty("label", this._state)); //@todo !!!
if(typeof label !== "undefined")
{
this._label.load(label);
}
// Image =================================
if(hasImage === true)
{
this._image.load(/** @type {(string|ImageConfig)} */ (skin.getProperty("image", this._state)));
}
else
{
// this._image.load(null);
}
// if(hasImage === false && hasLabel === false)
// {
// this._allItemsLoaded();
// }
};
/**
* Apply the layout of the button skin elements.
* @method FORGE.Button.prototype#_updateLayout
* @private
*/
FORGE.Button.prototype._updateLayout = function()
{
this.log("_updateLayout");
var skin = this._skins[this._skin];
var align = skin.getProperty("align", this._state);
var padding = skin.getProperty("padding", this._state);
var spacing = skin.getProperty("spacing", this._state);
var f = skin.getProperty("first", this._state);
var first, second;
if(f === "label")
{
first = this._label;
second = this._image;
}
else
{
first = this._image;
second = this._label;
}
switch(align)
{
case "left":
first.left = padding;
first.verticalCenter = true;
second.left = first.left + spacing + first.pixelWidth;
second.verticalCenter = true;
break;
case "right":
first.right = padding;
first.verticalCenter = true;
second.right = first.right + spacing + first.pixelWidth;
second.verticalCenter = true;
break;
case "top":
first.top = padding;
first.horizontalCenter = true;
second.top = first.top + first.pixelHeight + spacing;
second.horizontalCenter = true;
break;
case "bottom":
first.bottom = padding;
first.horizontalCenter = true;
second.bottom = first.bottom + spacing + first.pixelHeight;
second.horizontalCenter = true;
break;
default:
//center
first.horizontalCenter = true;
first.verticalCenter = true;
second.horizontalCenter = true;
second.verticalCenter = true;
}
};
/**
* Update auto sizes, ajust width and height if auto size is enabled.
* @method FORGE.Button#_updateAutoSize
* @private
*/
FORGE.Button.prototype._updateAutoSize = function()
{
this.log("_updateAutoSize");
if(this._autoWidth === false && this._autoHeight === false)
{
return;
}
var skin = this._skins[this._skin];
var padding = /** @type {number} */ (skin.getProperty("padding", this._state));
if(this._autoWidth === true)
{
var width = this.pixelWidth;
var xMin = Math.min(this._label.x, this._image.x);
var xMax = Math.max(this._label.x + this._label.pixelWidth, this._image.x + this._image.pixelWidth);
this._width = (xMax - xMin) + padding * 2 + this._borderWidth * 2;
this._unitWidth = "px";
this._dom.style.width = this._width+"px";
if(width !== this.pixelWidth)
{
this._notifyResize({ property: "autoWidth" });
}
}
if(this._autoHeight === true)
{
var height = this.pixelHeight;
var yMin = Math.min(this._label.y, this._image.y);
var yMax = Math.max(this._label.y + this._label.pixelHeight, this._image.y + this._image.pixelHeight);
this._height = (yMax - yMin) + padding * 2 + this._borderWidth * 2;
this._unitHeight = "px";
this._dom.style.height = this._height+"px";
if(height !== this.pixelHeight)
{
this._notifyResize({ property: "autoHeight" });
}
}
};
/**
* Parse a Button configuration
* @method FORGE.Button#_parseConfig
* @private
* @param {(ButtonConfig|FORGE.ButtonSkin)} config - The configuration to parse.
*/
FORGE.Button.prototype._parseConfig = function(config)
{
if(typeof config !== "undefined" && config !== null)
{
//If there is an array of skins, add them
if(FORGE.Utils.isArrayOf(config.skins, "ButtonSkin") === true)
{
for(var i = 0, ii = config.skins.length; i < ii; i++)
{
this.addSkin(config.skins[i]);
}
}
else
{
//convert skins object definition into FORGE.ButtonSkin objects
if(typeof config.skins === "object" && config.skins.length > 0)
{
var skin;
for(var j = 0, jj = config.skins.length; j < jj; j++)
{
skin = new FORGE.ButtonSkin(config.skins[j].name, config.skins[j].states);
this.addSkin(skin);
}
}
}
//If there is a single skin in config, add it
if(FORGE.Utils.isTypeOf(config.skin, "ButtonSkin"))
{
this.addSkin(config.skin);
}
else
{
//convert skin object definition into FORGE.ButtonSkin
if(typeof config.skin === "object")
{
var singleSkin = new FORGE.ButtonSkin(config.skin.name, config.skin.states);
this.addSkin(singleSkin);
}
}
if(FORGE.Utils.isTypeOf(config.default, "string") === true && this.hasSkin(/** @type {string} */ (config.default)) === true)
{
this._defaultSkin = /** @type {string} */ (config.default);
}
}
//If no skin found, add the default skin
var skins = this._skins || {};
if(Object.keys(skins).length === 0)
{
var defaultSkin = new FORGE.ButtonSkin("default");
this.addSkin(defaultSkin);
// this._skins.default = new FORGE.ButtonSkin("default");
}
//If no skin have been chosen pick the first one
if(this._defaultSkin === "")
{
this._defaultSkin = this._skins[Object.keys(skins)[0]].name;
}
};
/**
* Destroy image method.
* @method FORGE.Button#_destroyImage
* @private
*/
FORGE.Button.prototype._destroyImage = function()
{
if(this._image !== null)
{
this._image.onLoadComplete.remove(this._itemLoadComplete, this);
this._image.onResize.remove(this._itemResizeHandler, this);
this.removeChild(this._image, true);
this._image = null;
}
};
/**
* Destroy label method.
* @method FORGE.Button#_destroyLabel
* @private
*/
FORGE.Button.prototype._destroyLabel = function()
{
if(this._label !== null)
{
this._label.onLoadComplete.remove(this._itemLoadComplete, this);
this._label.onResize.remove(this._itemResizeHandler, this);
this.removeChild(this._label, true);
this._label = null;
}
};
/**
* Load a button configuration.
* @method FORGE.Button#load
* @param {(ButtonConfig|FORGE.ButtonSkin)} config - The button configuration to load.
*/
FORGE.Button.prototype.load = function(config)
{
this._parseConfig(config);
this._applySkin(this._defaultSkin);
};
/**
* Add a skin to the button
* @method FORGE.Button#addSkin
* @param {FORGE.ButtonSkin} skin - The button skin you want to add to this button.
* @param {boolean=} setup - Does the skin you add should be defined as the new current skin.
*/
FORGE.Button.prototype.addSkin = function(skin, setup)
{
if(FORGE.Utils.isTypeOf(skin, "ButtonSkin") === false)
{
throw "FORGE.Button: Invalid button skin!";
}
skin.setDefaultFromState(FORGE.Button.states.OUT);
this._skins[skin.name] = skin;
if(setup === true)
{
this._applySkin(skin.name);
}
};
/**
* Set the button skin
* @method FORGE.Button#setSkin
* @param {(string|FORGE.ButtonSkin)} value - Either an existing skin name or an existing or a new {@link FORGE.ButtonSkin}
*/
FORGE.Button.prototype.setSkin = function(value)
{
if(typeof value === "string" && FORGE.Utils.isTypeOf(this._skins[value], "ButtonSkin") === true)
{
this._applySkin(value);
}
else if(FORGE.Utils.isTypeOf(value, "ButtonSkin") === true)
{
if(this._skins[value.name] === value)
{
this._applySkin(value.name);
}
else if(typeof this._skins[value.name] === "undefined" || this._skins[value.name] === null)
{
this.addSkin(/** @type {FORGE.ButtonSkin} */ (value), true);
}
}
};
/**
* Does the button has a specified skin ?
* @param {string} name - The name of the skin you want to check.
* @return {boolean} Returns true if the button have a skin with asked name.
*/
FORGE.Button.prototype.hasSkin = function(name)
{
return (typeof this._skins[name] !== "undefined");
};
/**
* Update the skin display, use this method if you change the skin and you need to update it.
* @method FORGE.Button#updateSkin
*/
FORGE.Button.prototype.updateSkin = function()
{
this._skins[this._skin].setDefaultFromState(FORGE.Button.states.OUT);
this._applySkin(this._skin);
};
/**
* Destroy method.
* @method FORGE.Button#destroy
*/
FORGE.Button.prototype.destroy = function()
{
this._viewer.i18n.onLocaleChangeComplete.remove(this._localeChangeCompleteHandler, this);
if(typeof this._skins !== "undefined" && this._skins.length > 0)
{
for(var i in this._skins)
{
if(this._skins.hasOwnProperty(i))
{
this._skins[i].destroy();
}
}
}
this._skins = {};
this._destroyImage();
this._destroyLabel();
FORGE.DisplayObjectContainer.prototype.destroy.call(this);
};
/**
* Get the skins list.
* @name FORGE.Button#skins
* @readonly
* @type {Object}
*/
Object.defineProperty(FORGE.Button.prototype, "skins",
{
/** @this {FORGE.Button} */
get: function()
{
return this._skins;
}
});
/**
* Get and set the current skin.
* @name FORGE.Button#skin
* @type {FORGE.ButtonSkin}
*/
Object.defineProperty(FORGE.Button.prototype, "skin",
{
/** @this {FORGE.Button} */
get: function()
{
return this._skins[this._skin];
},
/** @this {FORGE.Button} */
set: function(value)
{
this.setSkin(value);
}
});
/**
* Get and set the flag for auto size on width.
* @name FORGE.Button#autoWidth
* @type {boolean}
*/
Object.defineProperty(FORGE.Button.prototype, "autoWidth",
{
/** @this {FORGE.Button} */
get: function()
{
return this._autoWidth;
},
/** @this {FORGE.Button} */
set: function(value)
{
if(typeof value !== "boolean")
{
return;
}
this._autoWidth = value;
this._updateAutoSize();
}
});
/**
* Get and set the flag for auto size on height.
* @name FORGE.Button#autoHeight
* @type {boolean}
*/
Object.defineProperty(FORGE.Button.prototype, "autoHeight",
{
/** @this {FORGE.Button} */
get: function()
{
return this._autoHeight;
},
/** @this {FORGE.Button} */
set: function(value)
{
if(typeof value !== "boolean")
{
return;
}
this._autoHeight = value;
this._updateAutoSize();
}
});