1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc.
  5  *
  6  * The contents of this file are subject to the Common Public Attribution License Version 1.0 (the "License");
  7  * you may not use this file except in compliance with the License.
  8  * You may obtain a copy of the License at: https://www.zimbra.com/license
  9  * The License is based on the Mozilla Public License Version 1.1 but Sections 14 and 15
 10  * have been added to cover use of software over a computer network and provide for limited attribution
 11  * for the Original Developer. In addition, Exhibit A has been modified to be consistent with Exhibit B.
 12  *
 13  * Software distributed under the License is distributed on an "AS IS" basis,
 14  * WITHOUT WARRANTY OF ANY KIND, either express or implied.
 15  * See the License for the specific language governing rights and limitations under the License.
 16  * The Original Code is Zimbra Open Source Web Client.
 17  * The Initial Developer of the Original Code is Zimbra, Inc.  All rights to the Original Code were
 18  * transferred by Zimbra, Inc. to Synacor, Inc. on September 14, 2015.
 19  *
 20  * All portions of the code are Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * @overview
 26  * This file defines a button.
 27  *
 28  */
 29 
 30 /**
 31  * Creates a button.
 32  * @class
 33  * This class represents a button, which is basically a smart label that can handle
 34  * various UI events. It knows when it has been hovered (the mouse is over it),
 35  * when it is active (mouse down), and when it has been pressed (mouse up).
 36  * In addition to a label's image and/or text, a button may have a dropdown menu.
 37  * <p>
 38  * There are several different types of button:
 39  * <ul>
 40  * <li><i>Push</i> - This is the standard push button</li>
 41  * <li><i>Toggle</i> - This is a button that exhibits selectable behaviour when clicked
 42  * 		e.g. on/off. To make a button selectable style "or" {@link DwtButton.SELECT_STYLE}
 43  * 		to the constructor's style parameter</li>
 44  * <li><i>Menu</i> - By setting a mene via the {@link #setMenu} method a button will become
 45  * 		a drop down or menu button.</li>
 46  * </ul>
 47  *
 48  * <h4>CSS</h4>
 49  * <ul>
 50  * <li><i>className</i>-hover - hovered style</li>
 51  * <li><i>className</i>-active - mouse down style</li>
 52  * <li><i>className</i>-selected - permanently down style</li>
 53  * <li><i>className</i>-disabled - disabled style</li>
 54  * </ul>
 55  *
 56  * <h4>Keyboard Actions</h4>
 57  * <ul>
 58  * <li>{@link DwtKeyMap.SELECT} - triggers the button</li>
 59  * <li>{@link DwtKeyMap.SUBMENU} - display's the button's submenu if one is set</li>
 60  * </ul>
 61  *
 62  * @author Ross Dargahi
 63  * @author Conrad Damon
 64  * 
 65  * @param {hash}	params		a hash of parameters
 66  * @param {DwtComposite}	params.parent	the parent widget
 67  * @param {constant}	params.style		the button style
 68  * @param {string}	params.className		the CSS class
 69  * @param {constant}	params.posStyle		the positioning style
 70  * @param {DwtButton.ACTION_MOUSEUP|DwtButton.ACTION_MOUSEDOWN}	params.actionTiming	if {@link DwtButton.ACTION_MOUSEUP}, then the button is triggered
 71  *											on mouseup events, else if {@link DwtButton.ACTION_MOUSEDOWN},
 72  * 											then the button is triggered on mousedown events
 73  * @param {string}	params.id		the id to use for the control HTML element
 74  * @param {number}	params.index 		the index at which to add this control among parent's children
 75  * @param {hash}	params.listeners		a hash of event listeners
 76  *        
 77  * @extends		DwtLabel
 78  */
 79 DwtButton = function(params) {
 80 	if (arguments.length == 0) { return; }
 81 	params = Dwt.getParams(arguments, DwtButton.PARAMS);
 82 	
 83 	params.className = params.className || "ZButton";
 84 	DwtLabel.call(this, params);
 85 
 86 	var parent = params.parent;
 87 	if (!parent._hasSetMouseEvents || AjxEnv.isIE) {
 88 		this._setMouseEvents();
 89 	}
 90 	
 91 	var events;
 92 	if (parent._hasSetMouseEvents) {
 93 		events = AjxEnv.isIE ? [DwtEvent.ONMOUSEENTER, DwtEvent.ONMOUSELEAVE] : [];
 94 	} else {
 95 		events = AjxEnv.isIE
 96 			? [DwtEvent.ONMOUSEENTER, DwtEvent.ONMOUSELEAVE]
 97 			: [DwtEvent.ONMOUSEOVER, DwtEvent.ONMOUSEOUT];
 98 		events.push(DwtEvent.ONMOUSEDOWN, DwtEvent.ONMOUSEUP, DwtEvent.ONCLICK);
 99 	}
100 	if (events && events.length) {
101 		this._setEventHdlrs(events);
102 	}
103 	this._listeners = params.listeners || DwtButton._listeners;
104 	this._addMouseListeners();
105 	this._ignoreInternalOverOut = true;
106 	
107 	this._dropDownEvtMgr = new AjxEventMgr();
108 
109 	this._selected = false;
110 
111 	this._actionTiming = params.actionTiming || DwtButton.ACTION_MOUSEUP;
112 	this.__preventMenuFocus = null;
113 	this._menuPopupStyle = DwtButton.MENU_POPUP_STYLE_BELOW;
114 };
115 
116 DwtButton.prototype = new DwtLabel;
117 DwtButton.prototype.constructor = DwtButton;
118 
119 DwtButton.prototype.isDwtButton = true;
120 DwtButton.prototype.toString = function() { return "DwtButton"; };
121 
122 DwtButton.prototype.role = 'button';
123 
124 //
125 // Constants
126 //
127 DwtButton.PARAMS = ["parent", "style", "className", "posStyle", "actionTiming", "id", "index", "listeners"];
128 DwtButton.TOGGLE_STYLE = DwtLabel._LAST_STYLE * 2; // NOTE: These must be powers of 2 because we do bit-arithmetic to check the style.
129 DwtButton.ALWAYS_FLAT = DwtLabel._LAST_STYLE * 4;
130 DwtButton._LAST_STYLE = DwtButton.ALWAYS_FLAT;
131 
132 DwtButton.ACTION_MOUSEUP = 1;
133 DwtButton.ACTION_MOUSEDOWN = 2; // No special appearance when hovered or active
134 
135 DwtButton.NOTIFY_WINDOW = 500;  // Time (in ms) during which to block additional clicks from being processed
136 
137 DwtButton.MENU_POPUP_STYLE_BELOW	= "BELOW";		// menu pops up just below the button (default)
138 DwtButton.MENU_POPUP_STYLE_ABOVE	= "ABOVE";		// menu pops up above the button
139 DwtButton.MENU_POPUP_STYLE_RIGHT	= "RIGHT";		// menu pops up below the button, with right edges aligned
140 DwtButton.MENU_POPUP_STYLE_CASCADE	= "CASCADE";	// menu pops up to right of the button
141 
142 DwtButton.MOUSE_EVENTS = [DwtEvent.ONMOUSEDOWN, DwtEvent.ONMOUSEUP];
143 
144 if (AjxEnv.isIE) {
145 	DwtButton.MOUSE_EVENTS.push(DwtEvent.ONMOUSEENTER, DwtEvent.ONMOUSELEAVE);
146 } else {
147 	DwtButton.MOUSE_EVENTS.push(DwtEvent.ONMOUSEOVER, DwtEvent.ONMOUSEOUT);
148 }
149 
150 //
151 // Data
152 //
153 DwtButton.prototype.TEMPLATE = "dwt.Widgets#ZButton";
154 
155 //
156 // Public methods
157 //
158 
159 /**
160  * Disposes of the button.
161  * 
162  */
163 DwtButton.prototype.dispose =
164 function() {
165 	if (this._menu && this._menu.isDwtMenu && (this._menu.parent == this)) {
166 		this._menu.dispose();
167 		this._menu = null;
168 	}
169 	DwtLabel.prototype.dispose.call(this);
170 };
171 
172 /**
173  * Adds a listener to be notified when the button is pressed.
174  *
175  * @param {AjxListener}	listener	the listener
176  * @param {number}	index		the index at which to add listener
177  */
178 DwtButton.prototype.addSelectionListener =
179 function(listener, index) {
180 	this.addListener(DwtEvent.SELECTION, listener, index);
181 };
182 
183 /**
184  * Removes a selection listener.
185  *
186  * @param {AjxListener}		listener	the listener to remove
187  */
188 DwtButton.prototype.removeSelectionListener =
189 function(listener) {
190 	this.removeListener(DwtEvent.SELECTION, listener);
191 };
192 
193 /**
194  * Removes all the selection listeners.
195  */
196 DwtButton.prototype.removeSelectionListeners =
197 function() {
198 	this.removeAllListeners(DwtEvent.SELECTION);
199 };
200 
201 /**
202  * Adds a listener to be notified when the dropdown arrow is pressed.
203  *
204  * @param {AjxListener}		listener	the listener
205  */
206 DwtButton.prototype.addDropDownSelectionListener =
207 function(listener) {
208 	return this._dropDownEvtMgr.addListener(DwtEvent.SELECTION, listener);
209 };
210 
211 /**
212  * Removes a dropdown selection listener.
213  *
214  * @param {AjxListener}		listener	the listener to remove
215  */
216 DwtButton.prototype.removeDropDownSelectionListener =
217 function(listener) {
218 	this._dropDownEvtMgr.removeListener(DwtEvent.SELECTION, listener);
219 };
220 
221 // defaults for drop down images (set here once on prototype rather than on each button instance)
222 DwtButton.prototype._dropDownImg 	= "SelectPullDownArrow";
223 DwtButton.prototype._dropDownDepImg	= "SelectPullDownArrow";
224 DwtButton.prototype._dropDownHovImg = "SelectPullDownArrowHover";
225 
226 /**
227  * Sets the dropdown images.
228  * 
229  * @param	{string}	enabledImg		the enabled image
230  * @param	{string}	disImg		the disabled image
231  * @param	{string}	hovImg		the hover image
232  * @param	{string}	depImg		the depressed image
233  */
234 DwtButton.prototype.setDropDownImages =
235 function (enabledImg, disImg, hovImg, depImg) {
236 	this._dropDownImg = enabledImg;
237 	this._dropDownHovImg = hovImg;
238 	this._dropDownDepImg = depImg;
239 };
240 
241 /**
242  * Sets the Drop Down Hover Image
243  */
244 DwtButton.prototype.setDropDownHovImage =
245 function(hovImg) {
246     this._dropDownHovImg = hovImg;    
247 }
248 
249 /**
250  * @private
251  */
252 DwtButton.prototype._addMouseListeners =
253 function() {
254 	AjxUtil.foreach(DwtButton.MOUSE_EVENTS, (function(event) {
255 		this.addListener(event, this._listeners[event]);
256 	}).bind(this));
257 };
258 
259 /**
260  * @private
261  */
262 DwtButton.prototype._removeMouseListeners =
263 function() {
264 	AjxUtil.foreach(DwtButton.MOUSE_EVENTS, (function(event) {
265 		this.removeListener(event, this._listeners[event]);
266 	}).bind(this));
267 };
268 
269 /**
270  * Sets the display state.
271  * 
272  * @param	{string}	state		the display state
273  * @param	{boolean}	force		if <code>true</code>, force the state change
274  * @see		DwtControl
275  */
276 DwtButton.prototype.setDisplayState =
277 function(state, force) {
278     if (this._selected && state != DwtControl.SELECTED && !force) {
279         state = [ DwtControl.SELECTED, state ].join(" ");
280     }
281     DwtLabel.prototype.setDisplayState.call(this, state);
282 };
283 
284 /**
285  * Sets the enabled/disabled state of the button. A disabled button may have a different
286  * image, and greyed out text. The button (and its menu) will only have listeners if it
287  * is enabled.
288  *
289  * @param {boolean}	enabled			if <code>true</code>, enable the button
290  *
291  */
292 DwtButton.prototype.setEnabled =
293 function(enabled) {
294 	if (enabled != this._enabled) {
295 		DwtLabel.prototype.setEnabled.call(this, enabled); // handles image/text
296         if (enabled) {
297 			// bug fix #36253 - HACK for IE. ARGH!!!
298 			var el = (AjxEnv.isIE) ? this.getHtmlElement().firstChild : null;
299 			if (el) {
300 				var cname = el.className;
301 				el.className = "";
302 				el.className = cname;
303 			}
304 			this._addMouseListeners();
305 			// set event handler for pull down menu if applicable
306 			if (this._menu) {
307 				this._setDropDownCellMouseHandlers(true);
308                 if (this._dropDownEl && this._dropDownImg) {
309                     AjxImg.setImage(this._dropDownEl, this._dropDownImg);
310                 }
311             }
312 
313 		} else {
314 			this._removeMouseListeners();
315 			// remove event handlers for pull down menu if applicable
316 			if (this._menu) {
317 				this._setDropDownCellMouseHandlers(false);
318                 if (this._dropDownEl && this._dropDownImg) {
319                     AjxImg.setDisabledImage(this._dropDownEl, this._dropDownImg);
320                 }
321 			}
322 		}
323 	}
324 };
325 
326 /**
327  * Sets the main (enabled) image. If the button is currently enabled, the image is updated.
328  * 
329  * @param	{string}	imageInfo		the image
330  */
331 DwtButton.prototype.setImage =
332 function(imageInfo, direction) {
333 	// This button is set to not show image. Doing it here is safer against bugs resulting from dynamically modified images and text such as teh case of spam vs. "no spam".
334 	// This way you don't have to worry in that code whether we show image or not (Which could change for example as it does in this bug when moving the button to the main buttons).
335 	if (this.whatToShow && !this.whatToShow.showImage) {
336 		return;
337 	}
338 	DwtLabel.prototype.setImage.apply(this, arguments);
339 	this._setMinWidth();
340 };
341 
342 /**
343  * Sets the text.
344  * 
345  * @param	{string}	text		the text
346  */
347 DwtButton.prototype.setText =
348 function(text) {
349 
350 	//see explanation in setImage
351 	if (this.whatToShow && !this.whatToShow.showText) {
352 		return;
353 	}
354 	DwtLabel.prototype.setText.call(this, text);
355 	this._setMinWidth();
356 };
357 
358 /**
359  * @private
360  */
361 DwtButton.prototype._setMinWidth =
362 function() {
363 	if (this.getText() != null) {
364 		Dwt.addClass(this.getHtmlElement(), "ZHasText");
365 	} else {
366 		Dwt.delClass(this.getHtmlElement(), "ZHasText");
367 	}
368 };
369 
370 /**
371  * Sets the hover image.
372  * 
373  * @param	{string}	hoverImageInfo		the image
374  * @param	{string}	direction			position of the image
375  */
376 DwtButton.prototype.setHoverImage =
377 function (hoverImageInfo, direction) {
378 	direction = direction || (this._style & DwtLabel.IMAGE_RIGHT ? DwtLabel.RIGHT : DwtLabel.LEFT);
379 	this._hoverImageInfo = this._hoverImageInfo || {};
380 	this._hoverImageInfo[direction] = hoverImageInfo;
381 };
382 
383 /**
384  * Adds a dropdown menu to the button, available through a small down-arrow. If a
385  * callback is passed as the dropdown menu, it is called the first time the
386  * menu is requested. The callback must return a valid DwtMenu object.
387  *
388  * @param {hash}				params				hash of params:
389  * @param {DwtMenu|AjxCallback}	menu				the dropdown menu or a callback
390  * @param {boolean}				shouldToggle		if <code>true</code>, toggle
391  * @param {string}				menuPopupStyle		one of DwtButton.MENU_POPUP_STYLE_* (default is BELOW)
392  * @param {boolean}				popupAbove			if <code>true</code>, pop up the menu above the button
393  * @param {boolean}				popupRight			if <code>true</code>, align the right edge of the menu to the right edge of the button
394  */
395 DwtButton.prototype.setMenu =
396 function(params) {
397 	
398 	params = Dwt.getParams(arguments, DwtButton.setMenuParams, (arguments.length == 1 && arguments[0] && !arguments[0].menu));
399 
400     if (params){
401 	    this._menu = params.menu;
402     }
403 
404 	if (this._menu) {
405 		// if menu is a callback, wait until it's created to set menu-related properties
406 		if (this._menu.isDwtMenu) {
407 			this._shouldToggleMenu = (params.shouldToggle === true);
408 			if (params.popupAbove) {
409 				this._menuPopupStyle = DwtButton.MENU_POPUP_STYLE_ABOVE;
410 			}
411 			else if (params.popupRight) {
412 				this._menuPopupStyle = DwtButton.MENU_POPUP_STYLE_RIGHT;
413 			}
414 			else {
415 				this._menuPopupStyle = params.menuPopupStyle || DwtButton.MENU_POPUP_STYLE_BELOW;
416 			}
417 			this._menuAdded(this._menu);
418 		}
419 		else {
420 			this._savedMenuParams = params;
421 		}
422         if (this._dropDownEl) {
423 			Dwt.addClass(this.getHtmlElement(), "ZHasDropDown");
424 			if (this._dropDownImg) {
425             	AjxImg.setImage(this._dropDownEl, this._dropDownImg);
426 			}
427 
428 			// set event handler if applicable
429 			if (this._enabled) {
430 				this._setDropDownCellMouseHandlers(true);
431 			}
432 
433             if (this._menu.isDwtMenu) {
434                 this._menu.setAssociatedElementId(this._dropDownEl.id);
435             }
436 		}
437 		if ((this.__preventMenuFocus != null) && this._menu.isDwtMenu) {
438 			this._menu.dontStealFocus(this.__preventMenuFocus);
439 		}
440     }
441 	// removing menu
442     else if (this._dropDownEl) {
443 		Dwt.delClass(this.getHtmlElement(), "ZHasDropDown");
444         this._dropDownEl.innerHTML = "";
445     }
446 };
447 DwtButton.setMenuParams = ["menu", "shouldToggle", "followIconStyle", "popupAbove", "popupRight"];
448 
449 /**
450  * @private
451  */
452 DwtButton.prototype._setDropDownCellMouseHandlers =
453 function(set) {
454 	this._dropDownEventsEnabled = set;
455 };
456 
457 /**
458 * Gets the button menu.
459 *
460 * @param {boolean}		dontCreate	 if <code>true</code>, the menu will not be lazily created
461 * @return	{DwtMenu}	the menu or <code>null</code> if menu is not set
462 */
463 DwtButton.prototype.getMenu =
464 function(dontCreate) {
465 	if (this._menu && this._menu.isAjxCallback) {
466 		if (dontCreate) {
467 			return null;
468 		}
469 		var callback = this._menu;
470 		var params = this._savedMenuParams || {};
471 		params.menu = callback.run(this);
472 		this.setMenu(params);
473 		if ((this.__preventMenuFocus != null) && (this._menu.isDwtMenu)) {
474 			this._menu.dontStealFocus(this.__preventMenuFocus);
475 		}
476 	}
477     if (this._menu) {
478         this.setAttribute("menuId", this._menu._htmlElId);
479     }
480     return this._menu;
481 };
482 
483 /**
484  * Resets the button display to normal (not hovered or active).
485  * 
486  */
487 DwtButton.prototype.resetClassName =
488 function() {
489     this.setDisplayState(DwtControl.NORMAL);
490 };
491 
492 /**
493  * Sets whether actions for this button should occur on mouse up or mouse down.
494  *
495  * @param	{DwtButton.ACTION_MOUSEDOWN|DwtButton.ACTION_MOUSEUP}		actionTiming		the action timing
496  */
497 DwtButton.prototype.setActionTiming =
498 function(actionTiming) {
499       this._actionTiming = actionTiming;
500 };
501 
502 /**
503  * Activates/de-activates the button. A button is hovered when the mouse is over it.
504  *
505  * @param {boolean}	hovered		if <code>true</code>, the button is hovered
506  */
507 DwtButton.prototype.setHovered =
508 function(hovered) {
509     this.setDisplayState(hovered ? DwtControl.HOVER : DwtControl.NORMAL);
510 };
511 
512 /**
513  * Sets the enabled image
514  * 
515  * @param	{string}	imageInfo	the image
516  */
517 DwtButton.prototype.setEnabledImage =
518 function (imageInfo) {
519 	this._enabledImageInfo = imageInfo;
520 	this.setImage(imageInfo);
521 };
522 
523 /**
524  * Sets the depressed image
525  * 
526  * @param	{string}	imageInfo	the image
527  */
528 DwtButton.prototype.setDepressedImage =
529 function (imageInfo) {
530     this._depressedImageInfo = imageInfo;
531 };
532 
533 /**
534  * Sets the button as selected.
535  * 
536  * @param	{boolean}	selected		if <code>true</code>, the button is selected
537  */
538 DwtButton.prototype.setSelected =
539 function(selected) {
540 	if (this._selected != selected) {
541 		this._selected = selected;
542         this.setDisplayState(selected ? DwtControl.SELECTED : DwtControl.NORMAL);
543     }
544 };
545 
546 /**
547  * Checks if the button is toggled.
548  * 
549  * @return	{boolean}	<code>true</code> if toggled
550  */
551 DwtButton.prototype.isToggled =
552 function() {
553 	return this._selected;
554 };
555 
556 /**
557  * Pops-up the button menu (if present).
558  * 
559  * @param	{DwtMenu}	menu		the menu to use or <code>null</code> to use currently set menu
560  */
561 DwtButton.prototype.popup =
562 function(menu, event) {
563 	menu = menu || this.getMenu();
564 
565     if (!menu) { return; }
566 
567     var parent = menu.parent;
568 	var parentBounds = parent.getBounds();
569 	var windowSize = menu.shell.getSize();
570 	var menuSize = menu.getSize();
571 	var parentElement = parent.getHtmlElement();
572 	// since buttons are often absolutely positioned, and menus aren't, we need x,y relative to window
573 	var parentLocation = Dwt.toWindow(parentElement, 0, 0);
574 	var leftBorder = (parentElement.style.borderLeftWidth == "") ? 0 : parseInt(parentElement.style.borderLeftWidth);
575 	var kbGenerated = Boolean(event && DwtKeyEvent.isKeyEvent(event));
576 
577 	var x;
578 	if (this._menuPopupStyle == DwtButton.MENU_POPUP_STYLE_RIGHT) {
579 		x = parentLocation.x + parentBounds.width - menuSize.x;
580 	}
581 	else if (this._menuPopupStyle == DwtButton.MENU_POPUP_STYLE_CASCADE) {
582 		x = parentLocation.x + parentBounds.width;
583 	}
584 	else {
585 		x = parentLocation.x + leftBorder;
586 		x = ((x + menuSize.x) >= windowSize.x) ? windowSize.x - menuSize.x : x;
587 	}
588 
589 	var y;
590 	if (this._menuPopupStyle == DwtButton.MENU_POPUP_STYLE_ABOVE) {
591 		y = parentLocation.y - menuSize.y;
592 	}
593 	else if (this._menuPopupStyle == DwtButton.MENU_POPUP_STYLE_CASCADE) {
594 		y = parentLocation.y;
595 	}
596 	else {
597 		var horizontalBorder = (parentElement.style.borderTopWidth == "") ? 0 : parseInt(parentElement.style.borderTopWidth);
598 		horizontalBorder += (parentElement.style.borderBottomWidth == "") ? 0 : parseInt(parentElement.style.borderBottomWidth);
599 		y = parentLocation.y + parentBounds.height + horizontalBorder;
600 	}
601 	menu.popup(0, x, y, kbGenerated);
602 	menu.setSelectedItem(0);
603 };
604 
605 /**
606  * Gets the key map name.
607  * 
608  * @return	{string}	the key map name
609  */
610 DwtButton.prototype.getKeyMapName =
611 function() {
612 	return DwtKeyMap.MAP_BUTTON;
613 };
614 
615 /**
616  * Handles a key action event.
617  * 
618  * @param	{constant}		actionCode		the action code (see {@link DwtKeyMap})
619  * @param	{DwtEvent}		ev		the event
620  * @return	{boolean}		<code>true</code> if the event is handled; <code>false</code> otherwise
621  * @see		DwtKeyMap
622  */
623 DwtButton.prototype.handleKeyAction =
624 function(actionCode, ev) {
625 	switch (actionCode) {
626 		case DwtKeyMap.SELECT:
627 			this._emulateSingleClick();
628 			break;
629 
630 		case DwtKeyMap.SUBMENU:
631 			var menu = this.getMenu();
632 			if (!menu) return false;
633 			this._emulateDropDownClick();
634 			menu.setSelectedItem(0);
635 			break;
636 	}
637 
638 	return true;
639 };
640 
641 /**
642  * Removes options from drop down menu
643  */
644 DwtButton.prototype.removePullDownMenuOptions =
645 function() {
646     if (this._menu) {
647         this._setDropDownCellMouseHandlers(false);
648         if (this._dropDownEl && this._dropDownImg) {
649             // removes initial down arrow
650             AjxImg.setImage(this._dropDownEl, "");
651             // removes arrow image set by mouse hover, click, etc.
652             this.setDropDownImages("", "", "", "");
653         }
654     }
655 };
656 
657 // Private methods
658 
659 /**
660  * @private
661  */
662 DwtButton.prototype._emulateSingleClick =
663 function() {
664 	this.trigger();
665 	var htmlEl = this.getHtmlElement();
666 	var p = Dwt.toWindow(htmlEl);
667 	var mev = new DwtMouseEvent();
668 	this._setMouseEvent(mev, {
669 		type: this._actionTiming == DwtButton.ACTION_MOUSEDOWN ?
670 			DwtEvent.ONMOUSEDOWN : DwtEvent.ONMOUSEUP,
671 		dwtObj: this,
672 		target: htmlEl,
673 		button: DwtMouseEvent.LEFT,
674 		docX: p.x,
675 		docY: p.y,
676 		kbNavEvent: true
677 	});
678 	this.notifyListeners(mev.type, mev);
679 };
680 
681 /**
682  * @private
683  */
684 DwtButton.prototype._emulateDropDownClick =
685 function() {
686     var htmlEl = this._dropDownEl;
687     if (!htmlEl) { return; }
688 
689 	var p = Dwt.toWindow(htmlEl);
690 	var mev = new DwtMouseEvent();
691 	this._setMouseEvent(mev, {
692 		dwtObj: this,
693 		target: htmlEl,
694 		button: DwtMouseEvent.LEFT,
695 		docX: p.x,
696 		docY: p.y,
697 		kbNavEvent: true
698 	});
699 	DwtButton._dropDownCellMouseUpHdlr(mev);
700 };
701 
702 /**
703  * This method is called from mouseUpHdlr in {@see DwtControl}.
704  * 
705  * @private
706  */
707 DwtButton.prototype._focusByMouseUpEvent =
708 function()  {
709 	//do nothing, override parents so that we do not focus on button using mouseUp. Makes no sense to focus.
710 };
711 
712 /**
713  * NOTE: _focus and _blur will be reworked to reflect styles correctly
714  * 
715  * @private
716  */
717 DwtButton.prototype._focus =
718 function() {
719     this.setDisplayState(DwtControl.FOCUSED);
720 };
721 
722 /**
723  * @private
724  */
725 DwtButton.prototype._blur =
726 function() {
727     this.setDisplayState(DwtControl.NORMAL);
728 };
729 
730 /**
731  * @private
732  */
733 DwtButton.prototype._toggleMenu =
734 function (event) {
735 	if (this._shouldToggleMenu){
736         var menu = this.getMenu();
737         if (!menu.isPoppedUp()){
738 			this.popup(null, event);
739 			this._menuUp = true;
740 		} else {
741 			menu.popdown(0, event);
742 			this._menuUp = false;
743             this.deactivate();
744         }
745 	} else {
746 		this.popup(null, event);
747 	}
748 };
749 
750 /**
751  * @private
752  */
753 DwtButton.prototype._isDropDownEvent =
754 function(ev) {
755 	if (this._dropDownEventsEnabled && this._dropDownEl) {
756 		var mouseX = ev.docX;
757 		var dropDownX = Dwt.toWindow(this._dropDownEl, 0, 0, window).x;
758 		if (mouseX >= dropDownX) {
759 			return true;
760 		}
761 	}
762 	return false;
763 };
764 
765 /**
766  * @private
767  */
768 DwtButton.prototype.trigger =
769 function (){
770     if (this._depressedImageInfo) {
771         this.setImage(this._depressedImageInfo);
772     }
773     this.setDisplayState(DwtControl.ACTIVE, true);
774     this.isActive = true;
775 };
776 
777 /**
778  * @private
779  */
780 DwtButton.prototype.deactivate =
781 function() {
782 	this._showHoverImage(true);
783 
784 	if (this._style & DwtButton.TOGGLE_STYLE){
785 		this._selected = !this._selected;
786 	}
787     this.setDisplayState(DwtControl.HOVER);
788 };
789 
790 /**
791  * @private
792  */
793 DwtButton.prototype.dontStealFocus = function(val) {
794 	if (val == null) {
795 		val = true;
796 	}
797 	if (this._menu && this._menu.isDwtMenu) {
798 		this._menu.dontStealFocus(val);
799 	}
800 	this.__preventMenuFocus = val;
801 };
802 
803 /**
804  * @private
805  */
806 DwtButton.prototype._toggleHoverClass =
807 function(show, direction) {
808 	var iconEl = this._getIconEl(direction);
809 	if (iconEl) {  //add a null check so buttons with no icon elements don't break the app.
810 		var info = show ? this._hoverImageInfo[direction] : this.__imageInfo[direction];
811 		iconEl.firstChild.className = AjxImg.getClassForImage(info);
812 	}
813 };
814 
815 /**
816  * @private
817  */
818 DwtButton.prototype._showHoverImage =
819 function(show) {
820 	// if the button is image-only, DwtLabel#setImage is bad
821 	// because it clears the element first
822 	// (innerHTML = "") causing a mouseout event, then it
823 	// re-sets the image, which results in a new mouseover
824 	// event, thus looping forever eating your CPU and
825 	// blinking.
826 	if (!this._hoverImageInfo) {
827 		return;
828 	}
829 	if (this._hoverImageInfo.left) {
830 		this._toggleHoverClass(show, DwtLabel.LEFT);
831 	}
832 	if (this._hoverImageInfo.right) {
833 		this._toggleHoverClass(show, DwtLabel.RIGHT);
834 	}
835 };
836 
837 /**
838  * @private
839  */
840 DwtButton.prototype._handleClick =
841 function(ev) {
842 	if (this.isListenerRegistered(DwtEvent.SELECTION)) {
843 		var now = (new Date()).getTime();
844 		if (!this._lastNotify || (now - this._lastNotify > DwtButton.NOTIFY_WINDOW)) {
845 			var selEv = DwtShell.selectionEvent;
846 			DwtUiEvent.copy(selEv, ev);
847 			selEv.item = this;
848 			selEv.detail = (typeof this.__detail == "undefined") ? 0 : this.__detail;
849 			this.notifyListeners(DwtEvent.SELECTION, selEv);
850 			this._lastNotify = now;
851 			this.shell.notifyGlobalSelection(selEv);
852 		}
853 	} else if (this._menu) {
854 		if(this._menu.isDwtMenu && !this.isListenerRegistered(DwtEvent.SELECTION)) {
855 			this._menu.setAssociatedObj(this);	
856 		}		
857 		this._toggleMenu(ev);
858 	}
859 };
860 
861 /**
862  * @private
863  */
864 DwtButton.prototype._setMouseOutClassName =
865 function() {
866     this.setDisplayState(DwtControl.NORMAL);
867 };
868 
869 /**
870  * @private
871  */
872 DwtButton.prototype._createHtmlFromTemplate = function(templateId, data) {
873     DwtLabel.prototype._createHtmlFromTemplate.call(this, templateId, data);
874     this._dropDownEl = document.getElementById(data.id+"_dropdown");
875 };
876 
877 // Accessibility
878 DwtButton.prototype._menuAdded = function(menu) {
879 	this.setAttribute("aria-haspopup", true);
880 	this.setAttribute("aria-controls", menu._htmlElId);
881 };
882 
883 // Accessibility
884 DwtButton.prototype._menuItemSelected = function(menuItem) {};
885 
886 /**
887  * Pops up the dropdown menu.
888  * 
889  * @private
890  */
891 DwtButton._dropDownCellMouseDownHdlr =
892 function(ev) {
893 	var obj = DwtControl.getTargetControl(ev);
894 
895     var mouseEv = DwtShell.mouseEvent;
896 	mouseEv.setFromDhtmlEvent(ev, obj);
897 
898 	if (mouseEv.button == DwtMouseEvent.LEFT) {
899 	    if (this._depImg){
900 			AjxImg.setImage(this, this._depImg);
901 	    }
902 	}
903 
904 	mouseEv._stopPropagation = true;
905 	mouseEv._returnValue = false;
906 	mouseEv.setToDhtmlEvent(ev);
907 	return false;
908 };
909 
910 /**
911  * Updates the current mouse event (set from the previous mouse down).
912  * 
913  * @private
914  */
915 DwtButton._dropDownCellMouseUpHdlr =
916 function(ev) {
917 	var mouseEv = DwtShell.mouseEvent;
918 	mouseEv.setFromDhtmlEvent(ev);
919 
920 	if (mouseEv.button == DwtMouseEvent.LEFT) {
921 	    if (this._dropDownHovImg && !this.noMenuBar) {
922 			AjxImg.setImage(this, this._dropDownHovImg);
923 	    }
924 
925 		DwtEventManager.notifyListeners(DwtEvent.ONMOUSEDOWN, mouseEv);
926 
927 		var obj = DwtControl.getTargetControl(ev);
928 		if (obj) {
929 			if (obj.getMenu() && obj.getMenu().isPoppedUp()) {
930 				obj.getMenu().popdown();
931 			}
932 			else {
933 				if (obj._menu && obj._menu.isAjxCallback) {
934 					obj.popup();
935 				}
936 
937 				if (obj._dropDownEvtMgr.isListenerRegistered(DwtEvent.SELECTION)) {
938 					var selEv = DwtShell.selectionEvent;
939 					DwtUiEvent.copy(selEv, mouseEv);
940 					selEv.item = obj;
941 					obj._dropDownEvtMgr.notifyListeners(DwtEvent.SELECTION, selEv);
942 				} else {
943 					obj._toggleMenu(ev);
944 				}
945 			}
946 		}
947 	}
948 	
949 	mouseEv._stopPropagation = true;
950 	mouseEv._returnValue = false;
951 	mouseEv.setToDhtmlEvent(ev);
952 	return false;
953 };
954 
955 /**
956  * Activates the button.
957  * 
958  * @private
959  */
960 DwtButton._mouseOverListener =
961 function(ev) {
962 	var button = ev.dwtObj;
963 	if (!button) { return false; }
964 	button._showHoverImage(true);
965     button.setDisplayState(DwtControl.HOVER);
966 
967     var dropDown = button._dropDownEl;
968     if (button._menu && dropDown && button._dropDownHovImg && !button.noMenuBar &&
969         button.isListenerRegistered(DwtEvent.SELECTION)) {
970 		if (button._dropDownHovImg) {
971 			AjxImg.setImage(dropDown, button._dropDownHovImg);
972 		}
973     }
974 	// bug fix 48266 IE hack, solution is similar to bug 36253
975 	// Just rewrite the el's Child's className to trigger IE to render it
976 	// In mouserOut, it seems the IE can render it automatically. 	
977 	if(AjxEnv.isIE){
978 	   	if(ev && ev.target && ev.target.firstChild){
979 			var el = ev.target.firstChild;
980 			var cname = el.className;
981 			el.className = "";
982 			el.className = cname;
983 		} 
984 	}    	
985     ev._stopPropagation = true;
986 };
987 
988 /**
989  * @private
990  */
991 DwtButton._mouseOutListener =
992 function(ev) {
993 	var button = ev.dwtObj;
994 	if (!button) { return false; }
995 	button._showHoverImage(false);
996 	button._setMouseOutClassName();
997     button.isActive = false;
998 
999     var dropDown = button._dropDownEl;
1000     if (button._menu && dropDown && button._dropDownImg) {
1001 		AjxImg.setImage(dropDown, button._dropDownImg);
1002     }
1003 };
1004 
1005 /**
1006  * @private
1007  */
1008 DwtButton._mouseDownListener =
1009 function(ev) {
1010 	var button = ev.dwtObj;
1011 	if (!button) { return false; }
1012 	if (button._isDropDownEvent(ev)) {
1013 		return DwtButton._dropDownCellMouseDownHdlr(ev);
1014 	}
1015 
1016 	if (ev.button != DwtMouseEvent.LEFT) { return; }
1017 
1018     var dropDown = button._dropDownEl;
1019     if (button._menu && dropDown && button._dropDownDepImg) {
1020 		AjxImg.setImage(dropDown, button._dropDownDepImg);
1021     }
1022 	switch (button._actionTiming) {
1023 	  case DwtButton.ACTION_MOUSEDOWN:
1024 		button.trigger();
1025 		button._handleClick(ev);
1026 		break;
1027 	  case DwtButton.ACTION_MOUSEUP:
1028 		button.trigger();
1029 		break;
1030 	}
1031 };
1032 
1033 /**
1034  * Button has been pressed, notify selection listeners.
1035  * 
1036  * @private
1037  */
1038 DwtButton._mouseUpListener =
1039 function(ev) {
1040 	var button = ev.dwtObj;
1041 	if (!button) { return false; }
1042 	if (button._isDropDownEvent(ev)) {
1043 		return DwtButton._dropDownCellMouseUpHdlr(ev);
1044 	}
1045 	if (ev.button != DwtMouseEvent.LEFT) { return; }
1046 
1047     var dropDown = button._dropDownEl;
1048     if (button._menu && dropDown && button._dropDownHovImg && !button.noMenuBar){
1049 		AjxImg.setImage(dropDown, button._dropDownHovImg);
1050     }
1051 	switch (button._actionTiming) {
1052 	  case DwtButton.ACTION_MOUSEDOWN:
1053  	    button.deactivate();
1054 		break;
1055 
1056 	  case DwtButton.ACTION_MOUSEUP:
1057 	    var el = button.getHtmlElement();
1058 		if (button.isActive) {
1059 			button.deactivate();
1060 			button._handleClick(ev);
1061 		}
1062 		break;
1063 	}
1064 };
1065 
1066 DwtButton._listeners = {};
1067 DwtButton._listeners[DwtEvent.ONMOUSEOVER] = new AjxListener(null, DwtButton._mouseOverListener);
1068 DwtButton._listeners[DwtEvent.ONMOUSEOUT] = new AjxListener(null, DwtButton._mouseOutListener);
1069 DwtButton._listeners[DwtEvent.ONMOUSEDOWN] = new AjxListener(null, DwtButton._mouseDownListener);
1070 DwtButton._listeners[DwtEvent.ONMOUSEUP] = new AjxListener(null, DwtButton._mouseUpListener);
1071 DwtButton._listeners[DwtEvent.ONMOUSEENTER] = new AjxListener(null, DwtButton._mouseOverListener);
1072 DwtButton._listeners[DwtEvent.ONMOUSELEAVE] = new AjxListener(null, DwtButton._mouseOutListener);
1073