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