1 /*
  2  * ***** BEGIN LICENSE BLOCK *****
  3  * Zimbra Collaboration Suite Web Client
  4  * Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 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, 2013, 2014, 2015, 2016 Synacor, Inc. All Rights Reserved.
 21  * ***** END LICENSE BLOCK *****
 22  */
 23 
 24 /**
 25  * Creates a shell.
 26  * @constructor
 27  * @class
 28  * This class represents a shell, the first widget that must be instantiated in a Dwt based 
 29  * application. By default the shell covers the whole browser window, though it may also be 
 30  * instantiated within an HTML element.
 31  * <p>
 32  * {@link DwtShell} should <b>NOT</b> be subclassed.
 33  * </p>
 34  *
 35  * @author Ross Dargahi
 36  * 
 37  * @param	{hash}	params		a hash of parameters
 38  * @param {string}	params.className			the CSS class name
 39  * @param {boolean}	params.docBodyScrollable	if <code>true</code>, then the document body is set to be scrollable
 40  * @param {Element}	params.userShell			an HTML element that will be reparented into an absolutely
 41  *											postioned container in this shell. This is useful in the situation where you have an HTML 
 42  *											template and want to use this in context of Dwt.
 43  * @param {Boolean}	params.useCurtain			if <code>true</code>, a curtain overlay is created to be used between hidden and viewable elements 
 44  *											using z-index (see {@link Dwt}) for various layering constants)
 45  *
 46  * @extends		DwtComposite
 47  */
 48 DwtShell = function(params) {
 49 	if (window._dwtShellId) {
 50 		throw new DwtException("DwtShell already exists for window", DwtException.INVALID_OP, "DwtShell");
 51 	}
 52 
 53 	var className = params.className || "DwtShell";
 54 	DwtComposite.call(this, {className:className});
 55 
 56 	// HACK! This is a hack to make sure that the control methods work 
 57 	// with DwtShell since the parent of DwtShell is null. 
 58 	this.__ctrlInited = true;
 59 
 60 	document.body.style.margin = 0;
 61 	if (!params.docBodyScrollable) {
 62 		if (AjxEnv.isIE) {
 63 			document.body.onscroll = DwtShell.__onBodyScroll;
 64 		}
 65 		document.body.style.overflow = "hidden";
 66 	}
 67 
 68 	document.body.onselect = DwtShell._preventDefaultSelectPrt;
 69 	document.body.onselectstart = DwtShell._preventDefaultSelectPrt;
 70 	document.body.oncontextmenu = DwtShell._preventDefaultPrt;
 71 	window.onresize = DwtShell._resizeHdlr;
 72 
 73 	var htmlElement = document.createElement("div");
 74 	this._htmlElId = window._dwtShellId = htmlElement.id = params.id || Dwt.getNextId();
 75 	DwtControl.ALL_BY_ID[this._htmlElId] = this;
 76 
 77 	htmlElement.className = className;
 78 	htmlElement.style.width = htmlElement.style.height = "100%";
 79 	Dwt.setPosition(htmlElement, DwtControl.ABSOLUTE_STYLE);
 80 
 81 	if (htmlElement.style.overflow) {
 82 		htmlElement.style.overflow = null;
 83 	}
 84 
 85 	// if there is a user shell (body content), move it below this shell
 86 	// into a container that's absolutely positioned
 87 	try {
 88 		if (params.userShell) {
 89 			document.body.removeChild(params.userShell);
 90 		}
 91 	} catch (ex) {}
 92 	document.body.appendChild(htmlElement);
 93 	if (params.userShell) {
 94 		var userShellContainer = new DwtControl({parent:this, posStyle:Dwt.ABSOLUTE_STYLE});
 95 		userShellContainer.getHtmlElement().appendChild(params.userShell);
 96 		userShellContainer.setSize("100%", "100%");
 97 		userShellContainer.zShow(true);
 98 		this._userShell = params.userShell;
 99 	} else {
100 		this._userShell = null;
101 	}
102 	this.shell = this;
103 
104 	// Busy overlay - used when we want to enforce a modal busy state
105 	this._createBusyOverlay(htmlElement);
106 
107 	// Veil overlay - used by DwtDialog to disable underlying app
108 	this._veilOverlay = document.createElement("div");
109 	this._veilOverlay.className = (!AjxEnv.isLinux) ? "VeilOverlay" : "VeilOverlay-linux";
110 	this._veilOverlay.style.position = "absolute";
111 	this._veilOverlay.style.cursor = AjxEnv.isIE6up ? "not-allowed" : "wait";
112 	Dwt.setBounds(this._veilOverlay, 0, 0, "100%", "100%");
113 	Dwt.setZIndex(this._veilOverlay, Dwt.Z_HIDDEN);
114 	this._veilOverlay.veilZ = new Array();
115 	this._veilOverlay.veilZ.push(Dwt.Z_HIDDEN);
116 	this._veilOverlay.dialogZ = new Array();
117 	this._veilOverlay.activeDialogs = new Array();
118 	this._veilOverlay.innerHTML = "<table cellspacing=0 cellpadding=0 style='width:100%; height:100%'><tr><td> </td></tr></table>";
119 	htmlElement.appendChild(this._veilOverlay);
120 
121 	// Curtain overlay - used between hidden and viewable elements using z-index
122 	if (params.useCurtain) {
123 		this._curtainOverlay = document.createElement("div");
124 		this._curtainOverlay.className = "CurtainOverlay";
125 		this._curtainOverlay.style.position = "absolute";
126 		Dwt.setBounds(this._curtainOverlay, 0, 0, "100%", "100%")
127 		Dwt.setZIndex(this._curtainOverlay, Dwt.Z_CURTAIN);
128 		this._curtainOverlay.innerHTML = "<table cellspacing=0 cellpadding=0 style='width:100%; height:100%'><tr><td> </td></tr></table>";
129 		htmlElement.appendChild(this._curtainOverlay);
130 	}
131 
132 	this._uiEvent = new DwtUiEvent(true);
133 	this.relayout();
134 
135 	// tooltip singleton used by all controls in shell
136 	this._toolTip = new DwtToolTip(this);
137 	this._hoverMgr = new DwtHoverMgr();
138 	
139 	this._keyboardMgr = new DwtKeyboardMgr(this);
140 }
141 
142 DwtShell.prototype = new DwtComposite;
143 DwtShell.prototype.constructor = DwtShell;
144 
145 /**
146  * DwtDialog not defined yet, can't base ID on it
147  * @private
148  */
149 DwtShell.CANCEL_BUTTON = -1;
150 
151 // Event objects used to populate events so we dont need to create them for each event
152 DwtShell.controlEvent 	= new DwtControlEvent();
153 DwtShell.focusEvent 	= new DwtFocusEvent();
154 DwtShell.keyEvent 		= new DwtKeyEvent();
155 DwtShell.mouseEvent 	= new DwtMouseEvent();
156 DwtShell.selectionEvent = new DwtSelectionEvent(true);
157 DwtShell.treeEvent 		= new DwtTreeEvent();
158 
159 DwtShell._GLOBAL_SELECTION = "GlobalSelection";
160 
161 // Public methods
162 
163 DwtShell.prototype.toString = 
164 function() {
165 	return "DwtShell";
166 }
167 
168 /**
169  * Gets the shell managing the browser window (if any).
170  *
171  * @param {Window}      win     the global context
172  * @return {DwtShell}		the shell or <code>null</code>
173  */
174 DwtShell.getShell = function(win) {
175     win = win || window;
176 	return DwtControl.fromElementId(win._dwtShellId);
177 };
178 
179 /**
180  * Gets the shell's keyboard manager.
181  * 
182  * @return	{DwtKeyboardMgr}		the keyboard manager
183  * 
184  * @private
185  */
186 DwtShell.prototype.getKeyboardMgr =
187 function() {
188 	return this._keyboardMgr;
189 }
190 
191 /**
192  * Sets the busy overlay. The busy overlay disables input to the application and makes the 
193  * cursor a wait cursor. Optionally a work in progress (WIP) dialog may be requested. Since
194  * multiple calls to this method may be interleaved, it accepts a unique ID to keep them
195  * separate. We also maintain a count of outstanding calls to <code>setBusy(true)</code>. When that count
196  * changes between 0 and 1, the busy overlay is applied or removed.
197  * 
198  * @param {boolean}	busy			if <code>true</code>, set the busy overlay, otherwise hide the busy overlay
199  * @param {number}	id					a unique ID for this instance
200  * @param {boolean}	showBusyDialog 		if <code>true</code>, show the WIP dialog
201  * @param {number}	busyDialogDelay 		the number of ms to delay before popping up the WIP dialog
202  * @param {AjxCallback}	cancelBusyCallback	the callback to run when OK button is pressed in WIP dialog
203  */ 
204 DwtShell.prototype.setBusy =
205 function(busy, id, showBusyDialog, busyDialogDelay, cancelBusyCallback) {
206 	if (busy) {
207 		this._setBusyCount++;
208 	} else if (this._setBusyCount > 0) {
209 		this._setBusyCount--;
210 	}
211 
212     if (!this._setBusy && (this._setBusyCount > 0)) {
213 		// transition from non-busy to busy state
214 		Dwt.setCursor(this._busyOverlay, "wait");
215     	Dwt.setVisible(this._busyOverlay, true);
216     	this._setBusy = this._blockInput = true;
217     	DBG.println(AjxDebug.DBG2, "set busy overlay, id = " + id);
218     } else if (this._setBusy && (this._setBusyCount <= 0)) {
219 		// transition from busy to non-busy state
220 	    Dwt.setCursor(this._busyOverlay, "default");
221 	    Dwt.setVisible(this._busyOverlay, false);
222 	    this._setBusy = this._blockInput = false;
223     	DBG.println(AjxDebug.DBG2, "remove busy overlay, id = " + id);
224 	}
225 	
226 	// handle busy dialog whether we've changed state or not
227 	if (busy && showBusyDialog) {
228 		if (busyDialogDelay && busyDialogDelay > 0) {
229 			this._busyActionId[id] = AjxTimedAction.scheduleAction(this._busyTimedAction, busyDialogDelay);
230 		} else {
231 			this._showBusyDialogAction(id);
232 		}
233 
234 		this._cancelBusyCallback = cancelBusyCallback;
235 		if (this._busyDialog) {
236 			this._busyDialog.setButtonEnabled(DwtShell.CANCEL_BUTTON, (cancelBusyCallback != null));
237 		}
238 	} else {
239     	if (this._busyActionId[id] && (this._busyActionId[id] != -1)) {
240     		AjxTimedAction.cancelAction(this._busyActionId[id]);
241     		this._busyActionId[id] = -1;
242     	}
243    		if (this._busyDialog && this._busyDialog.isPoppedUp) {
244     		this._busyDialog.popdown();
245    		}
246     } 
247 }
248 
249 // (hee hee)
250 DwtShell.prototype.getBusy =
251 function() {
252 	return this._setBusy;
253 };
254 
255 /**
256  * Sets the text for the shell busy dialog
257  *
258  * @param {string}	text 		the text to set (may be HTML)
259  */
260 DwtShell.prototype.setBusyDialogText =
261 function(text) {
262 	this._busyDialogText = text;
263 	if (this._busyDialogTxt) {
264 		this._busyDialogTxt.innerHTML = (text) ? text : "";
265 	}
266 }
267 
268 /**
269  * Sets the shell busy dialog title.
270  * 
271  * @param {string}	title 		the title text
272  */
273 DwtShell.prototype.setBusyDialogTitle =
274 function(title) {
275 	this._busyDialogTitle = title;
276 	if (this._busyDialog) {
277 		this._busyDialog.setTitle((title) ? title : AjxMsg.workInProgress);
278 	}
279 }
280 
281 DwtShell.prototype.getHoverMgr = 
282 function() {
283 	return this._hoverMgr;
284 }
285 
286 /**
287  * Gets the tool tip.
288  * 
289  * @return	{string}	the tool tip
290  */
291 DwtShell.prototype.getToolTip = 
292 function() {
293 	return this._toolTip;
294 }
295 
296 DwtShell.prototype.getH = 
297 function(incScroll) {
298 	return (!this._virtual) ? Dwt.getSize(this.getHtmlElement(), incScroll).y
299 	                        : Dwt.getSize(document.body, incScroll).y;
300 }
301 
302 DwtShell.prototype.getW = 
303 function(incScroll) {
304 	return (!this._virtual) ? Dwt.getSize(this.getHtmlElement(), incScroll).x
305 	                        : Dwt.getSize(document.body, incScroll).x;
306 }
307 
308 DwtShell.prototype.getSize = 
309 function(incScroll) {
310 	return (!this._virtual) ? Dwt.getSize(this.getHtmlElement(), incScroll)
311 	                        : Dwt.getSize(document.body, incScroll);
312 }
313 
314 DwtShell.prototype.getLocation =
315 function() {
316 	return (!this._virtual) ? Dwt.getLocation(this.getHtmlElement())
317 	                        : Dwt.getLocation(document.body);
318 }
319 
320 DwtShell.prototype.getX =
321 function() {
322 	return (!this._virtual) ? Dwt.getLocation(this.getHtmlElement()).x
323 	                        : Dwt.getLocation(document.body).x;
324 }
325 
326 DwtShell.prototype.getY =
327 function() {
328 	return (!this._virtual) ? Dwt.getLocation(this.getHtmlElement()).y
329 	                        : Dwt.getLocation(document.body).y;
330 }
331 
332 
333 DwtShell.prototype.getBounds = 
334 function(incScroll) {
335 	return (!this._virtual) ? Dwt.getBounds(this.getHtmlElement(), incScroll)
336 	                        : Dwt.getBounds(document.body, incScroll);
337 }
338 
339 /**
340  * If the shell is set as a virtual shell, then all children that are 
341  * directly added to the shell become children on the page's body element. This
342  * is useful in the cases where Dwt is to beused  with existing HTML documents
343  * rather than as the foundation for an application.
344  * 
345  * @private
346  */
347 DwtShell.prototype.setVirtual =
348 function() {
349 	this._virtual = true;
350 	this.setVisible(false);
351 }
352 
353 /**
354  * Adds a focus listener.
355  * 
356  * @param	{AjxListener}	listener		the listener
357  */
358 DwtShell.prototype.addFocusListener =
359 function(listener) {
360 	if (!this._hasFocusHandler) {
361 		var doc = document;
362 		if ((typeof doc.onfocusin != "undefined" ) && doc.attachEvent) {  // if (IE)
363 			doc.attachEvent("onfocusin", DwtShell.__focusHdlr);
364 		} else if (window.addEventListener) {
365 			window.addEventListener("focus", DwtShell.__focusHdlr, false);
366 		}
367 		this._hasFocusHandler = true;
368 	}
369 	this.addListener(DwtEvent.ONFOCUS, listener);
370 };
371 
372 /**
373  * Adds a blur listener.
374  * 
375  * @param	{AjxListener}	listener		the listener
376  */
377 DwtShell.prototype.addBlurListener =
378 function(listener) {
379 	if (!this._hasBlurHandler) {
380 		var doc = document;
381 		if ((typeof doc.onfocusin != "undefined" ) && doc.attachEvent) {  // if (IE)
382 			doc.attachEvent("onfocusout", DwtShell.__blurHdlr);
383 		} else if (window.addEventListener) {
384 			window.addEventListener("blur", DwtShell.__blurHdlr, false);
385 		}
386 		this._hasBlurHandler = true;
387 	}
388 	this.addListener(DwtEvent.ONBLUR, listener);
389 };
390 
391 /**
392  * Adds a global selection listener.
393  * 
394  * @param	{AjxListener}	listener		the listener
395  */
396 DwtShell.prototype.addGlobalSelectionListener =
397 function(listener) {
398 	this.addListener(DwtShell._GLOBAL_SELECTION, listener);
399 };
400 
401 /**
402  * Removes a global selection listener.
403  * 
404  * @param	{AjxListener}	listener		the listener
405  */
406 DwtShell.prototype.removeGlobalSelectionListener =
407 function(listener) {
408 	this.removeListener(DwtShell._GLOBAL_SELECTION, listener);
409 };
410 
411 DwtShell.prototype.notifyGlobalSelection =
412 function(event) {
413 	this.notifyListeners(DwtShell._GLOBAL_SELECTION, event);
414 };
415 
416 /**
417  * @return {boolean}	<code>true</code> if the shell is virtual
418  * 
419  * @private
420  */
421 DwtShell.prototype.isVirtual =
422 function() {
423 	return this._virtual;
424 }
425 
426 
427 // Private / protected methods
428 
429 DwtShell.prototype._showBusyDialogAction =
430 function(id) {
431 	var bd = this._getBusyDialog();
432 	bd.popup();
433 	this._busyActionId[id] = -1;
434 }
435 
436 DwtShell.prototype._createBusyOverlay =
437 function(htmlElement) {
438     this._busyOverlay = document.createElement("div");
439     this._busyOverlay.className = (!AjxEnv.isLinux) ? "BusyOverlay" : "BusyOverlay-linux";
440     this._busyOverlay.style.position = "absolute";
441     Dwt.setBounds(this._busyOverlay, 0, 0, "100%", "100%")
442     Dwt.setZIndex(this._busyOverlay, Dwt.Z_VEIL);
443     this._busyOverlay.innerHTML = "<table cellspacing=0 cellpadding=0 style='width:100%; height:100%'><tr><td> </td></tr></table>";
444     htmlElement.appendChild(this._busyOverlay);
445 	Dwt.setVisible(this._busyOverlay, false);
446 
447 	this._busyTimedAction = new AjxTimedAction(this, this._showBusyDialogAction);
448 	this._busyActionId = {};
449 	
450 	this._setBusyCount = 0;
451 	this._setBusy = false;
452 }
453 
454 DwtShell.prototype._getBusyDialog =
455 function(htmlElement) {
456 	if (!this._busyDialog) {
457 		var cancelButton = new DwtDialog_ButtonDescriptor(DwtShell.CANCEL_BUTTON, AjxMsg.cancelRequest, DwtDialog.ALIGN_CENTER);
458 	    this._busyDialog = new DwtDialog({parent:this, className:"DwtShellBusyDialog", title:AjxMsg.workInProgress,
459 	    								  standardButtons:DwtDialog.NO_BUTTONS, extraButtons:[cancelButton], zIndex:Dwt.BUSY + 10});
460 	    this._busyDialog.registerCallback(DwtShell.CANCEL_BUTTON, this._busyCancelButtonListener, this);
461 	    var txtId = Dwt.getNextId();
462 	    var html = [
463 	        "<table class='DialogContent'><tr>",
464 	            "<td><div class='WaitIcon'></div></td><td class='MsgText' id='", txtId, "'> </td>",
465 	        "</tr></table>"].join("");
466 	    
467 	    this._busyDialog.setContent(html);
468 	    this._busyDialogTxt = document.getElementById(txtId);
469 		if (this._busyDialogText) {
470 			this._busyDialogTxt.innerHTML = this._busyDialogText;
471 		}
472 		if (this._busyDialogTitle) {
473 			this._busyDialog.setTitle(this._busyDialogTitle);
474 		}
475 		this._busyDialog.setButtonEnabled(DwtShell.CANCEL_BUTTON, (this._cancelBusyCallback != null));
476 	}
477 	return this._busyDialog;
478 };
479 
480 /**
481  *
482  * Relayout user skin elements. Called whenever hiding or showing a
483  * part of the user skin, or when resizing the window.
484  *
485  * The layout works on elements of class "skin_layout_filler" -- which
486  * must also be of either class "skin_layout_row" or
487  * "skin_layout_cell". It finds the size of our parent, subtract the
488  * sizes all sibling rows or cells (excluding other fillers) and
489  * divide the remaining size between this filler and any sibling
490  * fillers.
491  */
492 DwtShell.prototype.relayout =
493 function() {
494     this._currWinSize = Dwt.getWindowSize();
495 
496     if (this._userShell) {
497         var fillers = Dwt.byClassName('skin_layout_filler', this._userShell);
498 
499         AjxUtil.foreach(fillers, function(elem) {
500             if (Dwt.hasClass(elem, 'skin_layout_row')) {
501                 var row = elem;
502                 var table = row.parentNode;
503                 var height = Dwt.getSize(table).y;
504                 var nfillers = 0;
505 
506                 var insets = Dwt.getInsets(table);
507                 height -= insets.top + insets.bottom;
508                 var margins = Dwt.getMargins(row);
509                 height -= margins.top + margins.bottom;
510 
511                 AjxUtil.foreach(table.children, function(otherrow) {
512                     var margins = Dwt.getMargins(otherrow);
513                     height -= margins.top + margins.bottom;
514 
515                     if (Dwt.hasClass(otherrow, 'skin_layout_filler')) {
516                         nfillers += 1;
517                     } else {
518                         var otherheight = Dwt.getSize(otherrow).y;
519 
520                         AjxUtil.foreach(otherrow.children, function(cell) {
521                             var margins = Dwt.getMargins(cell);
522                             var height = Dwt.getSize(cell).y +
523                                 margins.top + margins.bottom;
524                             otherheight = Math.max(otherheight, height);
525                         });
526 
527                         height -= otherheight;
528                     }
529                 });
530 
531                 row.style.height = Math.max(height / nfillers, 0) + 'px';
532 
533             } else if (Dwt.hasClass(elem, 'skin_layout_cell')) {
534                 var cell = elem;
535                 var row = cell.parentNode;
536                 var table = row.parentNode;
537                 var width = Dwt.getSize(table).x;
538                 var nfillers = 0;
539 
540                 var insets = Dwt.getInsets(table);
541                 width -= insets.left + insets.right;
542                 var margins = Dwt.getMargins(row);
543                 width -= margins.left + margins.right;
544 
545                 AjxUtil.foreach(row.children, function(othercell) {
546                     var margins = Dwt.getMargins(othercell);
547                     width -= margins.left + margins.right;
548 
549                     if (Dwt.hasClass(othercell, 'skin_layout_filler')) {
550                         nfillers += 1;
551                     } else {
552 
553 						if (cell.id === "skin_td_main" && othercell.id === "skin_td_tree_app_sash" &&
554 							AjxEnv.isChrome && !AjxUtil.isInt(window.devicePixelRatio)) {
555 							// See bug #96808.
556 							// Chrome seems to change the hardcoded pixel value.
557 							// Depending on the zoom level it fluctuates +/- 1. This messes up elements' width calculation.
558 							// The problematic element is #skin_td_tree_app_sash when calculating width for #skin_td_main.
559 							// The value of sash's width is set in skins.
560 							// Decreasing the width by 3 works on all zoom levels.
561 							// Only do this on non-integer devicePixelRatio
562 							// (e.g. skip 100% zoom on retina and non-retina displays where devicePixelRatio is 2 and 1).
563 							width -= 3;
564 						}
565 
566                         width -= Dwt.getSize(othercell).x;
567                     }
568                 });
569 
570                 cell.style.width = Math.max(width / nfillers, 0) + 'px';
571 
572             } else if (window.console) {
573                 console.warn('not fixing sizes for element!', elem);
574             }
575         });
576     }
577 };
578 
579 // Listeners
580 
581 DwtShell.prototype._busyCancelButtonListener =
582 function(ev) {
583 	this._cancelBusyCallback.run();
584 	if (this._busyDialog) {
585 		this._busyDialog.popdown();
586 	}
587 }
588 
589 
590 // Static methods
591 
592 DwtShell._preventDefaultSelectPrt =
593 function(ev) {
594     var evt = DwtControl.fromElementId(window._dwtShellId)._uiEvent;
595     evt.setFromDhtmlEvent(ev, true);
596 
597 	if (evt.dwtObj && evt.dwtObj instanceof DwtControl && !evt.dwtObj.preventSelection(evt.target)) {
598         evt._stopPropagation = false;
599         evt._returnValue = true;
600     } else {
601         evt._stopPropagation = true;
602         evt._returnValue = false;
603     }
604     evt.setToDhtmlEvent(ev);
605     return !evt._stopPropagation;
606 };
607 
608 DwtShell._preventDefaultPrt =
609 function(ev) {
610 	ev = DwtUiEvent.getEvent(ev);
611 	var target = ev.target ? ev.target : ev.srcElement;
612 	
613     var evt = DwtControl.fromElementId(window._dwtShellId)._uiEvent;
614     evt.setFromDhtmlEvent(ev, true);
615 	//default behavior
616     evt._stopPropagation = true;
617     evt._returnValue = false;
618 	if (evt.dwtObj && evt.dwtObj instanceof DwtControl && !evt.dwtObj.preventContextMenu(evt.target)) {
619         evt._stopPropagation = false;
620         evt._returnValue = true;
621     } else if (target != null && typeof(target) == 'object') {
622      	if ((target.tagName == "A" ||  target.tagName == "a") && target.href) {
623 	        evt._stopPropagation = false;
624     	    evt._returnValue = true;
625     	}
626     } 
627     
628     evt.setToDhtmlEvent(ev);
629     return evt._returnValue;
630 };
631 
632 
633 /* This the resize handler to track when the browser window size changes */
634 DwtShell._resizeHdlr =
635 function(ev) {
636 	var shell = DwtControl.fromElementId(window._dwtShellId);
637 	if (shell.isListenerRegistered(DwtEvent.CONTROL)) {
638 	 	var evt = DwtShell.controlEvent;
639 	 	evt.reset();
640 	 	evt.oldWidth = shell._currWinSize.x;
641 	 	evt.oldHeight = shell._currWinSize.y;
642 		shell.relayout();
643 	 	evt.newWidth = shell._currWinSize.x;
644 	 	evt.newHeight = shell._currWinSize.y;
645 	 	shell.notifyListeners(DwtEvent.CONTROL, evt);
646 	} else {
647 		shell.relayout();
648 	}
649 };
650 
651 DwtShell.__onBodyScroll = function() {
652 	// alert(document.body.scrollTop + "/" + document.body.scrollLeft);
653 	document.body.scrollTop = 0;
654 	document.body.scrollLeft = 0;
655 	// DwtShell._resizeHdlr();
656 };
657 
658 DwtShell.__focusHdlr =
659 function() {
660 	var focusEvent = DwtShell.focusEvent;
661 	var self = DwtShell.getShell(window);
662 	focusEvent.dwtObj = self;
663 	focusEvent.state = DwtFocusEvent.FOCUS;
664 	self.notifyListeners(DwtEvent.ONFOCUS, focusEvent);
665 };
666 
667 DwtShell.__blurHdlr =
668 function() {
669 	var focusEvent = DwtShell.focusEvent;
670 	var self = DwtShell.getShell(window);
671 	focusEvent.dwtObj = self;
672 	focusEvent.state = DwtFocusEvent.BLUR;
673 	self.notifyListeners(DwtEvent.ONBLUR, focusEvent);
674 };
675