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  * Default constructor.
 26  * @constructor
 27  * @class
 28  * Dwt is a static class that defines a number of constants and helper methods that
 29  * support the <code>ajax.dwt.*</code> package.
 30  *
 31  * @author Ross Dargahi
 32  * @author Conrad Damon
 33  */
 34 
 35 Dwt = function() {
 36 };
 37 
 38 // Constants for positioning
 39 /**
 40  * Static position style.
 41  */
 42 Dwt.STATIC_STYLE = "static";
 43 
 44 /**
 45  * Absolute position style.
 46  */
 47 Dwt.ABSOLUTE_STYLE = "absolute";
 48 
 49 /**
 50  * Relative position style.
 51  */
 52 Dwt.RELATIVE_STYLE = "relative";
 53 
 54 /**
 55  * Fixed position style.
 56  */
 57 Dwt.FIXED_STYLE = "fixed";
 58 
 59 // Background repeat
 60 /**
 61  * Do not repeat background image.
 62  */
 63 Dwt.NO_REPEAT = "no-repeat";
 64 
 65 /**
 66  * Repeat background image.
 67  */
 68 Dwt.REPEAT = "repeat";
 69 
 70 /**
 71  * Repeat background image horizontally.
 72  */
 73 Dwt.REPEAT_X = "repeat-x";
 74 
 75 /**
 76  * Repeat background image vertically.
 77  */
 78 Dwt.REPEAT_Y = "repeat-y";
 79 
 80 
 81 // display style
 82 /**
 83  * Inline display style.
 84  */
 85 Dwt.DISPLAY_INLINE = "inline";
 86 
 87 /**
 88  * Block display style.
 89  */
 90 Dwt.DISPLAY_BLOCK = "block";
 91 
 92 /**
 93  * No display style.
 94  */
 95 Dwt.DISPLAY_NONE = "none";
 96 
 97 /**
 98  * Table row style.
 99  */
100 Dwt.DISPLAY_TABLE_ROW = "table-row";
101 
102 /**
103  * Table cell style.
104  */
105 Dwt.DISPLAY_TABLE_CELL = "table-cell";
106 
107 // Scroll constants
108 /**
109  * Clip on overflow.
110  */
111 Dwt.CLIP = 1;
112 
113 /**
114  * Allow overflow to be visible.
115  */
116 Dwt.VISIBLE = 2;
117 
118 /**
119  * Automatically create scrollbars if content overflows.
120  */
121 Dwt.SCROLL = 3;
122 
123 /**
124  * Always have scrollbars whether content overflows or not.
125  */
126 Dwt.FIXED_SCROLL = 4;
127 
128 /**
129  * Only show scrollbars on Y when content overflows.
130  */
131 Dwt.SCROLL_Y = 5;
132 
133 /**
134  * Only show scrollbars on X when content overflows.
135  */
136 Dwt.SCROLL_X = 6;
137 
138 
139 // z-index order
140 /** 
141  * Hidden layer. Elements at this layer will be hidden from view.
142  */
143 Dwt.Z_HIDDEN = 100;
144 
145 /**
146  * The curtain layer.
147  * @type int
148  * @see DwtShell
149  */
150 Dwt.Z_CURTAIN = 200;
151 
152 
153 /**
154  * Visible layer. Elements at this layer will be in view.
155  */
156 Dwt.Z_VIEW = 300;
157 
158 /**
159  * Popup menu layer. Used by the menu components.
160  */
161 Dwt.Z_MENU = 500;
162 
163 /**
164  * Veil layer. The veil appears just behind modal dialogs render other components
165  * unable to receive mouse input.
166  */
167 Dwt.Z_VEIL = 600;
168 
169 /**
170  * Dialog layer. Dialogs are positioned at this layer.
171  */
172 Dwt.Z_DIALOG = 700;
173 
174 /**
175  * Used by menus that are part of a dialog.
176  */
177 Dwt.Z_DIALOG_MENU = 750;
178 
179 /**
180  * Tooltips layer.
181  */
182 Dwt.Z_TOOLTIP = 775;
183 
184 /**
185  * Drag and Drop (DnD) icon layer. DnD icons are positioned at this layer so they
186  * move across the top of other components.
187  */
188 Dwt.Z_DND = 800;		// Drag N Drop icons
189 
190 /**
191  * This layer appears in front of other layers to block all user mouse input.
192  */
193 Dwt.Z_BUSY = 900;
194 
195 /**
196  * The toast layer.
197  */
198 Dwt.Z_TOAST = 950;
199 
200 /**
201  * Used by the splash screens.
202  */
203 Dwt.Z_SPLASH = 1000;
204 
205 
206 /**
207  * Default value. Used when setting such things as size and bounds to indicate a
208  * component should not be set. For example if setting size and not wishing to set
209  * the height.
210  * <pre>
211  * Dwt.setSize(htmlElement, 100, Dwt.DEFAULT)
212  * </pre>
213  * 
214  */
215 Dwt.DEFAULT = -123456789;
216 
217 /**
218  * Used to clear a value.
219  */
220 Dwt.CLEAR = -20000;
221 
222 /**
223  * Offscreen position. Used when setting a elements position.
224  */
225 Dwt.LOC_NOWHERE = -10000;
226 
227 // Drag N Drop action constants. These are bit fields.
228 /**
229  * No drag and drop operation.
230  */
231 Dwt.DND_DROP_NONE = 0;
232 
233 /**
234  * Copy drag and drop operation.
235  */
236 Dwt.DND_DROP_COPY = 1;
237 
238 /**
239  * Move drag and drop operation.
240  */
241 Dwt.DND_DROP_MOVE = 2;
242 
243 /**
244  * Ballpark figure for width of a scrollbar.
245  */
246 Dwt.SCROLLBAR_WIDTH = 22;
247 
248 // Editor formats
249 Dwt.HTML = "text/html";
250 Dwt.TEXT = "text/plain";
251 
252 // Keys used for retrieving data
253 // TODO JSDoc
254 Dwt.KEY_OBJECT = "_object_";
255 Dwt.KEY_ID = "_id_";
256 
257 /**
258  * z-index increment unit. Used by components if they need to bump their z-index.
259  */
260 Dwt._Z_INC = 1;
261 
262 
263 /**
264  * @private
265  */
266 Dwt.__nextId = {};
267 
268 /**
269  * This method is used to generate a unique id to be used for an HTML element's id
270  * attribute.
271  *
272  * @return {string}	the next available element ID
273  */
274 Dwt.getNextId =
275 function(prefix) {
276 	prefix = prefix || "DWT";
277 	if (!Dwt.__nextId[prefix]) {
278 		Dwt.__nextId[prefix] = 1;
279 	}
280 	return prefix + Dwt.__nextId[prefix]++;
281 };
282 
283 /**
284  * This method is used to query an element for its id, generating one if it
285  * isn't set.
286  *
287  * @return {string}	the element ID
288  */
289 Dwt.getId =
290 function(element, prefix) {
291 	return element ? element.id || (element.id = Dwt.getNextId(prefix)) : null;
292 };
293 
294 /**
295  * @deprecated
296  * The association between an element and a control is now via DwtControl.ALL_BY_ID,
297  * where the unique element ID is a key to the control. The association is made when
298  * the control is initialized.
299  * 
300  * This method builds an indirect association between a DOM object and a JavaScript
301  * object. This indirection is important to prevent memory leaks (particularly in IE) by
302  * not directly creating a circular reference between a DOM object
303  *
304  * @param {DOMElement} domElement the DOM element (typically an HTML element)
305  * @param {Object} jsObject the JavaScript object
306  * 
307  * @private
308  */
309 Dwt.associateElementWithObject =
310 function(domElement, jsObject, attrName) {
311 	domElement[attrName||"dwtObj"] = jsObject.__internalId = AjxCore.assignId(jsObject);
312 };
313 
314 /**
315  * @deprecated
316  * The association will be broken by the control when it is disposed.
317  * 
318  * This method breaks the indirect association between a DOM object and a JavaScript
319  * object that was created by the <code>Dwt.associateElementWithObject</code>method
320  *
321  * @param {DOMElement} domElement the DOM element (typically an HTML element)
322  * @param {Object} jsObject the JavaScript object
323  * 
324  * @private
325  */
326 Dwt.disassociateElementFromObject =
327 function(domElement, jsObject, attrName) {
328 	if (domElement){
329 		domElement.removeAttribute(attrName||"dwtObj");
330 	}
331 	if (jsObject.__internalId){
332 		AjxCore.unassignId(jsObject.__internalId);
333 	}
334 };
335 
336 /**
337  * @deprecated		use {@link DwtControl.fromElement}
338  * 
339  * @private
340  */
341 Dwt.getObjectFromElement =
342 function(domElement, attrName) {
343 	return AjxCore.objectWithId(domElement[attrName||"dwtObj"]);
344 };
345 
346 Dwt.getElement =
347 function(el) {
348 	return (typeof(el) == "string") ? document.getElementById(el) : el;
349 };
350 
351 /**
352  * Finds an ancestor element with a non-empty value for the given attr.
353  * 
354  * @param	{DOMElement}	domElement	the starting DOM element
355  * @param	{string}		attrName	the attribute name
356  * 
357  * @return	{DOMElement}	the DOM element
358  */
359 Dwt.findAncestor =
360 function(domElement, attrName) {
361 	var attr = Dwt.getAttr(domElement, attrName);
362 	while (domElement && (attr == null || attr == "")) {
363 		domElement = domElement.parentNode;
364 		attr = Dwt.getAttr(domElement, attrName);
365 	}
366 	return domElement;
367 };
368 
369 /**
370  * Returns true if el1 is an ancestor (in the parent chain) of el2, or if
371  * el1 and el2 are the same element.
372  *
373  * @param {DOMElement}	el1
374  * @param {DOMElement}	el2
375  */
376 Dwt.isAncestor =
377 function(el1, el2) {
378 
379 	if (el1 == el2) {
380 		return true;
381 	}
382 
383 	var el = el2;
384 	while (el) {
385 		el = el.parentNode;
386 		if (el == el1) {
387 			return true;
388 		}
389 	}
390 	return false;
391 };
392 
393 Dwt.setHandler =
394 function(htmlElement, event, func) {
395 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
396 	if (event == DwtEvent.ONMOUSEWHEEL && AjxEnv.isGeckoBased) {
397 		Dwt.clearHandler(htmlElement, event);
398 	}
399 	htmlElement[event] = func;
400 	if (event == DwtEvent.ONMOUSEWHEEL && AjxEnv.isGeckoBased) {
401 		htmlElement.addEventListener("DOMMouseScroll", func, true);
402 	}
403 };
404 
405 Dwt.clearHandler =
406 function(htmlElement, event) {
407 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
408 	if (event == DwtEvent.ONMOUSEWHEEL && AjxEnv.isGeckoBased) {
409 		if (htmlElement[event]) {
410 			var func = htmlElement[event];
411 			htmlElement.removeEventListener("DOMMouseScroll", func, true);
412 		}
413 	}
414 	htmlElement[event] = null;
415 };
416 
417 Dwt.getBackgroundRepeat =
418 function(htmlElement) {
419 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
420 	return DwtCssStyle.getProperty(htmlElement, "background-repeat");
421 };
422 
423 Dwt.setBackgroundRepeat =
424 function(htmlElement, style) {
425 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
426 	htmlElement.style.backgroundRepeat = style;
427 };
428 
429 /**
430  * Gets the bounds of an HTML element.
431  *
432  * @param {HTMLElement} htmlElement		the HTML element
433  *
434  * @return {DwtRectangle}	the elements bounds
435  *
436  * @see #setBounds
437  * @see #getInsetBounds
438  * @see #getLocation
439  * @see #getSize
440  */
441 Dwt.getBounds =
442 function(htmlElement, rect) {
443 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return null; }
444 	var tmpPt = DwtPoint.tmp;
445 
446 	Dwt.getLocation(htmlElement, tmpPt);
447 	var locX = tmpPt.x;
448 	var locY = tmpPt.y;
449 
450 	Dwt.getSize(htmlElement, tmpPt);
451 
452 	if (!rect) {
453 		return new DwtRectangle(locX, locY, tmpPt.x, tmpPt.y);
454 	} else {
455 		rect.set(locX, locY, tmpPt.x, tmpPt.y);
456 		return rect;
457 	}
458 };
459 
460 /**
461  * Sets the bounds of an HTML element. The position type of the element must
462  * be absolute or else an exception is thrown. To omit setting a value set the
463  * actual parameter value to <i>Dwt.DEFAULT</i>
464  *
465  * @param {HTMLElement} htmlElement absolutely positioned HTML element
466  * @param {number|string} x the x coordinate of the element (for example: 10, "10px", {@link Dwt.DEFAULT})
467  * @param {number|string} y the y coordinate of the element (for example: 10, "10px", {@link Dwt.DEFAULT})
468  * @param {number} width the width of the element (for example: 100, "100px", "75%", {@link Dwt.DEFAULT})
469  * @param {number} height the height of the element  (for example: 100, "100px", "75%", {@link Dwt.DEFAULT})
470  *
471  * @throws DwtException
472  *
473  * @see #getBounds
474  * @see #setLocation
475  * @see #setSize
476  */
477 Dwt.setBounds =
478 function(htmlElement, x, y, width, height) {
479 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
480 	Dwt.setLocation(htmlElement, x, y);
481 	Dwt.setSize(htmlElement, width, height);
482 };
483 
484 /**
485  * Gets the element cursor for a given HTML element.
486  *
487  * @param {HTMLElement} htmlElement		the HTML element
488  *
489  * @return {string}	the html elements cursor
490  *
491  * @see #setCursor
492  */
493 Dwt.getCursor =
494 function(htmlElement) {
495 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return ""; }
496 	return DwtCssStyle.getProperty(htmlElement, "cursor");
497 };
498 
499 /**
500  * Sets an HTML element cursor.
501  *
502  * @param {HTMLElement} htmlElement the element for which to set the cursor
503  * @param {string} cursorName name of the new cursor
504  *
505  * @see #setCursor
506  */
507 Dwt.setCursor =
508 function(htmlElement, cursorName) {
509 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
510 	htmlElement.style.cursor = cursorName;
511 };
512 
513 /**
514  * Gets the location of an HTML element.
515  *
516  * @param {HTMLElement} htmlElement		the HTML element
517  *
518  * @return {DwtPoint}		the location of the HTML element
519  *
520  * @see #setLocation
521  * @see #getBounds
522  * @see #getSize
523  */
524 Dwt.getLocation =
525 function(htmlElement, point) {
526 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return null; }
527 	point = point || new DwtPoint(0, 0);
528 
529 	if (htmlElement.style.position == Dwt.ABSOLUTE_STYLE) {
530 		// parseInt will return NaN if "top" or "left" is "auto" or not set.
531 		// TODO: We should test for that and just go to toWindow in that case.
532 		point.set(parseInt(DwtCssStyle.getProperty(htmlElement, "left")),
533 				parseInt(DwtCssStyle.getProperty(htmlElement, "top")));
534 		return point;
535 	}
536 
537 	return Dwt.toWindow(htmlElement, 0, 0, null, null, point);
538 };
539 
540 /**
541  * Sets the location of an HTML element. The position type of the element must
542  * be absolute or else an exception is thrown. To only set one of the coordinates,
543  * pass in a value of {@link Dwt.DEFAULT} for the coordinate for which the value is
544  * not to be set
545  *
546  * @param {HTMLElement} htmlElement the absolutely positioned HTML element
547  * @param {number|string} x the x coordinate of the element (for example: 10, "10px", {@link Dwt.DEFAULT})
548  * @param {number|string} y the y coordinate of the element (for example: 10, "10px", {@link Dwt.DEFAULT})
549  *
550  * @throws DwtException
551  *
552  * @see #getLocation
553  * @see #setBounds
554  * @see #setSize
555  */
556 Dwt.setLocation =
557 function(htmlElement, x, y) {
558 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
559 	var position = DwtCssStyle.getProperty(htmlElement, 'position');
560 	if (position != Dwt.ABSOLUTE_STYLE && position != Dwt.RELATIVE_STYLE && position != Dwt.FIXED_STYLE) {
561 		DBG.println(AjxDebug.DBG1, "Cannot position static widget " + htmlElement.className);
562 		throw new DwtException("Static widgets may not be positioned", DwtException.INVALID_OP, "Dwt.setLocation");
563 	}
564 	if (x = Dwt.__checkPxVal(x)) {
565 		htmlElement.style.left = x;
566 	}
567 	if (y = Dwt.__checkPxVal(y)) {
568 		htmlElement.style.top = y;
569 	}
570 };
571 
572 Dwt.getPosition =
573 function(htmlElement) {
574 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
575 	return htmlElement.style.position;
576 };
577 
578 Dwt.setPosition =
579 function(htmlElement, posStyle) {
580 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
581 	htmlElement.style.position = posStyle;
582 };
583 
584 /**
585  * Returns <code>htmlElement</code>'s scroll style. The scroll style determines the element's
586  * behaviour when content overflows its boundaries. Possible values are:
587  * <ul>
588  * <li><i>Dwt.CLIP</i> - Clip on overflow</li>
589  * <li><i>Dwt.VISIBLE</i> - Allow overflow to be visible</li>
590  * <li><i>Dwt.SCROLL</i> - Automatically create scrollbars if content overflows</li>
591  * <li><i>Dwt.FIXED_SCROLL</i> - Always have scrollbars whether content overflows or not</li>
592  * </ul>
593  *
594  * @param {HTMLElement} htmlElement HTML element
595  *
596  * @return {Dwt.CLIP|Dwt.VISIBLE|Dwt.SCROLL|Dwt.FIXED_SCROLL}	the elements scroll style
597  */
598 Dwt.getScrollStyle =
599 function(htmlElement) {
600 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return ""; }
601 	var overflow =  DwtCssStyle.getProperty(htmlElement, "overflow");
602 
603 	if (overflow == "hidden")		{ return Dwt.CLIP; }
604 	if (overflow =="auto")			{ return Dwt.SCROLL; }
605 	if (overflow =="scroll")		{ return Dwt.FIXED_SCROLL; }
606 
607 	if (overflow == '') {
608 		var overflowX = DwtCssStyle.getProperty(htmlElement, "overflowX");
609 		var overflowY = DwtCssStyle.getProperty(htmlElement, "overflowY");
610 
611 		if (overflowX == 'scroll')	{ return Dwt.SCROLL_X; }
612 		if (overflowY == 'scroll')	{ return Dwt.SCROLL_Y; }
613 	}
614 	return Dwt.VISIBLE;
615 };
616 
617 /**
618  * Sets the <code>htmlElement</code>'s scroll style. The scroll style determines the elements's
619  * behaviour when content overflows its div's boundaries. Possible values are:
620  * <ul>
621  * <li><i>Dwt.CLIP</i> - Clip on overflow</li>
622  * <li><i>Dwt.VISIBLE</i> - Allow overflow to be visible</li>
623  * <li><i>Dwt.SCROLL</i> - Automatically create scrollbars if content overflows</li>
624  * <li><i>Dwt.FIXED_SCROLL</i> - Always have scrollbars whether content overflows or not</li>
625  * </ul>
626  *
627  * @param {HTMLElement} htmlElement HTML element
628  * @param {Dwt.CLIP|Dwt.VISIBLE|Dwt.SCROLL|Dwt.FIXED_SCROLL}	scrollStyle		the elements scroll style
629  */
630 Dwt.setScrollStyle =
631 function(htmlElement, scrollStyle) {
632 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
633 	if (scrollStyle == Dwt.CLIP)
634 		htmlElement.style.overflow = "hidden";
635 	else if (scrollStyle == Dwt.SCROLL)
636 		htmlElement.style.overflow = "auto";
637 	else if (scrollStyle == Dwt.FIXED_SCROLL)
638 		htmlElement.style.overflow = "scroll";
639 	else if (scrollStyle == Dwt.SCROLL_Y) {
640 		htmlElement.style.overflowX = "hidden";
641 		htmlElement.style.overflowY = "auto";
642 	} else if (scrollStyle == Dwt.SCROLL_X) {
643 		htmlElement.style.overflowY = "hidden";
644 		htmlElement.style.overflowX = "auto";
645 	} else {
646 		htmlElement.style.overflow = "visible";
647 	}
648 };
649 
650 
651 /**
652  * Gets the size of an HTML element. Normally, this yields the
653  * calculated size of the element. However, if 'getFromStyle' is
654  * true, the style is obtained directly from the CSS style.
655  *
656  * @param {HTMLElement} htmlElement		the HTML element
657  * @param {DwtPoint} point		if given, reuse this point
658  * @param {Boolean} getFromStyle		whether to use the calculated size
659  *
660  * @return {DwtPoint}	the elements size, margins included
661  *
662  * @see #getBounds
663  * @see #setBounds
664  * @see #getInsetBounds
665  * @see #getLocation
666  * @see #getOuterSize
667  */
668 Dwt.getSize =
669 function(htmlElement, point, getFromStyle) {
670     // Note: in FireFox, offsetHeight includes border and clientHeight does not;
671     // may want to look at clientHeight for FF
672 
673 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
674 	var p;
675 	if (!point) {
676 		p = new DwtPoint(0, 0);
677 	} else {
678 		p = point;
679 		p.set(0, 0);
680 	}
681 
682 	if (!htmlElement) { return p; }
683 
684 	if (getFromStyle) {
685 		if (htmlElement.style.width) { //assumption - the caller only cares about the dimension that is set via the style. So ok to keep 0 if it's not set. for simplicity.
686 			p.x = parseInt(htmlElement.style.width);
687 		}
688 		if (htmlElement.style.height) {
689 			p.y = parseInt(htmlElement.style.height);
690 		}
691 
692 		return p;
693 	}
694 
695 	p.x = htmlElement.offsetWidth;
696 	if (p.x != null) {
697 		p.y = htmlElement.offsetHeight;
698 	} else if (htmlElement.clip && htmlElement.clip.width != null) {
699 		p.x = parseInt(htmlElement.clip.width);
700 		p.y = parseInt(htmlElement.clip.height);
701 	} else if (htmlElement.style && htmlElement.style.pixelWidth != null) {
702 		p.x = parseInt(htmlElement.style.pixelWidth);
703 		p.y = parseInt(htmlElement.style.pixelHeight);
704 	}
705 
706 	return p;
707 };
708 
709 
710 /**
711  * Gets the outer size -- that is, the size including margins, padding, and borders -- of an
712  * HTML element.
713  *
714  * @param {HTMLElement} htmlElement		the HTML element
715  *
716  * @return {DwtPoint}	the elements size, margins included
717  *
718  * @see #getSize
719  * @see #getBounds
720  * @see #setBounds
721  * @see #getInsetBounds
722  * @see #getLocation
723  */
724 Dwt.getOuterSize =
725 function(htmlElement, point) {
726     var p = Dwt.getSize(htmlElement, point);
727 
728     if (p && Dwt.getVisible(htmlElement)) {
729         var margins = Dwt.getMargins(htmlElement);
730 		var insets = Dwt.getInsets(htmlElement);
731         p.x += margins.left + margins.right + insets.left + insets.right;
732         p.y += margins.top + margins.bottom + insets.top + insets.bottom;
733     }
734 
735     return p;
736 };
737 
738 Dwt.setSize =
739 function(htmlElement, width, height) {
740 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
741 	if (!htmlElement.style) { return; }
742 
743 	if (width == Dwt.CLEAR) {
744 		htmlElement.style.width = null;
745 	} else if (width = Dwt.__checkPxVal(width, true)) {
746 		htmlElement.style.width = width;
747 	}
748 
749 	if (height == Dwt.CLEAR) {
750 		htmlElement.style.height = null;
751 	} else if (height = Dwt.__checkPxVal(height, true)) {
752 		htmlElement.style.height = height;
753 	}
754 };
755 
756 /**
757  * Measure the extent in pixels of a section of html. This is not the worlds cheapest
758  * method to invoke so do so judiciously
759  *
760  * @param {string} html 	the html content for which that extents are to be calculated
761  *
762  * @return {DwtPoint}	the extent of the content
763  */
764 Dwt.getHtmlExtent =
765 function(html) {
766 	var div = AjxStringUtil.calcDIV();
767 	div.innerHTML = html;
768 	return Dwt.getSize(div);
769 };
770 
771 Dwt.toDocumentFragment =
772 function(html, id) {
773 	var div = AjxStringUtil.calcDIV();
774 	div.innerHTML = html;
775 
776 	var fragment = document.createDocumentFragment();
777 	var container = id && document.getElementById(id);
778 	if (container) {
779 		fragment.appendChild(container);
780 	}
781 	else {
782 		for (var child = div.firstChild; child; child = div.firstChild) {
783 			fragment.appendChild(child);
784 		}
785 	}
786 	return fragment;
787 };
788 
789 Dwt.getAttr =
790 function(htmlEl, attr, recursive) {
791 	// test for tagName so we dont try to eval non-html elements (i.e. document)
792 	if (!recursive) {
793 		return htmlEl && htmlEl.tagName
794 			? (htmlEl.getAttribute(attr) || htmlEl[attr])
795 			: null;
796 	} else {
797 		while (htmlEl) {
798 			if (Dwt.getAttr(htmlEl, attr) != null) {
799 				return htmlEl;
800 			}
801 			htmlEl = htmlEl.parentNode;
802 		}
803 		return null;
804 	}
805 };
806 
807 Dwt.getVisible =
808 function(htmlElement) {
809 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
810 	var disp = DwtCssStyle.getProperty(htmlElement, "display");
811 	return (disp != Dwt.DISPLAY_NONE);
812 };
813 
814 Dwt.setVisible =
815 function(htmlElement, visible) {
816 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
817 	if (visible) {
818 		if (htmlElement.nodeName.match(/tr/i)) {
819 			htmlElement.style.display = Dwt.DISPLAY_TABLE_ROW;
820 		}
821 		else if (htmlElement.nodeName.match(/td|th/i)) {
822 			htmlElement.style.display = Dwt.DISPLAY_TABLE_CELL;
823 		}
824 		else {
825 			htmlElement.style.display = htmlElement.getAttribute("x-display") ||
826 										Dwt.DISPLAY_BLOCK;
827 		}
828 	} else {
829 		var display = DwtCssStyle.getComputedStyleObject(htmlElement).display;
830 		if (display != "none") {
831 			htmlElement.setAttribute("x-display", display);
832 		}
833 		htmlElement.style.display = Dwt.DISPLAY_NONE;
834 	}
835 };
836 
837 Dwt.getVisibility =
838 function(htmlElement) {
839 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
840 	var vis = DwtCssStyle.getProperty(htmlElement, "visibility");
841 	return (vis == "visible");
842 };
843 
844 Dwt.setVisibility =
845 function(htmlElement, visible) {
846 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
847 	htmlElement.style.visibility = visible ? "visible" : "hidden";
848 };
849 
850 Dwt.__MSIE_OPACITY_RE = /alpha\(opacity=(\d+)\)/;
851 
852 Dwt.getOpacity =
853 function(htmlElement) {
854 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
855 	if (AjxEnv.isIE && !AjxEnv.isIE9up) {
856 		var filter = Dwt.getIEFilter(htmlElement, "alpha");
857 		var m = Dwt.__MSIE_OPACITY_RE.exec(filter) || [ filter, "100" ];
858 		return Number(m[1]);
859 	}
860 	return Number(htmlElement.style.opacity || 1) * 100;
861 };
862 
863 Dwt.setOpacity =
864 function(htmlElement, opacity) {
865 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
866 	if (AjxEnv.isIE && !AjxEnv.isIE9up) {
867 		var filterVal = opacity < 100 ? "alpha(opacity="+opacity+")" : "";
868 		Dwt.alterIEFilter(htmlElement, "alpha", filterVal);
869 	} else {
870 		htmlElement.style.opacity = opacity/100;
871 	}
872 };
873 
874 
875 /**
876  * Get the z-index of an element.
877  *
878  * @param {boolean} getFromStyle    get the value from the style attribute of
879  *                                  this element, or a parent
880  *
881  * @return	{number}	the z-index value
882  */
883 Dwt.getZIndex =
884 function(htmlElement, getFromStyle) {
885 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
886 
887 	if (getFromStyle) {
888 		while (htmlElement.style.zIndex === "" && htmlElement.parentNode) {
889 			htmlElement = htmlElement.parentNode;
890 		}
891 
892 		return htmlElement.style.zIndex;
893 	}
894 
895 	return DwtCssStyle.getProperty(htmlElement, "z-index");
896 };
897 
898 Dwt.setZIndex =
899 function(htmlElement, idx) {
900 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
901 	htmlElement.style.zIndex = idx;
902 	if (idx < Dwt.Z_VIEW) {
903 		htmlElement.setAttribute('aria-hidden', true);
904 	} else {
905 		htmlElement.removeAttribute('aria-hidden');
906 	}
907 };
908 
909 Dwt.getDisplay =
910 function(htmlElement) {
911 	DwtCssStyle.getProperty(htmlElement, "display");
912 };
913 
914 Dwt.setDisplay =
915 function(htmlElement, value) {
916 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
917 	htmlElement.style.display = value;
918 };
919 
920 /**
921  * Gets the window size of the browser.
922  * 
923  * @param	{DwtPoint}		point		the point to hold the windows x/y size
924  * @return	{DwtPoint}		the point holding the window x/y size
925  */
926 Dwt.getWindowSize =
927 function(point) {
928 	var p = (!point) ? new DwtPoint(0, 0) : point;
929 	if (window.innerWidth) {
930 		p.x = window.innerWidth;
931 		p.y = window.innerHeight;
932 	} else if (AjxEnv.isIE6CSS) {
933 		p.x = document.body.parentElement.clientWidth;
934 		p.y = document.body.parentElement.clientHeight;
935 	} else if (document.body && document.body.clientWidth) {
936 		p.x = document.body.clientWidth;
937 		p.y = document.body.clientHeight;
938 	}
939 	return p;
940 };
941 
942 Dwt.toWindow =
943 function(htmlElement, x, y, containerElement, dontIncScrollTop, point) {
944 	var p;
945 	if (!point) {
946 		p = new DwtPoint(x, y);
947 	} else {
948 		p = point;
949 		p.set(x, y);
950 	}
951 
952 	htmlElement = Dwt.getElement(htmlElement);
953 	var offsetParent = htmlElement;
954 	while (offsetParent && offsetParent != containerElement) {
955 		p.x += offsetParent.offsetLeft - offsetParent.scrollLeft;
956 		p.y += offsetParent.offsetTop;
957 		if (!dontIncScrollTop) {
958 			var scrollTop = AjxEnv.isOpera ? offsetParent.pageYOffset : offsetParent.scrollTop;
959 			if (scrollTop) {
960 				p.y -= scrollTop;
961 			}
962 			var parentNode = offsetParent.parentNode;
963 			while (parentNode != offsetParent.offsetParent && parentNode != containerElement) {
964 				scrollTop = AjxEnv.isOpera ? parentNode.pageYOffset : parentNode.scrollTop;
965 				if (scrollTop) {
966 					p.y -= scrollTop;
967 				}
968 				parentNode = parentNode.parentNode;
969 			}
970 		}
971 		offsetParent = offsetParent.offsetParent;
972 	}
973 	return p;
974 };
975 
976 Dwt.getInsets = function(htmlElement) {
977 	// return an object with the insets (border + padding size) for each side of the element, eg:
978 	//		{ left: 3, top:0, right:3, bottom:0 }
979 	// NOTE: assumes values from computedStyle are returned in pixels!!!
980 
981 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
982 	var style = DwtCssStyle.getComputedStyleObject(htmlElement);
983 
984 	var bl = parseInt(style.borderLeftWidth) 	|| 0;
985 	var bt = parseInt(style.borderTopWidth) 	|| 0;
986 	var br = parseInt(style.borderRightWidth)	|| 0;
987 	var bb = parseInt(style.borderBottomWidth)	|| 0;
988 
989 	var pl = parseInt(style.paddingLeft) 	|| 0;
990 	var pt = parseInt(style.paddingTop) 	|| 0;
991 	var pr = parseInt(style.paddingRight)	|| 0;
992 	var pb = parseInt(style.paddingBottom)	|| 0;
993 
994 	return {
995 			left 	: bl + pl,
996 			top  	: bt + pt,
997 			right 	: br + pr,
998 			bottom	: bb + pb
999 		};
1000 };
1001 
1002 Dwt.insetBounds = function(bounds, insets) {
1003 
1004 	// given a 'bounds' object [from Dwt.getBounds()] 
1005 	//	and an 'insets' object [from Dwt.getInsets()]
1006 	//	munge the bounds so it takes the insets into account.
1007 	// Useful to get the inner dimensions of an element.
1008 	if (!bounds) {
1009         return null;
1010     }
1011 
1012 	bounds.x += insets.left;
1013 	bounds.y += insets.top;
1014 	bounds.width  -= insets.left + insets.right;
1015 	bounds.height -= insets.top + insets.bottom;
1016 
1017 	return bounds;
1018 };
1019 
1020 /**
1021  * Gets the bounds of an HTML element, excluding borders and paddings.
1022  *
1023  * @param {HTMLElement} htmlElement		the HTML element
1024  *
1025  * @return {DwtRectangle}	the elements bounds
1026  *
1027  * @see #setBounds
1028  * @see #getInsetBounds
1029  * @see #getLocation
1030  * @see #getSize
1031  */
1032 Dwt.getInsetBounds = function(htmlElement) {
1033 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
1034 
1035 	var bounds = Dwt.getBounds(htmlElement);
1036 	var insets = Dwt.getInsets(htmlElement);
1037 
1038 	return Dwt.insetBounds(bounds, insets);
1039 };
1040 
1041 Dwt.getMargins = function(htmlElement) {
1042 	// return an object with the margins for each side of the element, eg:
1043 	//		{ left: 3, top:0, right:3, bottom:0 }
1044 	// NOTE: assumes values from computedStyle are returned in pixels!!!
1045 
1046 	if (!(htmlElement = Dwt.getElement(htmlElement))) { return; }
1047 	var style = DwtCssStyle.getComputedStyleObject(htmlElement);
1048 
1049 	return {
1050 		left 	: parseInt(style.marginLeft) 	|| 0,
1051 		top  	: parseInt(style.marginTop) 	|| 0,
1052 		right 	: parseInt(style.marginRight) 	|| 0,
1053 		bottom	: parseInt(style.marginBottom)	|| 0
1054 	};
1055 };
1056 
1057 /**
1058  * Get ancestor elements of the given node, up to and including the given
1059  * parent node. If no parent is given, assume the root document node. If the
1060  * parent node is not an ancestor of the child, return <code>null</code>.
1061  *
1062  * @param {HTMLElement} childNode		the child HTML element
1063  * @param {HTMLElement} parentNode		the parent HTML element
1064  * @param {Boolean} 	includeChild	if true, include the child itself
1065  *
1066  * @return {Array}						a list of HTML elements
1067  */
1068 Dwt.getAncestors =
1069 function(childNode, parentNode, includeChild) {
1070 	var ancestors = [];
1071 
1072 	// a reasonable default
1073 	if (!parentNode) {
1074 		parentNode = document.documentElement;
1075 	}
1076 
1077 	if (includeChild) {
1078 		ancestors.push(childNode);
1079 	}
1080 
1081 	while (childNode && childNode != parentNode) {
1082 		ancestors.push(childNode.parentNode);
1083 		childNode = childNode.parentNode;
1084 	}
1085 
1086 	// check if the parent was an ancestor
1087 	if (ancestors[ancestors.length - 1] != parentNode) {
1088 		return null;
1089 	}
1090 
1091 	return ancestors;
1092 };
1093 
1094 Dwt.setStatus =
1095 function(text) {
1096 	window.status = text;
1097 };
1098 
1099 Dwt.getTitle =
1100 function() {
1101 	return window.document.title;
1102 };
1103 
1104 Dwt.setTitle =
1105 function(text) {
1106 	window.document.title = text;
1107 };
1108 
1109 Dwt.getIframeDoc =
1110 function(iframeObj) {
1111 	if (iframeObj) {
1112 		return AjxEnv.isIE
1113 			? iframeObj.contentWindow.document
1114 			: iframeObj.contentDocument;
1115 	}
1116 	return null;
1117 };
1118 
1119 Dwt.getIframeWindow =
1120 function(iframeObj) {
1121 	return iframeObj.contentWindow;
1122 };
1123 
1124 /**
1125  * Creates and returns an element from a string of HTML.
1126  *
1127  * @param {string} html 	the HTML text
1128  * @param {boolean} isRow 	if <code>true</code>, if the element is a <code><tr></code>
1129  *
1130  * @return {HTMLElement}	an HTMLElement with the <code>html</code> as its content. if <code>isRow</code>
1131  * 		is <code>true</code>, then the element will be a table
1132  */
1133 Dwt.parseHtmlFragment =
1134 function(html, isRow) {
1135 	if (!Dwt._div) {
1136 		Dwt._div = document.createElement('div');
1137 	}
1138 	// TR element needs to have surrounding table
1139 	if (isRow) {
1140 		html = "<table style='table-layout:fixed'>" + html + "</table>";
1141 	}
1142 	Dwt._div.innerHTML = html;
1143 
1144 	if (isRow) {
1145 		var fragment = document.createDocumentFragment();
1146 		var rows = Dwt._div.firstChild.rows;
1147 		for (var i = rows.length - 1; i >= 0; i--) {
1148 			// NOTE: We always grab the first row because once we append it
1149 			//       to the fragment, it will be removed from the table.
1150 			fragment.appendChild(rows[0]);
1151 		}
1152 		return fragment.childNodes.length > 1 ? fragment : fragment.firstChild;
1153 	}
1154 	return Dwt._div.firstChild;
1155 };
1156 
1157 Dwt.contains =
1158 function(parentEl, childEl) {
1159 	var isContained = false;
1160 	if (parentEl.compareDocumentPosition) {
1161 		var relPos = parentEl.compareDocumentPosition(childEl);
1162 		if ((relPos == (document.DOCUMENT_POSITION_CONTAINED_BY | document.DOCUMENT_POSITION_FOLLOWING))) {
1163 			isContained = true;
1164 		}
1165 	} else if (parentEl.contains) {
1166 		isContained = parentEl.contains(childEl);
1167 	}
1168 	return isContained;
1169 };
1170 
1171 Dwt.removeChildren =
1172 function(htmlEl) {
1173 	while (htmlEl.hasChildNodes()) {
1174 		htmlEl.removeChild(htmlEl.firstChild);
1175 	}
1176 };
1177 
1178 /**
1179  * Opera always returns zero for cellIndex property of TD element :(
1180  *
1181  * @param cell		TD object we want cell index for
1182  * 
1183  * @private
1184  */
1185 Dwt.getCellIndex =
1186 function(cell) {
1187 	if (AjxEnv.isOpera) {
1188 		if (cell.tagName && cell.tagName.toLowerCase() == "td") {
1189 			// get the cells collection from the TD's parent TR
1190 			var cells = cell.parentNode.cells;
1191 			var len = cells.length;
1192 			for (var i = 0; i < len; i++) {
1193 				if (cells[i] == cell)
1194 					return i;
1195 			}
1196 		}
1197 	} else {
1198 		return cell.cellIndex;
1199 	}
1200 	return -1;
1201 };
1202 
1203 /**
1204  * Remove the <code>del</code> class name from the element's CSS class names and
1205  * optionally add <code>add</code> class name if given provided
1206  *
1207  * @param {HTMLElement} el HTML Element to which to add/delete class names
1208  * @param {string} [del] the class name to delete
1209  * @param {string} [add] the class name to add
1210  */
1211 Dwt.delClass =
1212 function(el, del, add) {
1213 
1214 	if (el == null) { return }
1215 	if (!del && !add) { return; }
1216 
1217 	if (typeof del == "string" && del.length) {
1218 		del = Dwt._DELCLASS_CACHE[del] || (Dwt._DELCLASS_CACHE[del] = new RegExp("\\b" + del + "\\b", "ig"));
1219 	}
1220 	var className = el.className || "";
1221 	className = className.replace(del, " ");
1222 	className = AjxStringUtil.trim(className);
1223 	el.className = add ? className + " " + add : className;
1224 };
1225 
1226 // cache the regexps here to avoid compiling the same regexp multiple times
1227 Dwt._DELCLASS_CACHE = {};
1228 
1229 /**
1230  * Adds the given class name to the element's CSS class names
1231  *
1232  * @param {HTMLElement} el the HTML Element to which to add the class name
1233  * @param {string} c the class name
1234  *
1235  * @see #delClass
1236  */
1237 Dwt.addClass =
1238 function(el, c) {
1239 	Dwt.delClass(el, c, c);
1240 };
1241 
1242 /**
1243  * Conditionally add or remove a class name from an element
1244  *
1245  * @param {HTMLElement} el the target element
1246  * @param {boolean} condition 	the condition to check
1247  * @param {string} a the class name when condition is <code>true</code>
1248  * @param {string} b the class name when condition is <code>false</code>
1249  */
1250 Dwt.condClass =
1251 function(el, condition, a, b) {
1252 	if (!!condition) {
1253 		if (b) {
1254 			Dwt.delClass(el, b);
1255 		}
1256 		Dwt.addClass(el, a);
1257 	} else {
1258 		Dwt.delClass(el, a);
1259 		if (b) {
1260 			Dwt.addClass(el, b);
1261 		}
1262 	}
1263 };
1264 
1265 /** Returns true if the specified element has the given class. */
1266 Dwt.hasClass = function(el, className) {
1267     if (!el || !className) return false;
1268     return el.className.match(new RegExp("\\b"+className+"\\b"));
1269 };
1270 
1271 /**
1272  * Sets the selection range.
1273  *
1274  * @param {input|iframe} input input for which to find the selection start point. This
1275  * 		may be a text input field or an iframe in design mode
1276  * @param {number} start 	the starting position
1277  * @param {number} end 	the ending position
1278  *
1279  * @see #getSelectionStart
1280  * @see #getSelectionEnd
1281  * @see #setSelectionText
1282  * @see #moveCursorToEnd
1283  */
1284 Dwt.setSelectionRange =
1285 function(input, start, end) {
1286 	if (input.setSelectionRange) {
1287         input.focus();
1288 		input.setSelectionRange(start, end);
1289 	} else if (input.createTextRange) {
1290 		var range = input.createTextRange();
1291 		range.collapse(true);
1292 		range.moveStart("character", start);
1293 		range.moveEnd("character", end - start);
1294 		range.select();
1295 	} else if (input.select) {
1296 		// FIXME: find solutions for other browsers
1297 		input.select();
1298 	}
1299 };
1300 
1301 /**
1302  * Retrieves the start of the selection.  For a collapsed range, this is
1303  * equivalent to {@link #getSelectionEnd}.
1304  *
1305  * @param {input|iframe} input input for which to find the selection start point. This
1306  * 		may be a text input field or an iframe in design mode
1307  *
1308  * @return {number}	starting position of the selection
1309  *
1310  * @see #getSelectionEnd
1311  * @see #setSelectionText
1312  * @see #setSelectionRange
1313  * @see #moveCursorToEnd
1314  */
1315 Dwt.getSelectionStart =
1316 function(input) {
1317 	if (AjxUtil.isSpecified(input.selectionStart)) {
1318 		return input.selectionStart;
1319 	} else if (document.selection) {
1320 		var range = document.selection.createRange();
1321 		var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
1322 		if (!isCollapsed)
1323 			range.collapse(true);
1324 		var b = range.getBookmark();
1325 		var offset = input.createTextRange().getBookmark().charCodeAt(2);
1326 		return Math.max(b.charCodeAt(2) - offset, 0);
1327 	}
1328 
1329 	// FIXME: find solutions for other browsers
1330 	return input.value.length;
1331 };
1332 
1333 /**
1334  * Retrieves the end of the selection.
1335  *
1336  * @param {input|iframe} input 	the input for which to find the selection end point. This
1337  * 		may be a text input field or an iframe in design mode
1338  *
1339  * @return {number}	the starting position of the selection
1340  *
1341  * @see #getSelectionStart
1342  * @see #setSelectionText
1343  * @see #setSelectionRange
1344  * @see #moveCursorToEnd
1345  */
1346 Dwt.getSelectionEnd =
1347 function(input) {
1348 	if (AjxUtil.isSpecified(input.selectionEnd)) {
1349 		return input.selectionEnd;
1350 	} else if (document.selection) {
1351 		var range = document.selection.createRange();
1352 		var isCollapsed = range.compareEndPoints("StartToEnd", range) == 0;
1353 		if (!isCollapsed)
1354 			range.collapse(false);
1355 		var b = range.getBookmark();
1356 		var offset = input.createTextRange().getBookmark().charCodeAt(2);
1357 		return Math.max(b.charCodeAt(2) - offset, 0);
1358 	}
1359 
1360 	// FIXME: find solutions for other browsers
1361 	return input.value.length;
1362 };
1363 
1364 /**
1365  * Sets the selection text
1366  *
1367  * @param {input|iframe} input	the input for which to set the selection text. This
1368  * 		may be a text input field or an iframe in design mode
1369  * @param {string} text 	the text to set as the selection
1370  *
1371  * @see #getSelectionStart
1372  * @see #getSelectionEnd
1373  * @see #setSelectionRange
1374  * @see #moveCursorToEnd
1375  */
1376 Dwt.setSelectionText =
1377 function(input, text) {
1378 	var start = Dwt.getSelectionStart(input);
1379 	var end = Dwt.getSelectionEnd(input);
1380 	var str = input.value;
1381 	var val = [
1382 		str.substr(0, start),
1383 		text,
1384 		str.substr(end)
1385 	].join("");
1386 
1387 	if (typeof input.setValue == "function") {
1388 		input.setValue(val);
1389 	} else {
1390 		input.value = val;
1391 	}
1392 	Dwt.setSelectionRange(input, start, start + text.length);
1393 };
1394 
1395 /**
1396  * Move cursor to the end of an input.
1397  *
1398  * @param {input} input	    text input
1399  *
1400  * @see #getSelectionStart
1401  * @see #getSelectionEnd
1402  * @see #setSelectionText
1403  * @see #setSelectionRange
1404  */
1405 Dwt.moveCursorToEnd =
1406 function(input) {
1407 	Dwt.setSelectionRange(input, input.value.length, input.value.length);
1408 };
1409 
1410 Dwt.instanceOf =
1411 function(objOrClassName, className) {
1412 	if (typeof objOrClassName == "string") {
1413 		return window[objOrClassName] &&
1414 				(objOrClassName == className || window[objOrClassName].prototype instanceof window[className]);
1415 	}
1416 	return (window[className] && objOrClassName instanceof window[className]);
1417 };
1418 
1419 /**
1420  * Normalizes an argument list into a hash with the given argument names.
1421  * If a single hash argument is passed, it is recognized as a params hash
1422  * and returned. Otherwise, the argument list is exploded into a params
1423  * hash with the given param names.
1424  * 
1425  * @param {Object}	args			Array-like structure of arguments
1426  * @param {array}	paramNames		an ordered list of param names
1427  */
1428 Dwt.getParams = function(args, paramNames) {
1429 
1430 	if (!args || args.length === 0 || (args.length === 1 && !args[0])) {
1431 		return {};
1432 	}
1433 
1434 	// Check for arg-list style of passing params. There will almost always
1435 	// be more than one arg, and the first one may be the parent DwtControl.
1436 	// Conversion is not done if there is a single argument that is a simple
1437 	// hash, or a proxy for a simple hash (see AjxUtil.createProxy).
1438 
1439 	if (args.length > 1 || !AjxUtil.isHash(args[0]._object_ || args[0])) {
1440 		var params = {};
1441 		for (var i = 0; i < args.length; i++) {
1442 			params[paramNames[i]] = args[i];
1443 		}
1444 		return params;
1445 	}
1446 	if (args.length === 1) {
1447 		return args[0];
1448 	}
1449 	return {};
1450 };
1451 
1452 //////////////////////////////////////////////////////////////////////////////////
1453 // PRIVATE METHODS
1454 //////////////////////////////////////////////////////////////////////////////////
1455 
1456 Dwt.__REM_RE = /^(-?[0-9]+(?:\.[0-9]*)?)rem$/;
1457 
1458 /**
1459  * @private
1460  */
1461 Dwt.__checkPxVal =
1462 function(val, check) {
1463 	if (val == Dwt.DEFAULT) { return false; }
1464 	if (isNaN(parseInt(val))) { return false; }
1465 
1466 	if (check && val < 0 && val != Dwt.LOC_NOWHERE) {
1467 		DBG.println(AjxDebug.DBG1, "negative pixel value: " + val);
1468 		val = 0;
1469 	}
1470 	if (typeof(val) == "number") {
1471 		val = val + "px";
1472 	}
1473 	if (!AjxEnv.supportsCSS3RemUnits && Dwt.__REM_RE.test(val)) {
1474 		val = DwtCssStyle.asPixelCount(val) + "px";
1475 	}
1476 	return val;
1477 };
1478 
1479 
1480 
1481 
1482 
1483 
1484 /////////////
1485 //	NEW STUFF FROM OWEN
1486 /////////////
1487 Dwt.byId =
1488 function(id, ancestor) {
1489 	if (!ancestor) {
1490 		return (typeof id == "string" ? document.getElementById(id) : id);
1491 	}
1492 
1493 	// Find node with id that descends from ancestor (also works on DOM trees
1494 	// that are not attached to the document object)
1495 	if (ancestor == id || ancestor.id == id) {
1496 		return ancestor;
1497 	}
1498 
1499 	for (var i = 0; i < ancestor.childNodes.length; i++) {
1500 		if (ancestor.childNodes[i].nodeType == 1) {
1501 			var cnode = Dwt.byId(id, ancestor.childNodes[i]);
1502 			if (cnode) { return cnode; }
1503 		}
1504 	}
1505 	return null;
1506 };
1507 
1508 /**
1509  * Get all elements of a certain tag name. Similar to
1510  * document.getElementsByTagName(), but returning an Array instead of
1511  * a NodeList.
1512  *
1513  * @param {String} tagName	the tag name, such as "A"
1514  * @param {HTMLElement} ancestor An optional ancestor element,
1515  *                      defaults to the document
1516  * @return	{Array}
1517  */
1518 Dwt.byTag =
1519 function(tagName, ancestor) {
1520 	if (!ancestor) {
1521 		ancestor = document;
1522 	}
1523 
1524 	return AjxUtil.toArray(ancestor.getElementsByTagName(tagName));
1525 };
1526 
1527 /**
1528  * Get all elements of the given class name. Similar to
1529  * document.getElementsByClassName(), but returning an Array instead
1530  * of a NodeList.
1531  *
1532  * @param {String} className
1533  * @param {HTMLElement} ancestor An optional ancestor element,
1534  *                      defaults to the document
1535  * @return	{Array}
1536  */
1537 Dwt.byClassName =
1538 function(className, ancestor) {
1539 	if (!ancestor) {
1540         ancestor = document;
1541 	}
1542 
1543 	var nodes;
1544 
1545 	if (ancestor.getElementsByClassName) {
1546 		nodes = ancestor.getElementsByClassName(className);
1547 	} else {
1548 		nodes = ancestor.querySelectorAll('.' + className);
1549 	}
1550 
1551 	return AjxUtil.toArray(nodes);
1552 };
1553 
1554 Dwt.show =
1555 function(it) {
1556 	var el = Dwt.byId(it);
1557 	if (el) {
1558 		Dwt.setVisible(el,true);
1559 	}
1560 };
1561 
1562 Dwt.hide =
1563 function(it) {
1564 	var el = Dwt.byId(it);
1565 	if (el) {
1566 		Dwt.setVisible(el,false);
1567 	}
1568 };
1569 
1570 //setText Methods
1571 
1572 Dwt.setText =
1573 function(htmlEl,text){
1574 	htmlEl.appendChild(document.createTextNode(text));
1575 };
1576 
1577 Dwt.populateText =
1578 function(){
1579 	if (arguments.length == 0 ) { return; }
1580 
1581 	var node, index = 0, length = arguments.length;
1582 	while (index < length) {
1583 		node = document.getElementById(arguments[index]);
1584 		if (node) {
1585 			Dwt.setText(node,arguments[index+1]);
1586 		}
1587 		index += 2;
1588 	}
1589 };
1590 
1591 //setHtml Methods
1592 
1593 Dwt.setInnerHtml =
1594 function(htmlEl,html){
1595 	htmlEl.innerHTML = html;
1596 };
1597 
1598 /**
1599  * Sets the favicon.
1600  *
1601  * @param {string} the url to the icon to display
1602  * 
1603  * @private
1604  */
1605 Dwt.setFavIcon =
1606 function(iconURL) {
1607 
1608 	// Look for an existing fav icon to modify.
1609 	var favIcon = null;
1610 	if (Dwt._favIconId) {
1611 		favIcon = document.getElementById(Dwt._favIconId);
1612 	} else {
1613 		var docHead = document.getElementsByTagName("head")[0];
1614 		var links = docHead.getElementsByTagName("link");
1615 		for (var i = 0; i < links.length; i++) {
1616 			var link = links[i];
1617 			if (link.rel.toUpperCase() == "SHORTCUT ICON") {
1618 				if (!link.id) {
1619 					link.id = Dwt._favIconId = Dwt.getNextId();
1620 				}
1621 				favIcon = link;
1622 				break;
1623 			}
1624 		}
1625 	}
1626 	// If available, change the existing favicon.
1627 	// (Need to remove/add to dom in order to force a redraw.)
1628 	if (favIcon) {
1629 		favIcon.href=iconURL;
1630 		favIcon.type = 'image/x-icon';
1631 		var parent = favIcon.parentNode;
1632 		parent.removeChild(favIcon);
1633 		parent.appendChild(favIcon);
1634 	}
1635 	// If no favicon was found in the document, create a new one.
1636 	else {
1637 		var newLink = document.createElement("link");
1638 		newLink.id = Dwt._favIconId = Dwt.getNextId()
1639 		newLink.rel = "SHORTCUT ICON";
1640 		newLink.href = iconURL;
1641 		newLink.type = "image/x-icon";
1642 		docHead = docHead || document.getElementsByTagName("head")[0];
1643 		docHead.appendChild(newLink);
1644 	}
1645 };
1646 
1647 Dwt.enableDesignMode =
1648 function(doc, on) {
1649 	if (!AjxEnv.isIE) {
1650 		doc.designMode = on ? "on" : "off";
1651 	} else {
1652 		var editorBody = doc.body;
1653 		if (!editorBody || editorBody.contentEditable === undefined) {
1654 			doc.designMode = on ? "on" : "off";
1655 		} else {
1656 			editorBody.contentEditable = on ? true : false;
1657 		}
1658 	}
1659 };
1660 
1661 /**
1662  * Hack to work around FF 3.6 change in behavior with regard to mouse down/up in
1663  * scrollbar, which breaks this list view's scrollbar. Return true and tell DOM
1664  * not to call <code>preventDefault()</code>, since we want default browser behavior.
1665  * <p>
1666  * Note: Callers should set up their elements so that a click that is not within
1667  * a scrollbar goes to a more specific element (and not the one that scrolls).
1668  * That way we don't have to perform sketchy math to see if the click was in the
1669  * scrollbar.
1670  * </p>
1671  * <p>
1672  * <a href="https://bugzilla.mozilla.org/show_bug.cgi?id=489667">https://bugzilla.mozilla.org/show_bug.cgi?id=489667</a>
1673  * </p>
1674  * <p>It looks like the FF bug will be fixed with the release of 3.6.4.</p>
1675  * @param {DwtMouseEvent}	ev			the event
1676  * @return	{boolean}	<code>true</code> if FF3.6+ scrollbar click was detected and handled
1677  * 
1678  * @private
1679  */
1680 Dwt.ffScrollbarCheck =
1681 function(ev) {
1682 	if (AjxEnv.isFirefox3_6up || AjxEnv.isDesktop2up) {
1683 		var t = ev.target;
1684 		if (t && (t.clientHeight && t.scrollHeight && (t.clientHeight != t.scrollHeight)) ||
1685 				 (t.clientWidth && t.scrollWidth && (t.clientWidth != t.scrollWidth)))
1686 		{
1687 			ev._dontCallPreventDefault = true;
1688 			ev._stopPropagation = false;
1689 			ev._returnValue = true;
1690 			return true;
1691 		}
1692 	}
1693 	return false;
1694 };
1695 
1696 Dwt.selectText =
1697 function(el) {
1698 
1699 	if (!el) {
1700 		Dwt.deselectText();
1701 		return;
1702 	}
1703 
1704 	if (document.selection) {
1705 		// IE
1706 		var range = el.parentTextEdit.createTextRange();
1707 		range.moveToElementText(el);
1708 		range.select();
1709 	}
1710 	else if (window.getSelection) {
1711 		var range = document.createRange();
1712 		range.selectNode(el);
1713 		var sel = window.getSelection();
1714 		sel.addRange(range);
1715 	}
1716 };
1717 
1718 Dwt.deselectText =
1719 function() {
1720 
1721 	if (document.selection) {
1722 		// IE
1723 		document.selection.empty();
1724 	}
1725 	else if (window.getSelection) {
1726 		window.getSelection().removeAllRanges();
1727 	}
1728 };
1729 
1730 /**
1731  * Inserts some text into an input at the caret.
1732  *
1733  * @param {Element}     input       INPUT or TEXTAREA
1734  * @param {String}      text        text to insert
1735  */
1736 Dwt.insertText = function(input, text) {
1737 
1738     if (!input || !text) {
1739         return;
1740     }
1741 
1742     if (document.selection) {
1743         // IE
1744         input.focus();
1745         var sel = document.selection.createRange();
1746         sel.text = text;
1747         input.focus();
1748     }
1749     else if (AjxUtil.isSpecified(input.selectionStart)) {
1750         var start = input.selectionStart,
1751             end = input.selectionEnd;
1752         input.value = input.value.substring(0, start) + text + input.value.substring(end, input.value.length);
1753         input.selectionStart = start + text.length;
1754         input.selectionEnd = end + text.length;
1755     }
1756     else {
1757         input.value += text;
1758     }
1759 };
1760 
1761 /**
1762  * Returns true if the two elements overlap.
1763  * 
1764  * @param el1
1765  * @param el2
1766  */
1767 Dwt.doOverlap =
1768 function(el1, el2) {
1769 
1770 	if (!el1 || !el2) { return false; }
1771 
1772 	var loc1 = Dwt.getLocation(el1), loc2 = Dwt.getLocation(el2);
1773 	var size1 = Dwt.getSize(el1), size2 = Dwt.getSize(el2);
1774 	var left1 = loc1.x, left2 = loc2.x, top1 = loc1.y, top2 = loc2.y;
1775 	var right1 = left1 + size1.x, right2 = left2 + size2.x;
1776 	var bottom1 = top1 + size1.y, bottom2 = top2 + size2.y;
1777 
1778 	return !(left1 > right2 || right1 < left2 || top1 > bottom2 || bottom1 < top2);
1779 };
1780 
1781 /**
1782  * Resets the scrollTop of container (if necessary) to ensure that element is visible.
1783  * 
1784  * @param {Element}		element		the element to be made visible
1785  * @param {Element}		container	the containing element to possibly scroll
1786  * @private
1787  */
1788 Dwt.scrollIntoView =
1789 function(element, container) {
1790 	
1791 	if (!element || !container) { return; }
1792 	
1793 	var elementTop = Dwt.toWindow(element, 0, 0, null, null, DwtPoint.tmp).y;
1794 	var containerTop = Dwt.toWindow(container, 0, 0, null, null, DwtPoint.tmp).y + container.scrollTop;
1795 
1796 	var diff = elementTop - containerTop;
1797 	if (diff < 0) {
1798 		container.scrollTop += diff;
1799 	} else {
1800 		var containerH = Dwt.getSize(container, DwtPoint.tmp).y;
1801 		var elementH = Dwt.getSize(element, DwtPoint.tmp).y;
1802 		diff = (elementTop + elementH) - (containerTop + containerH);
1803 		if (diff > 0) {
1804 			container.scrollTop += diff;
1805 		}
1806 	}
1807 };
1808 
1809 /**
1810  * Sets up a hidden div for performance metrics.  Use to set the start of object rendering
1811  * @param id {String}
1812  * @param date {Date}
1813  */
1814 Dwt.setLoadingTime = 
1815 function(id, date) {
1816 	if (!window.isPerfMetric) { return;	}
1817 	date = date || new Date();
1818 	id += "_loading";
1819 	var div = document.getElementById(id);
1820 	if (!div) {
1821 		div = document.createElement("div");
1822 		div.id = id;
1823 		div.style.display = "none";
1824 		document.body.appendChild(div);
1825 	}
1826 	div.innerHTML = date.getTime();
1827 	if (window.appDevMode) {
1828 		console.profile(id);
1829 	}
1830 };
1831 
1832 /**
1833  * Sets up a hidden div for performance metrics.  Use to set the end of object rendering
1834  * @param id {String}
1835  * @param date {Date}
1836  */
1837 Dwt.setLoadedTime = 
1838 function(id, date) {
1839 	if (!window.isPerfMetric) { return;	}
1840 	date = date || new Date();
1841 	id += "_loaded";
1842 	var div = document.getElementById(id);
1843 	if (!div) {
1844 		div = document.createElement("div");
1845 		div.id = id;
1846 		div.style.display = "none";
1847 		document.body.appendChild(div);
1848 	}
1849 	div.innerHTML = date.getTime();
1850 	if (window.appDevMode) {
1851 		console.profileEnd();
1852 	}
1853 };
1854 
1855 /**
1856  * Prints the computed time from performance metrics data
1857  */
1858 Dwt.printPerfMetric =
1859 function() {
1860 	//code to print all loading stats
1861 	$.each($('div[id*="_loaded"]'), function(index, elem) {
1862 		var end_id = $(elem).attr("id");
1863 		var start_id_prefix = end_id.substring(0,end_id.indexOf("_"));
1864 		var end_elem = $("#" + start_id_prefix+"_launched");
1865 		if (end_elem && end_elem.length > 0) {
1866 			var end_time = $("#" + start_id_prefix+"_launched").html();
1867 		} else {
1868 			end_time = $("#" + start_id_prefix+"_loading").html();
1869 		}
1870 		var log = "Load time for " + start_id_prefix + " is " + ($(elem).html()-end_time);
1871 		DBG.println(AjxDebug.DBG1,log);
1872 		if (console) {
1873 			console.log(log);
1874 		}
1875 	});
1876 }
1877 
1878 // Css for Templates
1879 Dwt.createLinearGradientCss =
1880 function(startColor, endColor, direction) {
1881     var gradientCss = null;
1882     var gradient = this.createLinearGradientInfo(startColor, endColor, direction);
1883     if (gradient.field) {
1884         gradientCss = gradient.field + ":" + gradient.css + ";";
1885     }
1886     return gradientCss;
1887 }
1888 
1889 /**
1890  * -- FF 3.6+
1891  *    background: -moz-linear-gradient(black, white);
1892  * -- Safari 4+, Chrome 2+
1893  *    background: -webkit-gradient(linear, left top, left bottom, color-stop(0%, #000000), color-stop(100%, #ffffff));
1894  * -- Safari 5.1+, Chrome 10+
1895  *    background: -webkit-linear-gradient(top, black, white);
1896  * -- Opera 11.10
1897  *    background: -o-linear-gradient(black, white);
1898  * -- IE6 & IE7
1899  *    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#000000', endColorstr='#ffffff');
1900  * -- IE8 & IE9
1901  *    -ms-filter: "progid:DXImageTransform.Microsoft.gradient(startColorstr='#000000', endColorstr='#ffffff')";
1902  * -- IE10
1903  *    background: -ms-linear-gradient(black, white);
1904  * -- the standard
1905  *    background: linear-gradient(black, white);
1906  */
1907 Dwt.createLinearGradientInfo =
1908 function(startColor, endColor, direction) {
1909 
1910     var cssDirection;
1911     var gradient = {};
1912     if (AjxEnv.isIE && !AjxEnv.isIE9up) {
1913         cssDirection = (direction == 'v') ? 0 : 1;
1914         gradient.field = "filter";
1915         gradient.name  = "DXImageTransform.Microsoft.Gradient";
1916         gradient.css   = "progid:" + gradient.name + "(" +
1917                          "GradientType=" + cssDirection + ",startColorstr=" + startColor +
1918                          ",endColorstr=" + endColor + "); zoom:1;";
1919     } else if (AjxEnv.isIE9) {
1920         var params = {
1921             x1: "0%",
1922             x2: direction == 'v' ? "0%" : "100%",
1923             y1: "0%",
1924             y2: direction == 'v' ? "100%" : "0%",
1925             startColor: startColor,
1926             endColor: endColor
1927         };
1928         var svgsrc =
1929             AjxTemplate.expand('dwt.Widgets#SVGGradient', params);
1930         gradient.field = "background";
1931         gradient.css   = ('url(data:image/svg+xml,' +
1932                           escape(svgsrc.replace(/\s+/g, ' ')) + ')');
1933     } else if (AjxEnv.isFirefox3_6up) {
1934         cssDirection = (direction == 'v') ? 'top' : 'left';
1935         gradient.field = "background";
1936         gradient.css   = "-moz-linear-gradient(" + cssDirection + "," + startColor + ", "  + endColor + ")";
1937     } else if ((AjxEnv.isSafari && AjxEnv.isSafari5_1up) || AjxEnv.isChrome10up) {
1938         cssDirection = (direction == 'v') ? 'top' : 'left';
1939         gradient.field = "background";
1940         gradient.css   = "-webkit-linear-gradient(" + cssDirection + ","+
1941                           startColor + ", " + endColor + ")";
1942     } else if ((AjxEnv.isSafari && AjxEnv.isSafari4up) || AjxEnv.isChrome2up) {
1943         var startPt = 'left top';
1944         var endPt   = (direction == 'v') ? "left bottom" : "right top";
1945         gradient.field = "background";
1946         gradient.css   = "-webkit-gradient(linear, " + startPt + ", " + endPt +
1947                          ", color-stop(0%, " + startColor + "), color-stop(100%, " + endColor + "))";
1948     } else {
1949         cssDirection = (direction == 'v') ? 'to bottom' : 'to right';
1950         gradient.field = "background";
1951         gradient.css   = "linear-gradient(" + cssDirection + "," + startColor + ", "  + endColor + ")";
1952     }
1953     return gradient;
1954 }
1955 
1956 Dwt.setLinearGradient =
1957 function(htmlElement, startColor, endColor, direction) {
1958     var gradient = Dwt.createLinearGradientInfo(startColor, endColor, direction);
1959     if (gradient.field == 'filter') {
1960         Dwt.alterIEFilter(htmlElement, gradient.name, gradient.css);
1961     } else {
1962         htmlElement.style[gradient.field] = gradient.css;
1963     }
1964 }
1965 
1966 Dwt.alterIEFilter =
1967 function(htmlElement, filterName, newFilter) {
1968     if (htmlElement.style.filter) {
1969         var found = false;
1970         var filters = htmlElement.style.filter.split(" ");
1971         for (var i = 0; i < filters.length; i++) {
1972             if (filters[i].indexOf(filterName) != -1) {
1973                 filters[i] = newFilter;
1974                 found = true;
1975                 break;
1976             }
1977         }
1978         if (!found) {
1979             filters[filters.length] = newFilter;
1980         }
1981         htmlElement.style.filter = filters.join(" ");
1982     } else {
1983        htmlElement.style.filter = newFilter;
1984     }
1985 }
1986 
1987 Dwt.getIEFilter =
1988 function(htmlElement, filterName) {
1989     var filter = "";
1990     if (htmlElement.style.filter) {
1991         var filters = htmlElement.style.filter.split(" ");
1992         for (var i = 0; i < filters.length; i++) {
1993             if (filters[i].indexOf(filterName) != -1) {
1994                 filter = filters[i];
1995                 break;
1996             }
1997         }
1998     }
1999     return filter
2000 }
2001 
2002 // Used for an unattached DOM subtree.
2003 Dwt.getDescendant =
2004 function(htmlElement, id) {
2005     var descendant = null;
2006     for (var i = 0; i < htmlElement.childNodes.length; i++) {
2007         var child = htmlElement.childNodes[i];
2008         if (child.id == id) {
2009             descendant = child;
2010         } else {
2011             descendant = Dwt.getDescendant(child, id);
2012         }
2013         if (descendant != null) {
2014             break;
2015         }
2016     }
2017     return descendant;
2018 };
2019 
2020 Dwt.getPreviousElementSibling =
2021 function(element) {
2022 	var sibling = element.previousElementSibling;
2023 
2024 	if (sibling !== undefined) {
2025 		return sibling;
2026 	}
2027 
2028 	// workaround for missing previousElementSibling in MSIE 8
2029 	for (sibling = element.previousSibling;
2030 		 sibling && sibling.nodeType !== 1;
2031 		 sibling = sibling.previousSibling);
2032 
2033 	return sibling;
2034 }
2035 
2036 Dwt.getNextElementSibling =
2037 function(element) {
2038 	var sibling = element.nextElementSibling;
2039 
2040 	if (sibling !== undefined) {
2041 		return sibling;
2042 	}
2043 
2044 	// workaround for missing nextElementSibling in MSIE 8
2045 	for (sibling = element.nextSibling;
2046 		 sibling && sibling.nodeType !== 1;
2047 		 sibling = sibling.nextSibling);
2048 
2049 	return sibling;
2050 }
2051 
2052 Dwt.getScrollbarSizes = function(node) {
2053     var insets = Dwt.getInsets(node);
2054     var style = DwtCssStyle.getComputedStyleObject(node);
2055 
2056     var bl = parseInt(style.borderLeftWidth)    || 0;
2057     var bt = parseInt(style.borderTopWidth)     || 0;
2058     var br = parseInt(style.borderRightWidth)   || 0;
2059     var bb = parseInt(style.borderBottomWidth)  || 0;
2060 
2061     var width = node.offsetWidth - node.clientWidth - bl - br;
2062     var height = node.offsetHeight - node.clientHeight - bt - bb;
2063 
2064     return new DwtPoint(width, height);
2065 };
2066