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