/* Copyright (c) 2009, Yahoo! Inc. All rights reserved. Code licensed under the BSD License: http://developer.yahoo.net/yui/license.txt version: 2.8.0r4 */ /** * @module button * @description
The Button Control enables the creation of rich, graphical * buttons that function like traditional HTML form buttons. Unlike * traditional HTML form buttons, buttons created with the Button Control can have * a label that is different from its value. With the inclusion of the optional * Menu Control, the Button Control can also be * used to create menu buttons and split buttons, controls that are not * available natively in HTML. The Button Control can also be thought of as a * way to create more visually engaging implementations of the browser's * default radio-button and check-box controls.
*The Button Control supports the following types:
*<input>
, <button>
,
* <a>
, or <span>
element to
* be used to create the button.
* @param {HTMLInputElement|
* HTMLButtonElement|HTMLElement} p_oElement Object reference for the
* <input>
, <button>
,
* <a>
, or <span>
element to be
* used to create the button.
* @param {Object} p_oElement Object literal specifying a set of
* configuration attributes used to create the button.
* @param {Object} p_oAttributes Optional. Object literal specifying a set
* of configuration attributes used to create the button.
* @namespace YAHOO.widget
* @class Button
* @constructor
* @extends YAHOO.util.Element
*/
// Shorthard for utilities
var Dom = YAHOO.util.Dom,
Event = YAHOO.util.Event,
Lang = YAHOO.lang,
UA = YAHOO.env.ua,
Overlay = YAHOO.widget.Overlay,
Menu = YAHOO.widget.Menu,
// Private member variables
m_oButtons = {}, // Collection of all Button instances
m_oOverlayManager = null, // YAHOO.widget.OverlayManager instance
m_oSubmitTrigger = null, // The button that submitted the form
m_oFocusedButton = null; // The button that has focus
// Private methods
/**
* @method createInputElement
* @description Creates an <input>
element of the
* specified type.
* @private
* @param {String} p_sType String specifying the type of
* <input>
element to create.
* @param {String} p_sName String specifying the name of
* <input>
element to create.
* @param {String} p_sValue String specifying the value of
* <input>
element to create.
* @param {String} p_bChecked Boolean specifying if the
* <input>
element is to be checked.
* @return {HTMLInputElement}
*/
function createInputElement(p_sType, p_sName, p_sValue, p_bChecked) {
var oInput,
sInput;
if (Lang.isString(p_sType) && Lang.isString(p_sName)) {
if (UA.ie) {
/*
For IE it is necessary to create the element with the
"type," "name," "value," and "checked" properties set all
at once.
*/
sInput = "";
oInput = document.createElement(sInput);
}
else {
oInput = document.createElement("input");
oInput.name = p_sName;
oInput.type = p_sType;
if (p_bChecked) {
oInput.checked = true;
}
}
oInput.value = p_sValue;
}
return oInput;
}
/**
* @method setAttributesFromSrcElement
* @description Gets the values for all the attributes of the source element
* (either <input>
or <a>
) that
* map to Button configuration attributes and sets them into a collection
* that is passed to the Button constructor.
* @private
* @param {HTMLInputElement|HTMLAnchorElement} p_oElement Object reference to the HTML
* element (either <input>
or <span>
*
) used to create the button.
* @param {Object} p_oAttributes Object reference for the collection of
* configuration attributes used to create the button.
*/
function setAttributesFromSrcElement(p_oElement, p_oAttributes) {
var sSrcElementNodeName = p_oElement.nodeName.toUpperCase(),
sClass = (this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME),
me = this,
oAttribute,
oRootNode,
sText;
/**
* @method setAttributeFromDOMAttribute
* @description Gets the value of the specified DOM attribute and sets it
* into the collection of configuration attributes used to configure
* the button.
* @private
* @param {String} p_sAttribute String representing the name of the
* attribute to retrieve from the DOM element.
*/
function setAttributeFromDOMAttribute(p_sAttribute) {
if (!(p_sAttribute in p_oAttributes)) {
/*
Need to use "getAttributeNode" instead of "getAttribute"
because using "getAttribute," IE will return the innerText
of a <button>
for the value attribute
rather than the value of the "value" attribute.
*/
oAttribute = p_oElement.getAttributeNode(p_sAttribute);
if (oAttribute && ("value" in oAttribute)) {
p_oAttributes[p_sAttribute] = oAttribute.value;
}
}
}
/**
* @method setFormElementProperties
* @description Gets the value of the attributes from the form element
* and sets them into the collection of configuration attributes used to
* configure the button.
* @private
*/
function setFormElementProperties() {
setAttributeFromDOMAttribute("type");
if (p_oAttributes.type == "button") {
p_oAttributes.type = "push";
}
if (!("disabled" in p_oAttributes)) {
p_oAttributes.disabled = p_oElement.disabled;
}
setAttributeFromDOMAttribute("name");
setAttributeFromDOMAttribute("value");
setAttributeFromDOMAttribute("title");
}
switch (sSrcElementNodeName) {
case "A":
p_oAttributes.type = "link";
setAttributeFromDOMAttribute("href");
setAttributeFromDOMAttribute("target");
break;
case "INPUT":
setFormElementProperties();
if (!("checked" in p_oAttributes)) {
p_oAttributes.checked = p_oElement.checked;
}
break;
case "BUTTON":
setFormElementProperties();
oRootNode = p_oElement.parentNode.parentNode;
if (Dom.hasClass(oRootNode, sClass + "-checked")) {
p_oAttributes.checked = true;
}
if (Dom.hasClass(oRootNode, sClass + "-disabled")) {
p_oAttributes.disabled = true;
}
p_oElement.removeAttribute("value");
p_oElement.setAttribute("type", "button");
break;
}
p_oElement.removeAttribute("id");
p_oElement.removeAttribute("name");
if (!("tabindex" in p_oAttributes)) {
p_oAttributes.tabindex = p_oElement.tabIndex;
}
if (!("label" in p_oAttributes)) {
// Set the "label" property
sText = sSrcElementNodeName == "INPUT" ?
p_oElement.value : p_oElement.innerHTML;
if (sText && sText.length > 0) {
p_oAttributes.label = sText;
}
}
}
/**
* @method initConfig
* @description Initializes the set of configuration attributes that are
* used to instantiate the button.
* @private
* @param {Object} Object representing the button's set of
* configuration attributes.
*/
function initConfig(p_oConfig) {
var oAttributes = p_oConfig.attributes,
oSrcElement = oAttributes.srcelement,
sSrcElementNodeName = oSrcElement.nodeName.toUpperCase(),
me = this;
if (sSrcElementNodeName == this.NODE_NAME) {
p_oConfig.element = oSrcElement;
p_oConfig.id = oSrcElement.id;
Dom.getElementsBy(function (p_oElement) {
switch (p_oElement.nodeName.toUpperCase()) {
case "BUTTON":
case "A":
case "INPUT":
setAttributesFromSrcElement.call(me, p_oElement,
oAttributes);
break;
}
}, "*", oSrcElement);
}
else {
switch (sSrcElementNodeName) {
case "BUTTON":
case "A":
case "INPUT":
setAttributesFromSrcElement.call(this, oSrcElement,
oAttributes);
break;
}
}
}
// Constructor
YAHOO.widget.Button = function (p_oElement, p_oAttributes) {
if (!Overlay && YAHOO.widget.Overlay) {
Overlay = YAHOO.widget.Overlay;
}
if (!Menu && YAHOO.widget.Menu) {
Menu = YAHOO.widget.Menu;
}
var fnSuperClass = YAHOO.widget.Button.superclass.constructor,
oConfig,
oElement;
if (arguments.length == 1 && !Lang.isString(p_oElement) && !p_oElement.nodeName) {
if (!p_oElement.id) {
p_oElement.id = Dom.generateId();
}
fnSuperClass.call(this, (this.createButtonElement(p_oElement.type)), p_oElement);
}
else {
oConfig = { element: null, attributes: (p_oAttributes || {}) };
if (Lang.isString(p_oElement)) {
oElement = Dom.get(p_oElement);
if (oElement) {
if (!oConfig.attributes.id) {
oConfig.attributes.id = p_oElement;
}
oConfig.attributes.srcelement = oElement;
initConfig.call(this, oConfig);
if (!oConfig.element) {
oConfig.element = this.createButtonElement(oConfig.attributes.type);
}
fnSuperClass.call(this, oConfig.element, oConfig.attributes);
}
}
else if (p_oElement.nodeName) {
if (!oConfig.attributes.id) {
if (p_oElement.id) {
oConfig.attributes.id = p_oElement.id;
}
else {
oConfig.attributes.id = Dom.generateId();
}
}
oConfig.attributes.srcelement = p_oElement;
initConfig.call(this, oConfig);
if (!oConfig.element) {
oConfig.element = this.createButtonElement(oConfig.attributes.type);
}
fnSuperClass.call(this, oConfig.element, oConfig.attributes);
}
}
};
YAHOO.extend(YAHOO.widget.Button, YAHOO.util.Element, {
// Protected properties
/**
* @property _button
* @description Object reference to the button's internal
* <a>
or <button>
element.
* @default null
* @protected
* @type HTMLAnchorElement|HTMLButtonElement
*/
_button: null,
/**
* @property _menu
* @description Object reference to the button's menu.
* @default null
* @protected
* @type {YAHOO.widget.Overlay|
* YAHOO.widget.Menu}
*/
_menu: null,
/**
* @property _hiddenFields
* @description Object reference to the <input>
* element, or array of HTML form elements used to represent the button
* when its parent form is submitted.
* @default null
* @protected
* @type HTMLInputElement|Array
*/
_hiddenFields: null,
/**
* @property _onclickAttributeValue
* @description Object reference to the button's current value for the
* "onclick" configuration attribute.
* @default null
* @protected
* @type Object
*/
_onclickAttributeValue: null,
/**
* @property _activationKeyPressed
* @description Boolean indicating if the key(s) that toggle the button's
* "active" state have been pressed.
* @default false
* @protected
* @type Boolean
*/
_activationKeyPressed: false,
/**
* @property _activationButtonPressed
* @description Boolean indicating if the mouse button that toggles
* the button's "active" state has been pressed.
* @default false
* @protected
* @type Boolean
*/
_activationButtonPressed: false,
/**
* @property _hasKeyEventHandlers
* @description Boolean indicating if the button's "blur", "keydown" and
* "keyup" event handlers are assigned
* @default false
* @protected
* @type Boolean
*/
_hasKeyEventHandlers: false,
/**
* @property _hasMouseEventHandlers
* @description Boolean indicating if the button's "mouseout,"
* "mousedown," and "mouseup" event handlers are assigned
* @default false
* @protected
* @type Boolean
*/
_hasMouseEventHandlers: false,
/**
* @property _nOptionRegionX
* @description Number representing the X coordinate of the leftmost edge of the Button's
* option region. Applies only to Buttons of type "split".
* @default 0
* @protected
* @type Number
*/
_nOptionRegionX: 0,
// Constants
/**
* @property CLASS_NAME_PREFIX
* @description Prefix used for all class names applied to a Button.
* @default "yui-"
* @final
* @type String
*/
CLASS_NAME_PREFIX: "yui-",
/**
* @property NODE_NAME
* @description The name of the node to be used for the button's
* root element.
* @default "SPAN"
* @final
* @type String
*/
NODE_NAME: "SPAN",
/**
* @property CHECK_ACTIVATION_KEYS
* @description Array of numbers representing keys that (when pressed)
* toggle the button's "checked" attribute.
* @default [32]
* @final
* @type Array
*/
CHECK_ACTIVATION_KEYS: [32],
/**
* @property ACTIVATION_KEYS
* @description Array of numbers representing keys that (when presed)
* toggle the button's "active" state.
* @default [13, 32]
* @final
* @type Array
*/
ACTIVATION_KEYS: [13, 32],
/**
* @property OPTION_AREA_WIDTH
* @description Width (in pixels) of the area of a split button that
* when pressed will display a menu.
* @default 20
* @final
* @type Number
*/
OPTION_AREA_WIDTH: 20,
/**
* @property CSS_CLASS_NAME
* @description String representing the CSS class(es) to be applied to
* the button's root element.
* @default "button"
* @final
* @type String
*/
CSS_CLASS_NAME: "button",
// Protected attribute setter methods
/**
* @method _setType
* @description Sets the value of the button's "type" attribute.
* @protected
* @param {String} p_sType String indicating the value for the button's
* "type" attribute.
*/
_setType: function (p_sType) {
if (p_sType == "split") {
this.on("option", this._onOption);
}
},
/**
* @method _setLabel
* @description Sets the value of the button's "label" attribute.
* @protected
* @param {String} p_sLabel String indicating the value for the button's
* "label" attribute.
*/
_setLabel: function (p_sLabel) {
this._button.innerHTML = p_sLabel;
/*
Remove and add the default class name from the root element
for Gecko to ensure that the button shrinkwraps to the label.
Without this the button will not be rendered at the correct
width when the label changes. The most likely cause for this
bug is button's use of the Gecko-specific CSS display type of
"-moz-inline-box" to simulate "inline-block" supported by IE,
Safari and Opera.
*/
var sClass,
nGeckoVersion = UA.gecko;
if (nGeckoVersion && nGeckoVersion < 1.9 && Dom.inDocument(this.get("element"))) {
sClass = (this.CLASS_NAME_PREFIX + this.CSS_CLASS_NAME);
this.removeClass(sClass);
Lang.later(0, this, this.addClass, sClass);
}
},
/**
* @method _setTabIndex
* @description Sets the value of the button's "tabindex" attribute.
* @protected
* @param {Number} p_nTabIndex Number indicating the value for the
* button's "tabindex" attribute.
*/
_setTabIndex: function (p_nTabIndex) {
this._button.tabIndex = p_nTabIndex;
},
/**
* @method _setTitle
* @description Sets the value of the button's "title" attribute.
* @protected
* @param {String} p_nTabIndex Number indicating the value for
* the button's "title" attribute.
*/
_setTitle: function (p_sTitle) {
if (this.get("type") != "link") {
this._button.title = p_sTitle;
}
},
/**
* @method _setDisabled
* @description Sets the value of the button's "disabled" attribute.
* @protected
* @param {Boolean} p_bDisabled Boolean indicating the value for
* the button's "disabled" attribute.
*/
_setDisabled: function (p_bDisabled) {
if (this.get("type") != "link") {
if (p_bDisabled) {
if (this._menu) {
this._menu.hide();
}
if (this.hasFocus()) {
this.blur();
}
this._button.setAttribute("disabled", "disabled");
this.addStateCSSClasses("disabled");
this.removeStateCSSClasses("hover");
this.removeStateCSSClasses("active");
this.removeStateCSSClasses("focus");
}
else {
this._button.removeAttribute("disabled");
this.removeStateCSSClasses("disabled");
}
}
},
/**
* @method _setHref
* @description Sets the value of the button's "href" attribute.
* @protected
* @param {String} p_sHref String indicating the value for the button's
* "href" attribute.
*/
_setHref: function (p_sHref) {
if (this.get("type") == "link") {
this._button.href = p_sHref;
}
},
/**
* @method _setTarget
* @description Sets the value of the button's "target" attribute.
* @protected
* @param {String} p_sTarget String indicating the value for the button's
* "target" attribute.
*/
_setTarget: function (p_sTarget) {
if (this.get("type") == "link") {
this._button.setAttribute("target", p_sTarget);
}
},
/**
* @method _setChecked
* @description Sets the value of the button's "target" attribute.
* @protected
* @param {Boolean} p_bChecked Boolean indicating the value for
* the button's "checked" attribute.
*/
_setChecked: function (p_bChecked) {
var sType = this.get("type");
if (sType == "checkbox" || sType == "radio") {
if (p_bChecked) {
this.addStateCSSClasses("checked");
}
else {
this.removeStateCSSClasses("checked");
}
}
},
/**
* @method _setMenu
* @description Sets the value of the button's "menu" attribute.
* @protected
* @param {Object} p_oMenu Object indicating the value for the button's
* "menu" attribute.
*/
_setMenu: function (p_oMenu) {
var bLazyLoad = this.get("lazyloadmenu"),
oButtonElement = this.get("element"),
sMenuCSSClassName,
/*
Boolean indicating if the value of p_oMenu is an instance
of YAHOO.widget.Menu or YAHOO.widget.Overlay.
*/
bInstance = false,
oMenu,
oMenuElement,
oSrcElement;
function onAppendTo() {
oMenu.render(oButtonElement.parentNode);
this.removeListener("appendTo", onAppendTo);
}
function setMenuContainer() {
oMenu.cfg.queueProperty("container", oButtonElement.parentNode);
this.removeListener("appendTo", setMenuContainer);
}
function initMenu() {
var oContainer;
if (oMenu) {
Dom.addClass(oMenu.element, this.get("menuclassname"));
Dom.addClass(oMenu.element, this.CLASS_NAME_PREFIX + this.get("type") + "-button-menu");
oMenu.showEvent.subscribe(this._onMenuShow, null, this);
oMenu.hideEvent.subscribe(this._onMenuHide, null, this);
oMenu.renderEvent.subscribe(this._onMenuRender, null, this);
if (Menu && oMenu instanceof Menu) {
if (bLazyLoad) {
oContainer = this.get("container");
if (oContainer) {
oMenu.cfg.queueProperty("container", oContainer);
}
else {
this.on("appendTo", setMenuContainer);
}
}
oMenu.cfg.queueProperty("clicktohide", false);
oMenu.keyDownEvent.subscribe(this._onMenuKeyDown, this, true);
oMenu.subscribe("click", this._onMenuClick, this, true);
this.on("selectedMenuItemChange", this._onSelectedMenuItemChange);
oSrcElement = oMenu.srcElement;
if (oSrcElement && oSrcElement.nodeName.toUpperCase() == "SELECT") {
oSrcElement.style.display = "none";
oSrcElement.parentNode.removeChild(oSrcElement);
}
}
else if (Overlay && oMenu instanceof Overlay) {
if (!m_oOverlayManager) {
m_oOverlayManager = new YAHOO.widget.OverlayManager();
}
m_oOverlayManager.register(oMenu);
}
this._menu = oMenu;
if (!bInstance && !bLazyLoad) {
if (Dom.inDocument(oButtonElement)) {
oMenu.render(oButtonElement.parentNode);
}
else {
this.on("appendTo", onAppendTo);
}
}
}
}
if (Overlay) {
if (Menu) {
sMenuCSSClassName = Menu.prototype.CSS_CLASS_NAME;
}
if (p_oMenu && Menu && (p_oMenu instanceof Menu)) {
oMenu = p_oMenu;
bInstance = true;
initMenu.call(this);
}
else if (Overlay && p_oMenu && (p_oMenu instanceof Overlay)) {
oMenu = p_oMenu;
bInstance = true;
oMenu.cfg.queueProperty("visible", false);
initMenu.call(this);
}
else if (Menu && Lang.isArray(p_oMenu)) {
oMenu = new Menu(Dom.generateId(), { lazyload: bLazyLoad, itemdata: p_oMenu });
this._menu = oMenu;
this.on("appendTo", initMenu);
}
else if (Lang.isString(p_oMenu)) {
oMenuElement = Dom.get(p_oMenu);
if (oMenuElement) {
if (Menu && Dom.hasClass(oMenuElement, sMenuCSSClassName) ||
oMenuElement.nodeName.toUpperCase() == "SELECT") {
oMenu = new Menu(p_oMenu, { lazyload: bLazyLoad });
initMenu.call(this);
}
else if (Overlay) {
oMenu = new Overlay(p_oMenu, { visible: false });
initMenu.call(this);
}
}
}
else if (p_oMenu && p_oMenu.nodeName) {
if (Menu && Dom.hasClass(p_oMenu, sMenuCSSClassName) ||
p_oMenu.nodeName.toUpperCase() == "SELECT") {
oMenu = new Menu(p_oMenu, { lazyload: bLazyLoad });
initMenu.call(this);
}
else if (Overlay) {
if (!p_oMenu.id) {
Dom.generateId(p_oMenu);
}
oMenu = new Overlay(p_oMenu, { visible: false });
initMenu.call(this);
}
}
}
},
/**
* @method _setOnClick
* @description Sets the value of the button's "onclick" attribute.
* @protected
* @param {Object} p_oObject Object indicating the value for the button's
* "onclick" attribute.
*/
_setOnClick: function (p_oObject) {
/*
Remove any existing listeners if a "click" event handler
has already been specified.
*/
if (this._onclickAttributeValue &&
(this._onclickAttributeValue != p_oObject)) {
this.removeListener("click", this._onclickAttributeValue.fn);
this._onclickAttributeValue = null;
}
if (!this._onclickAttributeValue &&
Lang.isObject(p_oObject) &&
Lang.isFunction(p_oObject.fn)) {
this.on("click", p_oObject.fn, p_oObject.obj, p_oObject.scope);
this._onclickAttributeValue = p_oObject;
}
},
// Protected methods
/**
* @method _isActivationKey
* @description Determines if the specified keycode is one that toggles
* the button's "active" state.
* @protected
* @param {Number} p_nKeyCode Number representing the keycode to
* be evaluated.
* @return {Boolean}
*/
_isActivationKey: function (p_nKeyCode) {
var sType = this.get("type"),
aKeyCodes = (sType == "checkbox" || sType == "radio") ?
this.CHECK_ACTIVATION_KEYS : this.ACTIVATION_KEYS,
nKeyCodes = aKeyCodes.length,
bReturnVal = false,
i;
if (nKeyCodes > 0) {
i = nKeyCodes - 1;
do {
if (p_nKeyCode == aKeyCodes[i]) {
bReturnVal = true;
break;
}
}
while (i--);
}
return bReturnVal;
},
/**
* @method _isSplitButtonOptionKey
* @description Determines if the specified keycode is one that toggles
* the display of the split button's menu.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
* @return {Boolean}
*/
_isSplitButtonOptionKey: function (p_oEvent) {
var bShowMenu = (Event.getCharCode(p_oEvent) == 40);
var onKeyPress = function (p_oEvent) {
Event.preventDefault(p_oEvent);
this.removeListener("keypress", onKeyPress);
};
// Prevent the browser from scrolling the window
if (bShowMenu) {
if (UA.opera) {
this.on("keypress", onKeyPress);
}
Event.preventDefault(p_oEvent);
}
return bShowMenu;
},
/**
* @method _addListenersToForm
* @description Adds event handlers to the button's form.
* @protected
*/
_addListenersToForm: function () {
var oForm = this.getForm(),
onFormKeyPress = YAHOO.widget.Button.onFormKeyPress,
bHasKeyPressListener,
oSrcElement,
aListeners,
nListeners,
i;
if (oForm) {
Event.on(oForm, "reset", this._onFormReset, null, this);
Event.on(oForm, "submit", this._onFormSubmit, null, this);
oSrcElement = this.get("srcelement");
if (this.get("type") == "submit" ||
(oSrcElement && oSrcElement.type == "submit"))
{
aListeners = Event.getListeners(oForm, "keypress");
bHasKeyPressListener = false;
if (aListeners) {
nListeners = aListeners.length;
if (nListeners > 0) {
i = nListeners - 1;
do {
if (aListeners[i].fn == onFormKeyPress) {
bHasKeyPressListener = true;
break;
}
}
while (i--);
}
}
if (!bHasKeyPressListener) {
Event.on(oForm, "keypress", onFormKeyPress);
}
}
}
},
/**
* @method _showMenu
* @description Shows the button's menu.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event) that triggered
* the display of the menu.
*/
_showMenu: function (p_oEvent) {
if (YAHOO.widget.MenuManager) {
YAHOO.widget.MenuManager.hideVisible();
}
if (m_oOverlayManager) {
m_oOverlayManager.hideAll();
}
var oMenu = this._menu,
aMenuAlignment = this.get("menualignment"),
bFocusMenu = this.get("focusmenu"),
fnFocusMethod;
if (this._renderedMenu) {
oMenu.cfg.setProperty("context",
[this.get("element"), aMenuAlignment[0], aMenuAlignment[1]]);
oMenu.cfg.setProperty("preventcontextoverlap", true);
oMenu.cfg.setProperty("constraintoviewport", true);
}
else {
oMenu.cfg.queueProperty("context",
[this.get("element"), aMenuAlignment[0], aMenuAlignment[1]]);
oMenu.cfg.queueProperty("preventcontextoverlap", true);
oMenu.cfg.queueProperty("constraintoviewport", true);
}
/*
Refocus the Button before showing its Menu in case the call to
YAHOO.widget.MenuManager.hideVisible() resulted in another element in the
DOM being focused after another Menu was hidden.
*/
this.focus();
if (Menu && oMenu && (oMenu instanceof Menu)) {
// Since Menus automatically focus themselves when made visible, temporarily
// replace the Menu focus method so that the value of the Button's "focusmenu"
// attribute determines if the Menu should be focus when made visible.
fnFocusMethod = oMenu.focus;
oMenu.focus = function () {};
if (this._renderedMenu) {
oMenu.cfg.setProperty("minscrollheight", this.get("menuminscrollheight"));
oMenu.cfg.setProperty("maxheight", this.get("menumaxheight"));
}
else {
oMenu.cfg.queueProperty("minscrollheight", this.get("menuminscrollheight"));
oMenu.cfg.queueProperty("maxheight", this.get("menumaxheight"));
}
oMenu.show();
oMenu.focus = fnFocusMethod;
oMenu.align();
/*
Stop the propagation of the event so that the MenuManager
doesn't blur the menu after it gets focus.
*/
if (p_oEvent.type == "mousedown") {
Event.stopPropagation(p_oEvent);
}
if (bFocusMenu) {
oMenu.focus();
}
}
else if (Overlay && oMenu && (oMenu instanceof Overlay)) {
if (!this._renderedMenu) {
oMenu.render(this.get("element").parentNode);
}
oMenu.show();
oMenu.align();
}
},
/**
* @method _hideMenu
* @description Hides the button's menu.
* @protected
*/
_hideMenu: function () {
var oMenu = this._menu;
if (oMenu) {
oMenu.hide();
}
},
// Protected event handlers
/**
* @method _onMouseOver
* @description "mouseover" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onMouseOver: function (p_oEvent) {
var sType = this.get("type"),
oElement,
nOptionRegionX;
if (sType === "split") {
oElement = this.get("element");
nOptionRegionX =
(Dom.getX(oElement) + (oElement.offsetWidth - this.OPTION_AREA_WIDTH));
this._nOptionRegionX = nOptionRegionX;
}
if (!this._hasMouseEventHandlers) {
if (sType === "split") {
this.on("mousemove", this._onMouseMove);
}
this.on("mouseout", this._onMouseOut);
this._hasMouseEventHandlers = true;
}
this.addStateCSSClasses("hover");
if (sType === "split" && (Event.getPageX(p_oEvent) > nOptionRegionX)) {
this.addStateCSSClasses("hoveroption");
}
if (this._activationButtonPressed) {
this.addStateCSSClasses("active");
}
if (this._bOptionPressed) {
this.addStateCSSClasses("activeoption");
}
if (this._activationButtonPressed || this._bOptionPressed) {
Event.removeListener(document, "mouseup", this._onDocumentMouseUp);
}
},
/**
* @method _onMouseMove
* @description "mousemove" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onMouseMove: function (p_oEvent) {
var nOptionRegionX = this._nOptionRegionX;
if (nOptionRegionX) {
if (Event.getPageX(p_oEvent) > nOptionRegionX) {
this.addStateCSSClasses("hoveroption");
}
else {
this.removeStateCSSClasses("hoveroption");
}
}
},
/**
* @method _onMouseOut
* @description "mouseout" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onMouseOut: function (p_oEvent) {
var sType = this.get("type");
this.removeStateCSSClasses("hover");
if (sType != "menu") {
this.removeStateCSSClasses("active");
}
if (this._activationButtonPressed || this._bOptionPressed) {
Event.on(document, "mouseup", this._onDocumentMouseUp, null, this);
}
if (sType === "split" && (Event.getPageX(p_oEvent) > this._nOptionRegionX)) {
this.removeStateCSSClasses("hoveroption");
}
},
/**
* @method _onDocumentMouseUp
* @description "mouseup" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onDocumentMouseUp: function (p_oEvent) {
this._activationButtonPressed = false;
this._bOptionPressed = false;
var sType = this.get("type"),
oTarget,
oMenuElement;
if (sType == "menu" || sType == "split") {
oTarget = Event.getTarget(p_oEvent);
oMenuElement = this._menu.element;
if (oTarget != oMenuElement &&
!Dom.isAncestor(oMenuElement, oTarget)) {
this.removeStateCSSClasses((sType == "menu" ?
"active" : "activeoption"));
this._hideMenu();
}
}
Event.removeListener(document, "mouseup", this._onDocumentMouseUp);
},
/**
* @method _onMouseDown
* @description "mousedown" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onMouseDown: function (p_oEvent) {
var sType,
bReturnVal = true;
function onMouseUp() {
this._hideMenu();
this.removeListener("mouseup", onMouseUp);
}
if ((p_oEvent.which || p_oEvent.button) == 1) {
if (!this.hasFocus()) {
this.focus();
}
sType = this.get("type");
if (sType == "split") {
if (Event.getPageX(p_oEvent) > this._nOptionRegionX) {
this.fireEvent("option", p_oEvent);
bReturnVal = false;
}
else {
this.addStateCSSClasses("active");
this._activationButtonPressed = true;
}
}
else if (sType == "menu") {
if (this.isActive()) {
this._hideMenu();
this._activationButtonPressed = false;
}
else {
this._showMenu(p_oEvent);
this._activationButtonPressed = true;
}
}
else {
this.addStateCSSClasses("active");
this._activationButtonPressed = true;
}
if (sType == "split" || sType == "menu") {
this._hideMenuTimer = Lang.later(250, this, this.on, ["mouseup", onMouseUp]);
}
}
return bReturnVal;
},
/**
* @method _onMouseUp
* @description "mouseup" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onMouseUp: function (p_oEvent) {
var sType = this.get("type"),
oHideMenuTimer = this._hideMenuTimer,
bReturnVal = true;
if (oHideMenuTimer) {
oHideMenuTimer.cancel();
}
if (sType == "checkbox" || sType == "radio") {
this.set("checked", !(this.get("checked")));
}
this._activationButtonPressed = false;
if (sType != "menu") {
this.removeStateCSSClasses("active");
}
if (sType == "split" && Event.getPageX(p_oEvent) > this._nOptionRegionX) {
bReturnVal = false;
}
return bReturnVal;
},
/**
* @method _onFocus
* @description "focus" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onFocus: function (p_oEvent) {
var oElement;
this.addStateCSSClasses("focus");
if (this._activationKeyPressed) {
this.addStateCSSClasses("active");
}
m_oFocusedButton = this;
if (!this._hasKeyEventHandlers) {
oElement = this._button;
Event.on(oElement, "blur", this._onBlur, null, this);
Event.on(oElement, "keydown", this._onKeyDown, null, this);
Event.on(oElement, "keyup", this._onKeyUp, null, this);
this._hasKeyEventHandlers = true;
}
this.fireEvent("focus", p_oEvent);
},
/**
* @method _onBlur
* @description "blur" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onBlur: function (p_oEvent) {
this.removeStateCSSClasses("focus");
if (this.get("type") != "menu") {
this.removeStateCSSClasses("active");
}
if (this._activationKeyPressed) {
Event.on(document, "keyup", this._onDocumentKeyUp, null, this);
}
m_oFocusedButton = null;
this.fireEvent("blur", p_oEvent);
},
/**
* @method _onDocumentKeyUp
* @description "keyup" event handler for the document.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onDocumentKeyUp: function (p_oEvent) {
if (this._isActivationKey(Event.getCharCode(p_oEvent))) {
this._activationKeyPressed = false;
Event.removeListener(document, "keyup", this._onDocumentKeyUp);
}
},
/**
* @method _onKeyDown
* @description "keydown" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onKeyDown: function (p_oEvent) {
var oMenu = this._menu;
if (this.get("type") == "split" &&
this._isSplitButtonOptionKey(p_oEvent)) {
this.fireEvent("option", p_oEvent);
}
else if (this._isActivationKey(Event.getCharCode(p_oEvent))) {
if (this.get("type") == "menu") {
this._showMenu(p_oEvent);
}
else {
this._activationKeyPressed = true;
this.addStateCSSClasses("active");
}
}
if (oMenu && oMenu.cfg.getProperty("visible") &&
Event.getCharCode(p_oEvent) == 27) {
oMenu.hide();
this.focus();
}
},
/**
* @method _onKeyUp
* @description "keyup" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onKeyUp: function (p_oEvent) {
var sType;
if (this._isActivationKey(Event.getCharCode(p_oEvent))) {
sType = this.get("type");
if (sType == "checkbox" || sType == "radio") {
this.set("checked", !(this.get("checked")));
}
this._activationKeyPressed = false;
if (this.get("type") != "menu") {
this.removeStateCSSClasses("active");
}
}
},
/**
* @method _onClick
* @description "click" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onClick: function (p_oEvent) {
var sType = this.get("type"),
oForm,
oSrcElement,
bReturnVal;
switch (sType) {
case "submit":
if (p_oEvent.returnValue !== false) {
this.submitForm();
}
break;
case "reset":
oForm = this.getForm();
if (oForm) {
oForm.reset();
}
break;
case "split":
if (this._nOptionRegionX > 0 &&
(Event.getPageX(p_oEvent) > this._nOptionRegionX)) {
bReturnVal = false;
}
else {
this._hideMenu();
oSrcElement = this.get("srcelement");
if (oSrcElement && oSrcElement.type == "submit" &&
p_oEvent.returnValue !== false) {
this.submitForm();
}
}
break;
}
return bReturnVal;
},
/**
* @method _onDblClick
* @description "dblclick" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onDblClick: function (p_oEvent) {
var bReturnVal = true;
if (this.get("type") == "split" && Event.getPageX(p_oEvent) > this._nOptionRegionX) {
bReturnVal = false;
}
return bReturnVal;
},
/**
* @method _onAppendTo
* @description "appendTo" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onAppendTo: function (p_oEvent) {
/*
It is necessary to call "_addListenersToForm" using
"setTimeout" to make sure that the button's "form" property
returns a node reference. Sometimes, if you try to get the
reference immediately after appending the field, it is null.
*/
Lang.later(0, this, this._addListenersToForm);
},
/**
* @method _onFormReset
* @description "reset" event handler for the button's form.
* @protected
* @param {Event} p_oEvent Object representing the DOM event
* object passed back by the event utility (YAHOO.util.Event).
*/
_onFormReset: function (p_oEvent) {
var sType = this.get("type"),
oMenu = this._menu;
if (sType == "checkbox" || sType == "radio") {
this.resetValue("checked");
}
if (Menu && oMenu && (oMenu instanceof Menu)) {
this.resetValue("selectedMenuItem");
}
},
/**
* @method _onFormSubmit
* @description "submit" event handler for the button's form.
* @protected
* @param {Event} p_oEvent Object representing the DOM event
* object passed back by the event utility (YAHOO.util.Event).
*/
_onFormSubmit: function (p_oEvent) {
this.createHiddenFields();
},
/**
* @method _onDocumentMouseDown
* @description "mousedown" event handler for the document.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onDocumentMouseDown: function (p_oEvent) {
var oTarget = Event.getTarget(p_oEvent),
oButtonElement = this.get("element"),
oMenuElement = this._menu.element;
if (oTarget != oButtonElement &&
!Dom.isAncestor(oButtonElement, oTarget) &&
oTarget != oMenuElement &&
!Dom.isAncestor(oMenuElement, oTarget)) {
this._hideMenu();
// In IE when the user mouses down on a focusable element
// that element will be focused and become the "activeElement".
// (http://msdn.microsoft.com/en-us/library/ms533065(VS.85).aspx)
// However, there is a bug in IE where if there is a
// positioned element with a focused descendant that is
// hidden in response to the mousedown event, the target of
// the mousedown event will appear to have focus, but will
// not be set as the activeElement. This will result
// in the element not firing key events, even though it
// appears to have focus. The following call to "setActive"
// fixes this bug.
if (UA.ie && oTarget.focus) {
oTarget.setActive();
}
Event.removeListener(document, "mousedown",
this._onDocumentMouseDown);
}
},
/**
* @method _onOption
* @description "option" event handler for the button.
* @protected
* @param {Event} p_oEvent Object representing the DOM event object
* passed back by the event utility (YAHOO.util.Event).
*/
_onOption: function (p_oEvent) {
if (this.hasClass(this.CLASS_NAME_PREFIX + "split-button-activeoption")) {
this._hideMenu();
this._bOptionPressed = false;
}
else {
this._showMenu(p_oEvent);
this._bOptionPressed = true;
}
},
/**
* @method _onMenuShow
* @description "show" event handler for the button's menu.
* @private
* @param {String} p_sType String representing the name of the event
* that was fired.
*/
_onMenuShow: function (p_sType) {
Event.on(document, "mousedown", this._onDocumentMouseDown,
null, this);
var sState = (this.get("type") == "split") ? "activeoption" : "active";
this.addStateCSSClasses(sState);
},
/**
* @method _onMenuHide
* @description "hide" event handler for the button's menu.
* @private
* @param {String} p_sType String representing the name of the event
* that was fired.
*/
_onMenuHide: function (p_sType) {
var sState = (this.get("type") == "split") ? "activeoption" : "active";
this.removeStateCSSClasses(sState);
if (this.get("type") == "split") {
this._bOptionPressed = false;
}
},
/**
* @method _onMenuKeyDown
* @description "keydown" event handler for the button's menu.
* @private
* @param {String} p_sType String representing the name of the event
* that was fired.
* @param {Array} p_aArgs Array of arguments sent when the event
* was fired.
*/
_onMenuKeyDown: function (p_sType, p_aArgs) {
var oEvent = p_aArgs[0];
if (Event.getCharCode(oEvent) == 27) {
this.focus();
if (this.get("type") == "split") {
this._bOptionPressed = false;
}
}
},
/**
* @method _onMenuRender
* @description "render" event handler for the button's menu.
* @private
* @param {String} p_sType String representing the name of the
* event thatwas fired.
*/
_onMenuRender: function (p_sType) {
var oButtonElement = this.get("element"),
oButtonParent = oButtonElement.parentNode,
oMenu = this._menu,
oMenuElement = oMenu.element,
oSrcElement = oMenu.srcElement,
oItem;
if (oButtonParent != oMenuElement.parentNode) {
oButtonParent.appendChild(oMenuElement);
}
this._renderedMenu = true;
// If the user has designated an