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 * Creates a menu. 26 * @constructor 27 * @class 28 * Creates a menu object to menu items can be added. Menus can be created in various styles as 29 * follows: 30 * <ul> 31 * <li>DwtMenu.BAR_STYLE - Traditional menu bar</li> 32 * <li>DwtMenu.POPUP_STYLE - Popup menu</li> 33 * <li>DwtMenu.DROPDOWN_STYLE - Used when a menu is a drop down (e.g. parent is a button or another menu item)</li> 34 * <li>DwtMenu.DROPDOWN_CENTERV_STYLE - like a dropdown, but position to the right, centered vertically on the parent</li> 35 * <li>DwtMenu.COLOR_PICKER_STYLE - Menu is hosting a single color picker</li> 36 * <li>DwtMenu.CALENDAR_PICKER_STYLE - Menu is hostng a single calendar</li> 37 * <li>DwtMenu.GENERIC_WIDGET_STYLE - Menu is hosting a single "DwtInsertTableGrid"</li> 38 * </ul> 39 * 40 * @author Ross Dargahi 41 * 42 * @param {hash} params a hash of parameters 43 * @param {DwtComposite} params.parent the parent widget 44 * @param {constant} params.style the menu style 45 * @param {string} params.className the CSS class 46 * @param {constant} params.posStyle the positioning style (see {@link DwtControl}) 47 * @param {constant} params.layout layout to use: DwtMenu.LAYOUT_STACK, DwtMenu.LAYOUT_CASCADE or DwtMenu.LAYOUT_SCROLL. A value of [true] defaults to DwtMenu.LAYOUT_CASCADE and a value of [false] defaults to DwtMenu.LAYOUT_STACK. 48 * @param {int} params.maxRows=0 if >0 and layout = LAYOUT_CASCADE or DwtMenu.LAYOUT_SCROLL, define how many rows are allowed before cascading/scrolling 49 * @param {boolean} params.congruent if the parent is a DwtMenuItem, align so that the submenu "merges" with the parent menu 50 * 51 * @extends DwtComposite 52 */ 53 54 DwtMenu = function(params) { 55 this._created = false; 56 if (arguments.length == 0) { return; } 57 params = Dwt.getParams(arguments, DwtMenu.PARAMS); 58 59 this._origStyle = params.style; 60 var parent = params.parent; 61 if (parent) { 62 if (parent instanceof DwtMenuItem || parent instanceof DwtButton) { 63 if ((params.style == DwtMenu.GENERIC_WIDGET_STYLE) || 64 (params.style == DwtMenu.DROPDOWN_CENTERV_STYLE)) { 65 this._style = params.style; 66 } else { 67 this._style = DwtMenu.DROPDOWN_STYLE; 68 } 69 } else { 70 this._style = params.style || DwtMenu.POPUP_STYLE; 71 } 72 if (!params.posStyle) { 73 params.posStyle = (this._style == DwtMenu.BAR_STYLE) ? DwtControl.STATIC_STYLE : DwtControl.ABSOLUTE_STYLE; 74 } 75 } 76 params.className = params.className || "DwtMenu"; 77 78 this._layoutStyle = params.layout == null || params.layout; 79 if (this._layoutStyle === true) { 80 this._layoutStyle = DwtMenu.LAYOUT_CASCADE; 81 } else if (this._layoutStyle === false) { 82 this._layoutStyle = DwtMenu.LAYOUT_STACK; 83 } 84 this._maxRows = this._layoutStyle && params.maxRows || 0; 85 this._congruent = params.congruent; 86 87 // Hack to force us to hang off of the shell for positioning. 88 params.parent = (parent instanceof DwtShell) ? parent : parent.shell; 89 DwtComposite.call(this, params); 90 this.parent = parent; 91 92 if (this._isPopupStyle() && (this._layoutStyle == DwtMenu.LAYOUT_STACK)) { 93 this.setScrollStyle(DwtControl.SCROLL); 94 } 95 96 if (!parent) { return; } 97 98 var events = AjxEnv.isIE ? [DwtEvent.ONMOUSEDOWN, DwtEvent.ONMOUSEUP] : 99 [DwtEvent.ONMOUSEDOWN, DwtEvent.ONMOUSEUP, DwtEvent.ONMOUSEOVER, DwtEvent.ONMOUSEOUT]; 100 this._setEventHdlrs(events); 101 this._hasSetMouseEvents = true; 102 103 var htmlElement = this.getHtmlElement(); 104 105 if (params.posStyle != DwtControl.STATIC_STYLE) { 106 Dwt.setLocation(htmlElement, Dwt.LOC_NOWHERE, Dwt.LOC_NOWHERE); 107 } 108 109 // Don't need to create table for color picker and calendar picker styles 110 if (this._style != DwtMenu.COLOR_PICKER_STYLE && 111 this._style != DwtMenu.CALENDAR_PICKER_STYLE && 112 this._style != DwtMenu.GENERIC_WIDGET_STYLE) 113 { 114 this._table = document.createElement("table"); 115 this._table.border = this._table.cellPadding = this._table.cellSpacing = 0; 116 this._table.className = "DwtMenuTable"; 117 this._table.id = Dwt.getNextId(); 118 119 120 if (this._layoutStyle == DwtMenu.LAYOUT_SCROLL) { 121 this._setupScroll(); 122 } else { 123 htmlElement.appendChild(this._table); 124 } 125 this._table.backgroundColor = DwtCssStyle.getProperty(htmlElement, "background-color"); 126 } 127 128 if (params.style != DwtMenu.BAR_STYLE) { 129 this.setVisible(false); 130 this._isPoppedUp = false; 131 } else { 132 DwtMenu._activeMenuIds.add(htmlElement.id, null, true); 133 this._isPoppedUp = true; 134 } 135 this._popdownAction = new AjxTimedAction(this, this._doPopdown); 136 this._popdownActionId = -1; 137 this._popupAction = new AjxTimedAction(this, this._doPopup); 138 this._popupActionId = -1; 139 140 this._outsideListener = new AjxListener(null, DwtMenu._outsideMouseDownListener); 141 142 this._menuItemsHaveChecks = false; 143 this._menuItemsHaveIcons = false; 144 this._menuItemsWithSubmenus = 0; 145 this.__currentItem = null; 146 this.__preventMenuFocus = false; 147 148 this._created = true; 149 150 // When items are added, the menu listens to selection events 151 // and will propagate the event to listeners that are registered 152 // on the menu itself. 153 this._itemSelectionListener = new AjxListener(this, this._propagateItemSelection); 154 155 // Accessibility 156 if (parent._menuAdded) { 157 parent._menuAdded(this); 158 } 159 }; 160 161 DwtMenu.PARAMS = ["parent", "style", "className", "posStyle", "cascade", "id"]; 162 163 DwtMenu.prototype = new DwtComposite; 164 DwtMenu.prototype.constructor = DwtMenu; 165 166 DwtMenu.prototype.isDwtMenu = true; 167 DwtMenu.prototype.toString = function() { return "DwtMenu"; }; 168 DwtMenu.prototype.role = "menu"; 169 170 DwtMenu.BAR_STYLE = "BAR"; 171 DwtMenu.POPUP_STYLE = "POPUP"; 172 DwtMenu.DROPDOWN_STYLE = "DROPDOWN"; 173 DwtMenu.DROPDOWN_CENTERV_STYLE = "DROPDOWN_CENTERV"; 174 DwtMenu.COLOR_PICKER_STYLE = "COLOR"; 175 DwtMenu.CALENDAR_PICKER_STYLE = "CALENDAR"; 176 DwtMenu.GENERIC_WIDGET_STYLE = "GENERIC"; 177 178 DwtMenu.HAS_ICON = "ZHasIcon"; 179 DwtMenu.HAS_CHECK = "ZHasCheck"; 180 DwtMenu.HAS_SUBMENU = "ZHasSubMenu"; 181 182 DwtMenu.LAYOUT_STACK = 0; 183 DwtMenu.LAYOUT_CASCADE = 1; 184 DwtMenu.LAYOUT_SCROLL = 2; 185 186 DwtMenu._activeMenuUp = false; 187 DwtMenu._activeMenuIds = new AjxVector(); 188 DwtMenu._activeMenus = new AjxVector() ; 189 190 DwtMenu.prototype.dispose = 191 function() { 192 this._table = null; 193 DwtComposite.prototype.dispose.call(this); 194 195 // Remove this from the shell. (Required because of hack in constructor.) 196 if (!(this.parent instanceof DwtShell)) { 197 this.shell.removeChild(this); 198 } 199 }; 200 201 /** 202 * Adds a selection listener. 203 * @param {AjxListener} listener The listener. 204 */ 205 DwtMenu.prototype.addSelectionListener = function(listener) { 206 this.addListener(DwtEvent.SELECTION, listener); 207 }; 208 209 /** 210 * Removes a selection listener. 211 * @param {AjxListener} listener The listener. 212 */ 213 DwtMenu.prototype.removeSelectionListener = function(listener) { 214 this.removeListener(DwtEvent.SELECTION, listener); 215 }; 216 217 /** 218 * Adds a popup listener. 219 * 220 * @param {AjxListener} listener the listener 221 */ 222 DwtMenu.prototype.addPopupListener = 223 function(listener) { 224 this.addListener(DwtEvent.POPUP, listener); 225 }; 226 227 /** 228 * Removes a popup listener. 229 * 230 * @param {AjxListener} listener the listener 231 */ 232 DwtMenu.prototype.removePopupListener = 233 function(listener) { 234 this.removeListener(DwtEvent.POPUP, listener); 235 }; 236 237 /** 238 * Adds a popdown listener. 239 * 240 * @param {AjxListener} listener the listener 241 */ 242 DwtMenu.prototype.addPopdownListener = 243 function(listener) { 244 this.addListener(DwtEvent.POPDOWN, listener); 245 }; 246 247 /** 248 * Removes a popdown listener. 249 * 250 * @param {AjxListener} listener the listener 251 */ 252 DwtMenu.prototype.removePopdownListener = 253 function(listener) { 254 this.removeListener(DwtEvent.POPDOWN, listener); 255 }; 256 257 DwtMenu.prototype.setWidth = 258 function(width) { 259 this._width = width; 260 261 if (this._table) { 262 Dwt.setSize(this._table, width, Dwt.CLEAR); 263 } 264 }; 265 266 DwtMenu.prototype.centerOnParentVertically = 267 function() { 268 return (this._style === DwtMenu.DROPDOWN_CENTERV_STYLE); 269 }; 270 271 DwtMenu.prototype._isPopupStyle = 272 function() { 273 return (this._style === DwtMenu.POPUP_STYLE || this._style === DwtMenu.DROPDOWN_STYLE || this._style === DwtMenu.DROPDOWN_CENTERV_STYLE); 274 }; 275 276 /** 277 * Gets a menu item. 278 * 279 * @param {string} index the index 280 * @return {DwtMenuItem} the menu item 281 */ 282 DwtMenu.prototype.getItem = 283 function(index) { 284 return this._children.get(index); 285 }; 286 287 DwtMenu.prototype.getItemIndex = 288 function(item) { 289 return this._children.indexOf(item); 290 }; 291 292 /** 293 * Gets the item by id. 294 * 295 * @param {string} key the id key 296 * @param {Object} id the id value 297 * @return {DwtMenuItem} the menu item 298 */ 299 DwtMenu.prototype.getItemById = 300 function(key, id) { 301 var items = this.getItems(); 302 for (var i = 0; i < items.length; i++) { 303 var itemId = items[i].getData(key); 304 if (itemId == id) { 305 items[i].index = i; //needed in some caller 306 return items[i]; 307 } 308 } 309 return null; 310 }; 311 312 /** 313 * Gets a count of the items. 314 * 315 * @return {number} the count 316 */ 317 DwtMenu.prototype.getItemCount = 318 function() { 319 return this._children.size(); 320 }; 321 322 /** 323 * Gets an array of items. 324 * 325 * @return {array} an array of {@link DwtMenuItem} objects 326 */ 327 DwtMenu.prototype.getItems = 328 function() { 329 return this._children.getArray(); 330 }; 331 332 DwtMenu.prototype.getSelectedItem = 333 function(style) { 334 var a = this._children.getArray(); 335 for (var i = 0; i < a.length; i++) { 336 var mi = a[i]; 337 if ((style == null || (mi._style && style != 0)) && mi.getChecked()) 338 return mi; 339 } 340 return null; 341 }; 342 343 /** 344 * Checks if the menu is popped-up. 345 * 346 * @return {boolean} <code>true</code> if popped-up 347 */ 348 DwtMenu.prototype.isPoppedUp = 349 function() { 350 return this._isPoppedUp; 351 }; 352 353 DwtMenu.prototype.popup = function(msec, x, y, kbGenerated) { 354 355 if (this._style == DwtMenu.BAR_STYLE) { 356 return; 357 } 358 359 if (this._popdownActionId != -1) { 360 AjxTimedAction.cancelAction(this._popdownActionId); 361 this._popdownActionId = -1; 362 } 363 else { 364 if (this._isPoppedUp || (this._popupActionId != -1 && msec && msec > 0)) { 365 return; 366 } 367 else if (this._popupActionId != -1) { 368 AjxTimedAction.cancelAction(this._popupActionId); 369 this._popupActionId = -1; 370 } 371 372 if (!msec) { 373 this._doPopup(x, y, kbGenerated); 374 } 375 else { 376 this._popupAction.args = [x, y, kbGenerated]; 377 this._popupActionId = AjxTimedAction.scheduleAction(this._popupAction, msec); 378 } 379 } 380 }; 381 382 DwtMenu.prototype.popdown = 383 function(msec, ev) { 384 if (this._style == DwtMenu.BAR_STYLE) return; 385 386 if (this._popupActionId != -1) { 387 AjxTimedAction.cancelAction(this._popupActionId); 388 this._popupActionId = -1; 389 } else { 390 if (!this._isPoppedUp || this._popdownActionId != -1) 391 return; 392 if (msec == null || msec == 0) 393 this._doPopdown(ev); 394 else 395 this._popdownActionId = AjxTimedAction.scheduleAction(this._popdownAction, msec); 396 } 397 }; 398 399 DwtMenu.prototype._setupScroll = function() { 400 var htmlElement = this.getHtmlElement(); 401 this._table.style.position = "relative"; 402 403 this._topScroller = document.createElement("div"); 404 this._topScroller.className = "DwtMenuScrollTop"; 405 this._topScroller.id = Dwt.getNextId(); 406 407 this._imgDivTop = document.createElement("div"); 408 this._imgDivTop.className = "ImgUpArrowSmall"; 409 this._topScroller.appendChild(this._imgDivTop); 410 Dwt.setHandler(this._imgDivTop, DwtEvent.ONMOUSEOUT, DwtMenu._stopEvent); 411 Dwt.setHandler(this._imgDivTop, DwtEvent.ONMOUSEOVER, DwtMenu._stopEvent); 412 htmlElement.appendChild(this._topScroller); 413 414 this._tableContainer = document.createElement("div"); 415 this._tableContainer.appendChild(this._table); 416 htmlElement.appendChild(this._tableContainer); 417 418 this._bottomScroller = document.createElement("div"); 419 this._bottomScroller.className = "DwtMenuScrollBottom"; 420 this._bottomScroller.id = Dwt.getNextId(); 421 422 this._imgDivBottom = document.createElement("div"); 423 this._imgDivBottom.className = "ImgDownArrowSmall"; 424 Dwt.setHandler(this._imgDivBottom, DwtEvent.ONMOUSEOUT, DwtMenu._stopEvent); 425 Dwt.setHandler(this._imgDivBottom, DwtEvent.ONMOUSEOVER, DwtMenu._stopEvent); 426 this._bottomScroller.appendChild(this._imgDivBottom); 427 htmlElement.appendChild(this._bottomScroller); 428 429 //scroll up 430 var scrollUpStartListener = AjxCallback.simpleClosure(this._scroll, this, this._table.id, true, false); 431 var scrollUpStopListener = AjxCallback.simpleClosure(this._scroll, this, this._table.id, false, false); 432 var mouseOutTopListener = AjxCallback.simpleClosure(this._handleMouseOut, this, this._topScroller.id, this._table.id); 433 var mouseOutBottomListener = AjxCallback.simpleClosure(this._handleMouseOut, this, this._bottomScroller.id, this._table.id); 434 435 Dwt.setHandler(this._topScroller, DwtEvent.ONMOUSEDOWN, scrollUpStartListener); 436 Dwt.setHandler(this._topScroller, DwtEvent.ONMOUSEUP, scrollUpStopListener); 437 if (!AjxEnv.isIE) { 438 Dwt.setHandler(this._topScroller, DwtEvent.ONMOUSEOUT, mouseOutTopListener); 439 } else { 440 Dwt.setHandler(this._topScroller, DwtEvent.ONMOUSELEAVE, scrollUpStopListener); 441 } 442 443 //scroll down 444 var scrollDownStartListener = AjxCallback.simpleClosure(this._scroll, this, this._table.id, true, true); 445 var scrollDownStopListener = AjxCallback.simpleClosure(this._scroll, this, this._table.id, false, true); 446 447 Dwt.setHandler(this._bottomScroller, DwtEvent.ONMOUSEDOWN, scrollDownStartListener); 448 Dwt.setHandler(this._bottomScroller, DwtEvent.ONMOUSEUP, scrollDownStopListener); 449 Dwt.setHandler(this._bottomScroller, DwtEvent.ONMOUSEUP, scrollDownStopListener); 450 if (!AjxEnv.isIE) { 451 Dwt.setHandler(this._bottomScroller, DwtEvent.ONMOUSEOUT, mouseOutBottomListener); 452 } else { 453 Dwt.setHandler(this._bottomScroller, DwtEvent.ONMOUSELEAVE, scrollDownStopListener); 454 } 455 456 var wheelListener = AjxCallback.simpleClosure(this._handleScroll, this, this._table.id); 457 Dwt.setHandler(htmlElement, DwtEvent.ONMOUSEWHEEL, wheelListener); 458 }; 459 460 DwtMenu.prototype.render = function(x, y) { 461 462 var windowSize = this.shell.getSize(); 463 var mySize = this.getSize(); 464 var htmlEl = this.getHtmlElement(); 465 466 // bug 9583 - can't query border size so just subtract generic padding 467 windowSize.y -= 10 + (AjxEnv.isIE ? 20 : 0); 468 windowSize.x -= 28; 469 470 var isScroll = this._layoutStyle == DwtMenu.LAYOUT_SCROLL; 471 var isPopup = this._isPopupStyle(); 472 var isCascade = this._layoutStyle == DwtMenu.LAYOUT_CASCADE; 473 if (this._table) { 474 if (isPopup && isCascade) { 475 var space = windowSize.y; 476 var newY = null; 477 var rows = this._table.rows; 478 var numRows = rows.length; 479 var maxRows = this._maxRows; 480 var height = mySize.y; 481 var requiredSpace = space - 25; // Account for space on top & bottom of menu. 482 for (var i = numRows - 1; i >= 0; i--) { 483 height -= Dwt.getSize(rows[i]).y; 484 if (height < requiredSpace) { 485 break; 486 } 487 } 488 var count = maxRows ? Math.min(i + 1, maxRows) : (i + 1); 489 for (var j = count; j < numRows; j++) { 490 var row = rows[(j - count) % count]; 491 var cell = row.insertCell(-1); 492 cell.className = "DwtMenuCascadeCell"; 493 var child = rows[j].cells[0].firstChild; 494 while (child != null) { 495 cell.appendChild(child); 496 child = child.nextSibling; 497 } 498 } 499 for (j = rows.length - 1; j >= count; j--) { 500 this._table.deleteRow(count); 501 } 502 var offset = numRows % count; 503 if (offset > 0) { 504 for (var j = offset; j < count; j++) { 505 var row = rows[j]; 506 var cell = row.insertCell(-1); 507 cell.className = "DwtMenuCascadeCell"; 508 cell.empty = true; 509 cell.innerHTML = " "; 510 } 511 } 512 513 mySize = this.getSize(); 514 if (newY) { 515 y = newY - mySize.y; 516 } 517 } 518 else if (isPopup && isScroll) { 519 var rows = this._table.rows; 520 var numRows = rows.length; 521 var maxRows = this._maxRows; 522 var limRows = maxRows ? Math.min(maxRows, numRows) : numRows; 523 var availableSpace = windowSize.y - 25; // Account for space on top & bottom of menu. 524 525 var height = 20; //for scroll buttons 526 for (var i = 0; i < limRows; i++) { 527 var rowSize = Dwt.getSize(rows[i]).y; 528 if (height + rowSize <= availableSpace) { 529 height += rowSize; 530 } 531 else { 532 break; 533 } 534 } 535 mySize.y = height; 536 } 537 } 538 539 var newW = "auto"; 540 var newH = "auto"; 541 if (isPopup && isScroll) { 542 newH = mySize.y; 543 if (this._tableContainer) { 544 this._tableContainer.style.height = (newH - 20) +"px"; 545 } 546 } 547 else if ((isPopup && isCascade) || y + mySize.y < windowSize.y - 5 ) { 548 newH = "auto"; 549 } 550 else { 551 newH = windowSize.y - y - 5; 552 } 553 if (isScroll) { 554 if (this._table) { 555 this._table.style.width = mySize.x; 556 } 557 newW = mySize.x; 558 } 559 this.setSize(newW, newH); 560 // NOTE: This hack is needed for FF/Moz because the containing div 561 // allows the inner table to overflow. When the menu cascades 562 // and the menu items get pushed off of the visible area, the 563 // div's border doesn't surround the menu items. This hack 564 // forces the outer div's width to surround the table. 565 566 if ((AjxEnv.isGeckoBased || AjxEnv.isSafari || (this._origStyle == DwtMenu.CALENDAR_PICKER_STYLE)) && this._table && !isScroll) { 567 htmlEl.style.width = (mySize.x + (isPopup && !isCascade ? 10 : 0)) + "px"; 568 } 569 570 // Popup menu type 571 var newX = x + mySize.x >= windowSize.x ? windowSize.x - mySize.x : x; 572 if (this.parent instanceof DwtMenuItem) { 573 Dwt.delClass(htmlEl, "DwtMenu-congruentLeft"); 574 Dwt.delClass(htmlEl, "DwtMenu-congruentRight"); 575 576 var pbound = this.parent.getBounds(); 577 var pmstyle = DwtCssStyle.getComputedStyleObject(this.parent.parent.getHtmlElement()); // Get the style for the DwtMenu holding the parent DwtMenuItem 578 var tstyle = DwtCssStyle.getComputedStyleObject(htmlEl); // Get the style for this menu (includes skinning) 579 580 //if the cascading extends over the edge of the screen, cascade to the left 581 if (((newX > pbound.x && newX < pbound.x + pbound.width) || (pbound.x >= newX && pbound.x < newX + mySize.x)) && pbound.x >= mySize.x) { 582 var totalWidth = parseInt(tstyle.width); 583 if (!AjxEnv.isIE) { 584 totalWidth += parseInt(tstyle.paddingLeft) + parseInt(tstyle.paddingRight) + parseInt(tstyle.borderLeftWidth) + parseInt(tstyle.borderRightWidth); 585 } 586 newX = (parseInt(pmstyle.left) || pbound.x) - (totalWidth || mySize.x); 587 if (this._congruent) { 588 var offset; 589 if (AjxEnv.isIE) { 590 offset = parseInt(tstyle.borderLeftWidth); 591 } 592 else { 593 offset = parseInt(tstyle.borderLeftWidth) + parseInt(tstyle.borderRightWidth); 594 } 595 if (!isNaN(offset)) { 596 newX += offset; 597 Dwt.addClass(htmlEl, "DwtMenu-congruentLeft"); 598 } 599 } 600 } 601 else { // Cascade to the right 602 var left = parseInt(pmstyle.left) || (pbound.x - (parseInt(pmstyle.paddingLeft) || 0)); 603 var width = parseInt(pmstyle.width) || pbound.width; 604 newX = left + width; 605 if (this._congruent) { 606 var offset = parseInt(pmstyle.paddingRight) + parseInt(tstyle.paddingLeft) + parseInt(tstyle.borderLeftWidth); 607 if (!isNaN(offset)) { 608 newX += offset; 609 Dwt.addClass(htmlEl, "DwtMenu-congruentRight"); 610 } 611 } 612 } 613 } 614 615 if (this._style === DwtMenu.DROPDOWN_CENTERV_STYLE) { 616 y -= mySize.y/2; 617 if (y < 0) { 618 y = 0; 619 } 620 } 621 var newY = isPopup && y + mySize.y >= windowSize.y ? windowSize.y - mySize.y : y; 622 623 if (this.parent instanceof DwtMenuItem && this._congruent) { 624 var offset = (parseInt(tstyle.paddingTop) || 0) - (parseInt(tstyle.borderTopWidth) || 0); 625 if (offset > 0) { 626 newY -= offset; 627 } 628 } 629 630 // make sure we aren't locating the menu offscreen 631 newX = newX < 0 && newX !== Dwt.DEFAULT ? 0 : newX; 632 newY = newY < 0 && newY !== Dwt.DEFAULT ? 0 : newY; 633 this.setLocation(newX, newY); 634 }; 635 636 DwtMenu.prototype.getKeyMapName = 637 function() { 638 return DwtKeyMap.MAP_MENU; 639 }; 640 641 DwtMenu.prototype._handleScroll = 642 function(divID, ev) { 643 if (!ev) ev = window.event; 644 var div = Dwt.byId(divID); 645 if (div && ev) { 646 ev = ev ? ev : window.event; 647 var wheelData = ev.detail ? ev.detail * -1 : ev.wheelDelta / 40; 648 var rows = div.rows; 649 var step = Dwt.getSize(rows[0]).y || 10; 650 this._popdownSubmenus(); 651 if (wheelData > 0) { //scroll up 652 this._doScroll(div, +step) 653 } else if (wheelData < 0) { //scroll down 654 this._doScroll(div, -step) 655 } 656 } 657 }; 658 659 DwtMenu.prototype._handleMouseOut = 660 function(divID, tableID, ev) { 661 if (divID && ev.type && ev.type == "mouseout" && !AjxEnv.isIE) { 662 var div = divID ? Dwt.byId(divID) : null; 663 fromEl = ev.target; 664 if (fromEl != div) { 665 return; 666 } 667 toEl = ev.relatedTarget; 668 while (toEl) { 669 toEl = toEl.parentNode; 670 if (toEl == div) { 671 return; 672 } 673 } 674 this._scroll(tableID, false, false, null); 675 } 676 }; 677 678 DwtMenu.prototype._scroll = 679 function(divID, scrolling, direction, ev) { 680 var div = divID ? document.getElementById(divID) : null; 681 if (div && scrolling) { 682 var rows = div.rows; 683 var step = Dwt.getSize(rows[0]).y || 10; 684 if (this._direction != direction || !this._scrollTimer) { 685 this._popdownSubmenus(); 686 this._direction = direction; 687 if (this._scrollTimer) { 688 clearInterval(this._scrollTimer); 689 this._scrollTimer = null; 690 } 691 692 if (direction) { //scroll down 693 this._scrollTimer = setInterval(AjxCallback.simpleClosure(this._doScroll, this, div, -step), 100); 694 this._doScroll(div, -step); 695 } else { //scroll up 696 this._scrollTimer = setInterval(AjxCallback.simpleClosure(this._doScroll, this, div, step), 100); 697 this._doScroll(div, step); 698 } 699 } 700 } else { 701 if (this._scrollTimer) { 702 clearInterval(this._scrollTimer); 703 this._scrollTimer = null; 704 } 705 } 706 }; 707 708 DwtMenu.prototype._doScroll = 709 function(div, step) { 710 if (div && step) { 711 var old = parseInt(div.style.top) || 0; 712 var top; 713 if (step < 0) { // scroll down 714 var rows = this._table.rows || null; 715 var height = rows && rows.length && Dwt.getSize(rows[0]).y; 716 var max = div.scrollHeight - (parseInt(div.parentNode.style.height) || ((this._maxRows || (rows && rows.length)) * height) || 0); 717 if (Math.abs(old + step) <= max) { 718 top = old + step; 719 } else { 720 top = -max; 721 } 722 } else { // scroll up 723 if ((old + step) < 0) { 724 top = old + step; 725 } else { 726 top = 0; 727 } 728 } 729 Dwt.setLocation(div, Dwt.DEFAULT, top); 730 } 731 }; 732 733 /** 734 * Checks a menu item (the menu must be radio or checkbox style). The menu item 735 * is identified through the given field/value pair. 736 * 737 * @param {DwtMenuItem} item the menu item to scroll to 738 * @param {boolean} justMakeVisible false: scroll so the item is in the topmost row; true: scroll so the item is visible (scrolling down to an item puts it in the bottom row, doesn't scroll if the item is already visible) 739 * 740 */ 741 DwtMenu.prototype.scrollToItem = 742 function(item, justMakeVisible) { 743 var index = this.getItemIndex(item); 744 if (index != -1) 745 this.scrollToIndex(index, justMakeVisible); 746 }; 747 748 DwtMenu.prototype.scrollToIndex = 749 function(index, justMakeVisible) { 750 if (this._created && this._layoutStyle == DwtMenu.LAYOUT_SCROLL && index !== null && index >= 0 && this._table) { 751 var rows = this._table.rows; 752 if (rows) { 753 var maxRows = this._maxRows; 754 var visibleHeight = 0; 755 var rowHeights = []; 756 for (var i = 0, numRows = rows.length; i < numRows; i++) { 757 var h = Dwt.getSize(rows[i]).y; 758 if (i < maxRows) 759 visibleHeight += h; 760 rowHeights.push(h); 761 } 762 763 var itemHeight = rowHeights[index]; 764 var currentOffset = parseInt(this._table.style.top) || 0; 765 if (index >= rows.length) 766 index = rows.length-1; 767 768 var itemOffset = 0; 769 for (var i=0; i<index && i<rowHeights.length; i++) { 770 itemOffset += rowHeights[i]; 771 } 772 var delta = 0; 773 if (justMakeVisible) { 774 if (itemOffset < -currentOffset) { 775 delta = -(itemOffset + currentOffset); // Scroll up, making the item the topmost visible row 776 } else if (itemOffset + itemHeight > visibleHeight - currentOffset) { 777 delta = -(itemOffset + currentOffset - visibleHeight + itemHeight); // Scroll down, making the item the lowermost visible row 778 } // else do not scroll; item is already visible 779 } else { 780 delta = -(itemOffset + currentOffset); // Scroll so that the item is the topmost visible row 781 } 782 if (delta) { 783 this._popdownSubmenus(); 784 this._doScroll(this._table, delta); 785 } 786 } 787 } 788 }; 789 790 DwtMenu.prototype.handleKeyAction = function(actionCode, ev) { 791 792 // For now don't deal with anything but BAR, POPUP, and DROPDOWN style menus 793 switch (this._style) { 794 case DwtMenu.BAR_STYLE: 795 case DwtMenu.POPUP_STYLE: 796 case DwtMenu.DROPDOWN_STYLE: 797 case DwtMenu.DROPDOWN_CENTERV_STYLE: 798 break; 799 800 default: 801 return false; 802 } 803 804 switch (actionCode) { 805 806 case DwtKeyMap.PAGE_UP: 807 case DwtKeyMap.PAGE_DOWN: 808 var item = this.__currentItem || this._children.get(0); 809 var index = this.getItemIndex(item); 810 if (this._maxRows && index !== -1) { 811 this.setSelectedItem(index + ((actionCode === DwtKeyMap.PAGE_UP) ? -this._maxRows : this._maxRows)); 812 } 813 else { 814 this.setSelectedItem(actionCode === DwtKeyMap.PAGE_DOWN); 815 } 816 break; 817 818 case DwtKeyMap.SELECT_PREV: 819 case DwtKeyMap.SELECT_NEXT: 820 this.setSelectedItem(actionCode === DwtKeyMap.SELECT_NEXT); 821 break; 822 823 case DwtKeyMap.SELECT: 824 if (this.__currentItem) { 825 this.__currentItem._emulateSingleClick(); 826 } 827 break; 828 829 case DwtKeyMap.SUBMENU: 830 if (this.__currentItem && this.__currentItem._menu) { 831 this.__currentItem._popupMenu(0, true); 832 } 833 break; 834 835 case DwtKeyMap.PARENTMENU: 836 if (this.parent.isDwtMenuItem) { 837 this.popdown(); 838 this.parent.focus(); 839 } 840 841 break; 842 843 case DwtKeyMap.CANCEL: 844 this.popdown(); 845 break; 846 847 default: 848 return false; 849 } 850 851 return true; 852 }; 853 854 /** 855 * This allows the caller to associate one object with the menu. Association 856 * means, for events, treat the menu, and this object as one. If I click on 857 * elements pertaining to this object, we will think of them as part of the 858 * menu. 859 * @see _outsideMouseListener. 860 * 861 * @private 862 */ 863 DwtMenu.prototype.setAssociatedObj = 864 function(dwtObj) { 865 this._associatedObj = dwtObj; 866 }; 867 868 DwtMenu.prototype.setAssociatedElementId = 869 function(id){ 870 this._associatedElId = id; 871 }; 872 873 /** 874 * Checks a menu item (the menu must be radio or checkbox style). The menu item 875 * is identified through the given field/value pair. 876 * 877 * @param {Object} field a key for menu item data 878 * @param {Object} value value for the data of the menu item to check 879 * 880 */ 881 DwtMenu.prototype.checkItem = 882 function(field, value, skipNotify) { 883 var items = this._children.getArray(); 884 for (var i = 0; i < items.length; i++) { 885 var item = items[i]; 886 if (!(item.isStyle(DwtMenuItem.CHECK_STYLE) || item.isStyle(DwtMenuItem.RADIO_STYLE))) { 887 continue; 888 } 889 var val = item.getData(field); 890 if (val == value) 891 item.setChecked(true, skipNotify); 892 } 893 }; 894 895 /** 896 * Programmatically selects a menu item. The item can be specified with an index, 897 * or as the next or previous item based on which item is currently selected. If 898 * the new item is a separator or is disabled, it won't be selected. Instead, the 899 * next suitable item will be used. 900 * 901 * @param {boolean|number} which if <code>true</code>, selects the next menu item 902 * if <code>false</code>, selects the previous menu item 903 * if <code>DwtMenuItem</code>, select that menu item 904 * if <code>int</code>, selects the menu item with that index 905 */ 906 DwtMenu.prototype.setSelectedItem = 907 function(which, preventFocus) { 908 var currItem = this.__currentItem; 909 if (typeof(which) == "boolean") { 910 currItem = !currItem 911 ? this._children.get(0) 912 : which ? this._children.getNext(currItem) : this._children.getPrev(currItem); 913 } else if (which instanceof DwtMenuItem) { 914 if (this._children.contains(which)) 915 currItem = which; 916 } else { 917 which = Math.max(0, Math.min(this._children.size()-1, which)); 918 currItem = this._children.get(which); 919 } 920 // While the current item is not enabled or is a separator, try another 921 while (currItem) { 922 if (!currItem.isStyle) { // this is not a DwtMenuItem 923 if (!preventFocus) { 924 currItem.focus(); 925 } 926 break; 927 } 928 else if (!currItem.isStyle(DwtMenuItem.SEPARATOR_STYLE) && currItem.getEnabled() && currItem.getVisible()) { 929 break; 930 } 931 currItem = (which === false) ? this._children.getPrev(currItem) : this._children.getNext(currItem); 932 } 933 if (!currItem) { return; } 934 935 this.scrollToItem(currItem, true); 936 if (!preventFocus) { 937 currItem.focus(); 938 } 939 940 if (this.parent && this.parent._menuItemSelected) { 941 this.parent._menuItemSelected(currItem); 942 } 943 }; 944 945 DwtMenu.prototype.clearExternallySelectedItems = 946 function() { 947 if (this._externallySelected != null) { 948 this._externallySelected._deselect(); 949 this._externallySelected = null; 950 } 951 }; 952 953 DwtMenu.prototype.removeChild = 954 function(child) { 955 if (this._table) { 956 if (this._style == DwtMenu.BAR_STYLE) { 957 var cell = child.getHtmlElement().parentNode; 958 this._table.rows[0].deleteCell(Dwt.getCellIndex(cell)); 959 } else { 960 var el = child.getHtmlElement(); 961 if (el && el.parentNode && el.parentNode.parentNode.rowIndex > -1)// Make sure that the element exists in the table 962 this._table.deleteRow(el.parentNode.parentNode.rowIndex); 963 } 964 } 965 this._children.remove(child); 966 967 if (child.removeSelectionListener) { 968 child.removeSelectionListener(this._itemSelectionListener); 969 } 970 }; 971 972 DwtMenu.prototype.addChild = 973 function(child) { 974 DwtComposite.prototype.addChild.apply(this, arguments); 975 // Color pickers and calendars are not menu aware so we have to deal with 976 // them acordingly 977 if (Dwt.instanceOf(child, "DwtColorPicker") || Dwt.instanceOf(child, "DwtCalendar") || 978 (this._style == DwtMenu.GENERIC_WIDGET_STYLE)) { 979 980 this._addItem(child); 981 } 982 983 if (child.addSelectionListener) { 984 child.addSelectionListener(this._itemSelectionListener); 985 } 986 }; 987 988 // All children are added now, including menu items. Previously, it wasn't 989 // reparenting and that was preventing the menu items from using templates 990 // because they need to be in the DOM in order to get access to elements 991 // within the template. 992 DwtMenu.prototype._addItem = 993 function(item, index) { 994 if (this._style == DwtMenu.COLOR_PICKER_STYLE || 995 this._style == DwtMenu.CALENDAR_PICKER_STYLE || 996 this._style == DwtMenu.GENERIC_WIDGET_STYLE) 997 { 998 return; 999 } 1000 1001 var row; 1002 var col; 1003 if (this._style == DwtMenu.BAR_STYLE) { 1004 var rows = this._table.rows; 1005 row = (rows.length != 0) ? rows[0]: this._table.insertRow(0); 1006 if (index == null || index > row.cells.length) 1007 index = rows.cells.length; 1008 col = row.insertCell(index); 1009 col.align = "center"; 1010 col.vAlign = "middle"; 1011 var spc = row.insertCell(-1); 1012 spc.nowrap = true; 1013 spc.width = "7px"; 1014 } else { 1015 // If item we're adding is check/radio style, and its the first such 1016 // item in the menu, then we must instruct our other children to add 1017 // a "checked column" to ensure that things line up 1018 if (item.isStyle && (item.isStyle(DwtMenuItem.CHECK_STYLE) || item.isStyle(DwtMenuItem.RADIO_STYLE))) { 1019 this._checkItemAdded(); 1020 } 1021 if (index == null || index > this._table.rows.length) 1022 index = -1; 1023 row = this._table.insertRow(index); 1024 col = row.insertCell(0); 1025 } 1026 col.noWrap = true; 1027 col.appendChild(item.getHtmlElement()); 1028 // this._children.add(item, index); 1029 }; 1030 1031 DwtMenu.prototype._radioItemSelected = 1032 function(child, skipNotify) { 1033 var radioGroupId = child._radioGroupId; 1034 var sz = this._children.size(); 1035 var a = this._children.getArray(); 1036 for (var i = 0; i < sz; i++) { 1037 if (a[i] != child && a[i].isStyle(DwtMenuItem.RADIO_STYLE) && 1038 a[i]._radioGroupId == radioGroupId && a[i]._itemChecked) 1039 { 1040 a[i].setChecked(false, skipNotify); 1041 break; 1042 } 1043 } 1044 }; 1045 1046 DwtMenu.prototype._propagateItemSelection = function(evt) { 1047 if (this.isListenerRegistered(DwtEvent.SELECTION)) { 1048 this.notifyListeners(DwtEvent.SELECTION, evt); 1049 } 1050 }; 1051 1052 DwtMenu.prototype._menuHasCheckedItems = 1053 function() { 1054 return this._menuItemsHaveChecks; 1055 }; 1056 1057 DwtMenu.prototype._menuHasItemsWithIcons = 1058 function() { 1059 return this._menuItemsHaveIcons; 1060 }; 1061 1062 DwtMenu.prototype._menuHasSubmenus = 1063 function() { 1064 return (this._menuItemsWithSubmenus > 0); 1065 }; 1066 1067 /* Once an icon is added to any menuItem, then the menu will be considered 1068 * to contain menu items with icons in perpetuity */ 1069 DwtMenu.prototype._iconItemAdded = 1070 function(item) { 1071 if (!this._menuItemsHaveIcons) Dwt.addClass(this.getHtmlElement(), DwtMenu.HAS_ICON); 1072 this._menuItemsHaveIcons = true; 1073 }; 1074 1075 /* Once an check/radio is added to any menuItem, then the menu will be considered 1076 * to contain checked items in perpetuity */ 1077 DwtMenu.prototype._checkItemAdded = function(item) { 1078 if (!this._menuItemsHaveChecks) Dwt.addClass(this.getHtmlElement(), DwtMenu.HAS_CHECK); 1079 this._menuItemsHaveChecks = true; 1080 }; 1081 1082 DwtMenu.prototype._submenuItemAdded = 1083 function() { 1084 Dwt.addClass(this.getHtmlElement(), DwtMenu.HAS_SUBMENU); 1085 this._menuItemsWithSubmenus++; 1086 }; 1087 1088 DwtMenu.prototype._submenuItemRemoved = 1089 function() { 1090 if (this._menuItemsWithSubmenus == 1) { 1091 var sz = this._children.size(); 1092 var a = this._children.getArray(); 1093 for (var i = 0; i < sz; i++) 1094 a[i]._submenuItemRemoved(); 1095 } 1096 this._menuItemsWithSubmenus--; 1097 if (this._menuItemsWithSubmenus == 0) { 1098 Dwt.delClass(this.getHtmlElement(), DwtMenu.HAS_SUBMENU); 1099 } 1100 }; 1101 1102 DwtMenu.prototype._popdownSubmenus = function() { 1103 var sz = this._children.size(); 1104 var a = this._children.getArray(); 1105 for (var i = 0; i < sz; i++) { 1106 if (a[i]._popdownMenu) a[i]._popdownMenu(); 1107 } 1108 }; 1109 1110 DwtMenu.prototype.dontStealFocus = 1111 function(val) { 1112 if (val == null) 1113 val = true; 1114 this.__preventMenuFocus = !!val; 1115 }; 1116 1117 DwtMenu.prototype._doPopup = 1118 function(x, y, kbGenerated) { 1119 1120 // bump z-index if we're inside a dialog 1121 var zIndex = DwtBaseDialog.getActiveDialog() ? Dwt.Z_DIALOG_MENU : Dwt.Z_MENU; 1122 this.setZIndex(zIndex); 1123 this.setVisible(true); 1124 1125 this.render(x, y); 1126 1127 var isScroll = this._layoutStyle == DwtMenu.LAYOUT_SCROLL; 1128 var isCascade = this._layoutStyle == DwtMenu.LAYOUT_CASCADE; 1129 if (!isScroll) { 1130 this.setScrollStyle(this._isPopupStyle() && isCascade ? Dwt.CLIP : Dwt.SCROLL); 1131 } else if (this._tableContainer) { 1132 Dwt.setScrollStyle(this._tableContainer, Dwt.CLIP); 1133 } 1134 1135 this.notifyListeners(DwtEvent.POPUP, this); 1136 1137 // Hide the tooltip 1138 var tooltip = this.shell.getToolTip(); 1139 if (tooltip) { 1140 tooltip.popdown(); 1141 } 1142 1143 this._popupActionId = -1; 1144 this._isPoppedUp = true; 1145 1146 var omem = DwtOutsideMouseEventMgr.INSTANCE; 1147 var omemParams = { 1148 id: "DwtMenu", 1149 obj: this, 1150 outsideListener: this._outsideListener 1151 } 1152 omem.startListening(omemParams); 1153 1154 if (!DwtMenu._activeMenu) { 1155 DwtMenu._activeMenu = this; 1156 DwtMenu._activeMenuUp = true; 1157 } 1158 1159 DwtMenu._activeMenuIds.add(this._htmlElId, null, true); 1160 DwtMenu._activeMenuIds.sort(); 1161 DwtMenu._activeMenus.add(this, null, true); 1162 1163 // Put our tabgroup in play 1164 DwtShell.getShell(window).getKeyboardMgr().pushTabGroup(this._compositeTabGroup, this.__preventMenuFocus); 1165 1166 /* If the popup was keyboard generated, then pick the first enabled child 1167 item */ 1168 if (kbGenerated || !this.parent.isDwtMenu) { 1169 this.setSelectedItem(0, this.__preventMenuFocus); 1170 } 1171 }; 1172 1173 DwtMenu.prototype.getSize = 1174 function(incScroll) { 1175 var size; 1176 if (this._table) { 1177 size = Dwt.getSize(this._table, incScroll); 1178 } else { 1179 size = DwtComposite.prototype.getSize.call(this, incScroll); 1180 } 1181 if (this._width && this._width > size.x) size.x = this._width; 1182 return size; 1183 }; 1184 1185 DwtMenu.prototype._doPopdown = 1186 function(ev) { 1187 // Notify all sub menus to pop themselves down 1188 var a = this._children.getArray(); 1189 var s = this._children.size(); 1190 for (var i = 0; i < s; i++) { 1191 if ((a[i] instanceof DwtMenuItem) && !(a[i].isStyle(DwtMenuItem.SEPARATOR_STYLE))) { 1192 a[i]._popdownMenu(); 1193 } 1194 } 1195 this.setVisible(false); 1196 this._ev = ev; 1197 1198 this.notifyListeners(DwtEvent.POPDOWN, this); 1199 1200 var omem = DwtOutsideMouseEventMgr.INSTANCE; 1201 omem.stopListening({id:"DwtMenu", obj:this}); 1202 1203 if (DwtMenu._activeMenu == this) { 1204 DwtMenu._activeMenu = null; 1205 DwtMenu._activeMenuUp = false; 1206 } 1207 DwtMenu._activeMenuIds.remove(this._htmlElId); 1208 DwtMenu._activeMenus.remove(this); 1209 this._popdownActionId = -1; 1210 this._isPoppedUp = false; 1211 1212 if (this._isPopupStyle() && this._table && this._table.rows && this._table.rows.length && this._table.rows[0].cells.length) { 1213 var numColumns = this._table.rows[0].cells.length; 1214 var numRows = this._table.rows.length; 1215 for (var i = 1; i < numColumns; i++) { 1216 for (var j = 0; j < numRows; j++) { 1217 var cell = this._table.rows[j].cells[i]; 1218 if (!cell.empty) { 1219 var child = cell.firstChild; 1220 var row = this._table.insertRow(this._table.rows.length); 1221 var cell = row.insertCell(0); 1222 while (child != null) { 1223 cell.appendChild(child); 1224 child = child.nextSibling; 1225 } 1226 } 1227 } 1228 } 1229 for (var j = 0; j < numRows; j++) { 1230 var row = this._table.rows[j]; 1231 for (var i = row.cells.length - 1; i > 0; i--) { 1232 row.deleteCell(i); 1233 } 1234 } 1235 } 1236 1237 if (this.__currentItem) { 1238 this.__currentItem.blur(); 1239 } 1240 1241 // Take our tabgroup out of play 1242 DwtShell.getShell(window).getKeyboardMgr().popTabGroup(this._compositeTabGroup); 1243 }; 1244 1245 DwtMenu.prototype._getActiveItem = 1246 function(){ 1247 var a = this._children.getArray(); 1248 var s = this._children.size(); 1249 for (var i = 0; i < s; i++) { 1250 if (a[i]._isMenuPoppedUp()) 1251 return a[i]; 1252 } 1253 return null; 1254 }; 1255 1256 DwtMenu._outsideMouseDownListener = 1257 function(ev) { 1258 1259 if (DwtMenu._activeMenuUp) { 1260 var menu = DwtMenu._activeMenu; 1261 1262 // assuming that the active menu is the parent of all other menus 1263 // that are up, search through the array of child menu dom IDs as 1264 // well as our own. 1265 var id = menu._htmlElId; 1266 var htmlEl = DwtUiEvent.getTarget(ev); 1267 while (htmlEl != null) { 1268 if (htmlEl.id && htmlEl.id != "" && 1269 (htmlEl.id == id || htmlEl.id == menu._associatedElId || 1270 DwtMenu._activeMenuIds.binarySearch(htmlEl.id) != -1 )) { 1271 return false; 1272 } 1273 htmlEl = htmlEl.parentNode; 1274 } 1275 1276 // If we've gotten here, the mousedown happened outside the active 1277 // menu, so we hide it. 1278 menu.popdown(0, ev); 1279 1280 //it should remove all the active menus 1281 var cMenu = null ; 1282 do { 1283 cMenu = DwtMenu._activeMenus.getLast(); 1284 if (cMenu!= null && cMenu instanceof DwtMenu) cMenu.popdown(); 1285 } while (cMenu != null) ; 1286 } 1287 }; 1288 1289 DwtMenu._stopEvent = function(e) { 1290 if (!e) e = window.event; 1291 e.cancelBubble = true; 1292 if (e.stopPropagation) { 1293 e.stopPropagation(); 1294 } 1295 }; 1296 1297 /* 1298 * Returns true if any menu is currently popped up. 1299 */ 1300 DwtMenu.menuShowing = 1301 function() { 1302 return DwtMenu._activeMenuUp; 1303 }; 1304 1305 DwtMenu.closeActiveMenu = 1306 function(ev) { 1307 if (DwtMenu._activeMenuUp) { 1308 DwtMenu._activeMenu.popdown(0, ev); 1309 } 1310 }; 1311