Automatic installer.php lang files by installer_builder (20070726)
[moodle-linuxchix.git] / lib / yui / dom / dom.js
blobc37bd1b04e46985ffb27c8edf4cb63cc6d6912d5
1 /*
2 Copyright (c) 2006, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 0.12.2
6 */
7 /**
8  * The dom module provides helper methods for manipulating Dom elements.
9  * @module dom
10  *
11  */
13 (function() {
14     var Y = YAHOO.util,     // internal shorthand
15         getStyle,           // for load time browser branching
16         setStyle,           // ditto
17         id_counter = 0,     // for use with generateId
18         propertyCache = {}; // for faster hyphen converts
19     
20     // brower detection
21     var ua = navigator.userAgent.toLowerCase(),
22         isOpera = (ua.indexOf('opera') > -1),
23         isSafari = (ua.indexOf('safari') > -1),
24         isGecko = (!isOpera && !isSafari && ua.indexOf('gecko') > -1),
25         isIE = (!isOpera && ua.indexOf('msie') > -1); 
26     
27     // regex cache
28     var patterns = {
29         HYPHEN: /(-[a-z])/i
30     };
32     
33     var toCamel = function(property) {
34         if ( !patterns.HYPHEN.test(property) ) {
35             return property; // no hyphens
36         }
37         
38         if (propertyCache[property]) { // already converted
39             return propertyCache[property];
40         }
41         
42         while( patterns.HYPHEN.exec(property) ) {
43             property = property.replace(RegExp.$1,
44                     RegExp.$1.substr(1).toUpperCase());
45         }
46         
47         propertyCache[property] = property;
48         return property;
49         //return property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase()}) // cant use function as 2nd arg yet due to safari bug
50     };
51     
52     // branching at load instead of runtime
53     if (document.defaultView && document.defaultView.getComputedStyle) { // W3C DOM method
54         getStyle = function(el, property) {
55             var value = null;
56             
57             var computed = document.defaultView.getComputedStyle(el, '');
58             if (computed) { // test computed before touching for safari
59                 value = computed[toCamel(property)];
60             }
61             
62             return el.style[property] || value;
63         };
64     } else if (document.documentElement.currentStyle && isIE) { // IE method
65         getStyle = function(el, property) {                         
66             switch( toCamel(property) ) {
67                 case 'opacity' :// IE opacity uses filter
68                     var val = 100;
69                     try { // will error if no DXImageTransform
70                         val = el.filters['DXImageTransform.Microsoft.Alpha'].opacity;
72                     } catch(e) {
73                         try { // make sure its in the document
74                             val = el.filters('alpha').opacity;
75                         } catch(e) {
76                         }
77                     }
78                     return val / 100;
79                     break;
80                 default: 
81                     // test currentStyle before touching
82                     var value = el.currentStyle ? el.currentStyle[property] : null;
83                     return ( el.style[property] || value );
84             }
85         };
86     } else { // default to inline only
87         getStyle = function(el, property) { return el.style[property]; };
88     }
89     
90     if (isIE) {
91         setStyle = function(el, property, val) {
92             switch (property) {
93                 case 'opacity':
94                     if ( typeof el.style.filter == 'string' ) { // in case not appended
95                         el.style.filter = 'alpha(opacity=' + val * 100 + ')';
96                         
97                         if (!el.currentStyle || !el.currentStyle.hasLayout) {
98                             el.style.zoom = 1; // when no layout or cant tell
99                         }
100                     }
101                     break;
102                 default:
103                 el.style[property] = val;
104             }
105         };
106     } else {
107         setStyle = function(el, property, val) {
108             el.style[property] = val;
109         };
110     }
111     
112     /**
113      * Provides helper methods for DOM elements.
114      * @namespace YAHOO.util
115      * @class Dom
116      */
117     YAHOO.util.Dom = {
118         /**
119          * Returns an HTMLElement reference.
120          * @method get
121          * @param {String | HTMLElement |Array} el Accepts a string to use as an ID for getting a DOM reference, an actual DOM reference, or an Array of IDs and/or HTMLElements.
122          * @return {HTMLElement | Array} A DOM reference to an HTML element or an array of HTMLElements.
123          */
124         get: function(el) {
125             if (!el) { return null; } // nothing to work with
126             
127             if (typeof el != 'string' && !(el instanceof Array) ) { // assuming HTMLElement or HTMLCollection, so pass back as is
128                 return el;
129             }
130             
131             if (typeof el == 'string') { // ID
132                 return document.getElementById(el);
133             }
134             else { // array of ID's and/or elements
135                 var collection = [];
136                 for (var i = 0, len = el.length; i < len; ++i) {
137                     collection[collection.length] = Y.Dom.get(el[i]);
138                 }
139                 
140                 return collection;
141             }
143             return null; // safety, should never happen
144         },
145     
146         /**
147          * Normalizes currentStyle and ComputedStyle.
148          * @method getStyle
149          * @param {String | HTMLElement |Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
150          * @param {String} property The style property whose value is returned.
151          * @return {String | Array} The current value of the style property for the element(s).
152          */
153         getStyle: function(el, property) {
154             property = toCamel(property);
155             
156             var f = function(element) {
157                 return getStyle(element, property);
158             };
159             
160             return Y.Dom.batch(el, f, Y.Dom, true);
161         },
162     
163         /**
164          * Wrapper for setting style properties of HTMLElements.  Normalizes "opacity" across modern browsers.
165          * @method setStyle
166          * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
167          * @param {String} property The style property to be set.
168          * @param {String} val The value to apply to the given property.
169          */
170         setStyle: function(el, property, val) {
171             property = toCamel(property);
172             
173             var f = function(element) {
174                 setStyle(element, property, val);
175                 
176             };
177             
178             Y.Dom.batch(el, f, Y.Dom, true);
179         },
180         
181         /**
182          * Gets the current position of an element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
183          * @method getXY
184          * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
185          * @return {Array} The XY position of the element(s)
186          */
187         getXY: function(el) {
188             var f = function(el) {
189     
190             // has to be part of document to have pageXY
191                 if (el.parentNode === null || el.offsetParent === null ||
192                         this.getStyle(el, 'display') == 'none') {
193                     return false;
194                 }
195                 
196                 var parentNode = null;
197                 var pos = [];
198                 var box;
199                 
200                 if (el.getBoundingClientRect) { // IE
201                     box = el.getBoundingClientRect();
202                     var doc = document;
203                     if ( !this.inDocument(el) && parent.document != document) {// might be in a frame, need to get its scroll
204                         doc = parent.document;
206                         if ( !this.isAncestor(doc.documentElement, el) ) {
207                             return false;                      
208                         }
210                     }
212                     var scrollTop = Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
213                     var scrollLeft = Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
214                     
215                     return [box.left + scrollLeft, box.top + scrollTop];
216                 }
217                 else { // safari, opera, & gecko
218                     pos = [el.offsetLeft, el.offsetTop];
219                     parentNode = el.offsetParent;
220                     if (parentNode != el) {
221                         while (parentNode) {
222                             pos[0] += parentNode.offsetLeft;
223                             pos[1] += parentNode.offsetTop;
224                             parentNode = parentNode.offsetParent;
225                         }
226                     }
227                     if (isSafari && this.getStyle(el, 'position') == 'absolute' ) { // safari doubles in some cases
228                         pos[0] -= document.body.offsetLeft;
229                         pos[1] -= document.body.offsetTop;
230                     } 
231                 }
232                 
233                 if (el.parentNode) { parentNode = el.parentNode; }
234                 else { parentNode = null; }
235         
236                 while (parentNode && parentNode.tagName.toUpperCase() != 'BODY' && parentNode.tagName.toUpperCase() != 'HTML') 
237                 { // account for any scrolled ancestors
238                     if (Y.Dom.getStyle(parentNode, 'display') != 'inline') { // work around opera inline scrollLeft/Top bug
239                         pos[0] -= parentNode.scrollLeft;
240                         pos[1] -= parentNode.scrollTop;
241                     }
242                     
243                     if (parentNode.parentNode) {
244                         parentNode = parentNode.parentNode; 
245                     } else { parentNode = null; }
246                 }
247         
248                 
249                 return pos;
250             };
251             
252             return Y.Dom.batch(el, f, Y.Dom, true);
253         },
254         
255         /**
256          * Gets the current X position of an element based on page coordinates.  The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
257          * @method getX
258          * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
259          * @return {String | Array} The X position of the element(s)
260          */
261         getX: function(el) {
262             var f = function(el) {
263                 return Y.Dom.getXY(el)[0];
264             };
265             
266             return Y.Dom.batch(el, f, Y.Dom, true);
267         },
268         
269         /**
270          * Gets the current Y position of an element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
271          * @method getY
272          * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
273          * @return {String | Array} The Y position of the element(s)
274          */
275         getY: function(el) {
276             var f = function(el) {
277                 return Y.Dom.getXY(el)[1];
278             };
279             
280             return Y.Dom.batch(el, f, Y.Dom, true);
281         },
282         
283         /**
284          * Set the position of an html element in page coordinates, regardless of how the element is positioned.
285          * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
286          * @method setXY
287          * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements
288          * @param {Array} pos Contains X & Y values for new position (coordinates are page-based)
289          * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
290          */
291         setXY: function(el, pos, noRetry) {
292             var f = function(el) {
293                 var style_pos = this.getStyle(el, 'position');
294                 if (style_pos == 'static') { // default to relative
295                     this.setStyle(el, 'position', 'relative');
296                     style_pos = 'relative';
297                 }
299                 var pageXY = this.getXY(el);
300                 if (pageXY === false) { // has to be part of doc to have pageXY
301                     return false; 
302                 }
303                 
304                 var delta = [ // assuming pixels; if not we will have to retry
305                     parseInt( this.getStyle(el, 'left'), 10 ),
306                     parseInt( this.getStyle(el, 'top'), 10 )
307                 ];
308             
309                 if ( isNaN(delta[0]) ) {// in case of 'auto'
310                     delta[0] = (style_pos == 'relative') ? 0 : el.offsetLeft;
311                 } 
312                 if ( isNaN(delta[1]) ) { // in case of 'auto'
313                     delta[1] = (style_pos == 'relative') ? 0 : el.offsetTop;
314                 } 
315         
316                 if (pos[0] !== null) { el.style.left = pos[0] - pageXY[0] + delta[0] + 'px'; }
317                 if (pos[1] !== null) { el.style.top = pos[1] - pageXY[1] + delta[1] + 'px'; }
318               
319                 if (!noRetry) {
320                     var newXY = this.getXY(el);
322                     // if retry is true, try one more time if we miss 
323                    if ( (pos[0] !== null && newXY[0] != pos[0]) || 
324                         (pos[1] !== null && newXY[1] != pos[1]) ) {
325                        this.setXY(el, pos, true);
326                    }
327                 }        
328         
329             };
330             
331             Y.Dom.batch(el, f, Y.Dom, true);
332         },
333         
334         /**
335          * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
336          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
337          * @method setX
338          * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
339          * @param {Int} x The value to use as the X coordinate for the element(s).
340          */
341         setX: function(el, x) {
342             Y.Dom.setXY(el, [x, null]);
343         },
344         
345         /**
346          * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
347          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
348          * @method setY
349          * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
350          * @param {Int} x To use as the Y coordinate for the element(s).
351          */
352         setY: function(el, y) {
353             Y.Dom.setXY(el, [null, y]);
354         },
355         
356         /**
357          * Returns the region position of the given element.
358          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
359          * @method getRegion
360          * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM reference, or an Array of IDs and/or HTMLElements.
361          * @return {Region | Array} A Region or array of Region instances containing "top, left, bottom, right" member data.
362          */
363         getRegion: function(el) {
364             var f = function(el) {
365                 var region = new Y.Region.getRegion(el);
366                 return region;
367             };
368             
369             return Y.Dom.batch(el, f, Y.Dom, true);
370         },
371         
372         /**
373          * Returns the width of the client (viewport).
374          * @method getClientWidth
375          * @deprecated Now using getViewportWidth.  This interface left intact for back compat.
376          * @return {Int} The width of the viewable area of the page.
377          */
378         getClientWidth: function() {
379             return Y.Dom.getViewportWidth();
380         },
381         
382         /**
383          * Returns the height of the client (viewport).
384          * @method getClientHeight
385          * @deprecated Now using getViewportHeight.  This interface left intact for back compat.
386          * @return {Int} The height of the viewable area of the page.
387          */
388         getClientHeight: function() {
389             return Y.Dom.getViewportHeight();
390         },
392         /**
393          * Returns a array of HTMLElements with the given class.
394          * For optimized performance, include a tag and/or root node when possible.
395          * @method getElementsByClassName
396          * @param {String} className The class name to match against
397          * @param {String} tag (optional) The tag name of the elements being collected
398          * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
399          * @return {Array} An array of elements that have the given class name
400          */
401         getElementsByClassName: function(className, tag, root) {
402             var method = function(el) { return Y.Dom.hasClass(el, className); };
403             return Y.Dom.getElementsBy(method, tag, root);
404         },
406         /**
407          * Determines whether an HTMLElement has the given className.
408          * @method hasClass
409          * @param {String | HTMLElement | Array} el The element or collection to test
410          * @param {String} className the class name to search for
411          * @return {Boolean | Array} A boolean value or array of boolean values
412          */
413         hasClass: function(el, className) {
414             var re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
415             
416             var f = function(el) {
417                 return re.test(el['className']);
418             };
419             
420             return Y.Dom.batch(el, f, Y.Dom, true);
421         },
422     
423         /**
424          * Adds a class name to a given element or collection of elements.
425          * @method addClass         
426          * @param {String | HTMLElement | Array} el The element or collection to add the class to
427          * @param {String} className the class name to add to the class attribute
428          */
429         addClass: function(el, className) {
430             var f = function(el) {
431                 if (this.hasClass(el, className)) { return; } // already present
432                 
433                 
434                 el['className'] = [el['className'], className].join(' ');
435             };
436             
437             Y.Dom.batch(el, f, Y.Dom, true);
438         },
439     
440         /**
441          * Removes a class name from a given element or collection of elements.
442          * @method removeClass         
443          * @param {String | HTMLElement | Array} el The element or collection to remove the class from
444          * @param {String} className the class name to remove from the class attribute
445          */
446         removeClass: function(el, className) {
447             var re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', 'g');
449             var f = function(el) {
450                 if (!this.hasClass(el, className)) { return; } // not present
451                 
452                 
453                 var c = el['className'];
454                 el['className'] = c.replace(re, ' ');
455                 if ( this.hasClass(el, className) ) { // in case of multiple adjacent
456                     this.removeClass(el, className);
457                 }
458                 
459             };
460             
461             Y.Dom.batch(el, f, Y.Dom, true);
462         },
463         
464         /**
465          * Replace a class with another class for a given element or collection of elements.
466          * If no oldClassName is present, the newClassName is simply added.
467          * @method replaceClass  
468          * @param {String | HTMLElement | Array} el The element or collection to remove the class from
469          * @param {String} oldClassName the class name to be replaced
470          * @param {String} newClassName the class name that will be replacing the old class name
471          */
472         replaceClass: function(el, oldClassName, newClassName) {
473             if (oldClassName === newClassName) { // avoid infinite loop
474                 return false;
475             }
476             
477             var re = new RegExp('(?:^|\\s+)' + oldClassName + '(?:\\s+|$)', 'g');
479             var f = function(el) {
480             
481                 if ( !this.hasClass(el, oldClassName) ) {
482                     this.addClass(el, newClassName); // just add it if nothing to replace
483                     return; // note return
484                 }
485             
486                 el['className'] = el['className'].replace(re, ' ' + newClassName + ' ');
488                 if ( this.hasClass(el, oldClassName) ) { // in case of multiple adjacent
489                     this.replaceClass(el, oldClassName, newClassName);
490                 }
491             };
492             
493             Y.Dom.batch(el, f, Y.Dom, true);
494         },
495         
496         /**
497          * Generates a unique ID
498          * @method generateId  
499          * @param {String | HTMLElement | Array} el (optional) An optional element array of elements to add an ID to (no ID is added if one is already present).
500          * @param {String} prefix (optional) an optional prefix to use (defaults to "yui-gen").
501          * @return {String | Array} The generated ID, or array of generated IDs (or original ID if already present on an element)
502          */
503         generateId: function(el, prefix) {
504             prefix = prefix || 'yui-gen';
505             el = el || {};
506             
507             var f = function(el) {
508                 if (el) {
509                     el = Y.Dom.get(el);
510                 } else {
511                     el = {}; // just generating ID in this case
512                 }
513                 
514                 if (!el.id) {
515                     el.id = prefix + id_counter++; 
516                 } // dont override existing
517                 
518                 
519                 return el.id;
520             };
521             
522             return Y.Dom.batch(el, f, Y.Dom, true);
523         },
524         
525         /**
526          * Determines whether an HTMLElement is an ancestor of another HTML element in the DOM hierarchy.
527          * @method isAncestor
528          * @param {String | HTMLElement} haystack The possible ancestor
529          * @param {String | HTMLElement} needle The possible descendent
530          * @return {Boolean} Whether or not the haystack is an ancestor of needle
531          */
532         isAncestor: function(haystack, needle) {
533             haystack = Y.Dom.get(haystack);
534             if (!haystack || !needle) { return false; }
535             
536             var f = function(needle) {
537                 if (haystack.contains && !isSafari) { // safari "contains" is broken
538                     return haystack.contains(needle);
539                 }
540                 else if ( haystack.compareDocumentPosition ) {
541                     return !!(haystack.compareDocumentPosition(needle) & 16);
542                 }
543                 else { // loop up and test each parent
544                     var parent = needle.parentNode;
545                     
546                     while (parent) {
547                         if (parent == haystack) {
548                             return true;
549                         }
550                         else if (!parent.tagName || parent.tagName.toUpperCase() == 'HTML') {
551                             return false;
552                         }
553                         
554                         parent = parent.parentNode;
555                     }
556                     return false;
557                 }     
558             };
559             
560             return Y.Dom.batch(needle, f, Y.Dom, true);      
561         },
562         
563         /**
564          * Determines whether an HTMLElement is present in the current document.
565          * @method inDocument         
566          * @param {String | HTMLElement} el The element to search for
567          * @return {Boolean} Whether or not the element is present in the current document
568          */
569         inDocument: function(el) {
570             var f = function(el) {
571                 return this.isAncestor(document.documentElement, el);
572             };
573             
574             return Y.Dom.batch(el, f, Y.Dom, true);
575         },
576         
577         /**
578          * Returns a array of HTMLElements that pass the test applied by supplied boolean method.
579          * For optimized performance, include a tag and/or root node when possible.
580          * @method getElementsBy
581          * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
583          * @param {String} tag (optional) The tag name of the elements being collected
584          * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
585          */
586         getElementsBy: function(method, tag, root) {
587             tag = tag || '*';
588             
589             var nodes = [];
590             
591             if (root) {
592                 root = Y.Dom.get(root);
593                 if (!root) { // if no root node, then no children
594                     return nodes;
595                 }
596             } else {
597                 root = document;
598             }
599             
600             var elements = root.getElementsByTagName(tag);
601             
602             if ( !elements.length && (tag == '*' && root.all) ) {
603                 elements = root.all; // IE < 6
604             }
605             
606             for (var i = 0, len = elements.length; i < len; ++i) {
607                 if ( method(elements[i]) ) { nodes[nodes.length] = elements[i]; }
608             }
610             
611             return nodes;
612         },
613         
614         /**
615          * Returns an array of elements that have had the supplied method applied.
616          * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) ).
617          * @method batch
618          * @param {String | HTMLElement | Array} el (optional) An element or array of elements to apply the method to
619          * @param {Function} method The method to apply to the element(s)
620          * @param {Any} o (optional) An optional arg that is passed to the supplied method
621          * @param {Boolean} override (optional) Whether or not to override the scope of "method" with "o"
622          * @return {HTMLElement | Array} The element(s) with the method applied
623          */
624         batch: function(el, method, o, override) {
625             var id = el;
626             el = Y.Dom.get(el);
627             
628             var scope = (override) ? o : window;
629             
630             if (!el || el.tagName || !el.length) { // is null or not a collection (tagName for SELECT and others that can be both an element and a collection)
631                 if (!el) {
632                     return false;
633                 }
634                 return method.call(scope, el, o);
635             } 
636             
637             var collection = [];
638             
639             for (var i = 0, len = el.length; i < len; ++i) {
640                 if (!el[i]) {
641                     id = el[i];
642                 }
643                 collection[collection.length] = method.call(scope, el[i], o);
644             }
645             
646             return collection;
647         },
648         
649         /**
650          * Returns the height of the document.
651          * @method getDocumentHeight
652          * @return {Int} The height of the actual document (which includes the body and its margin).
653          */
654         getDocumentHeight: function() {
655             var scrollHeight = (document.compatMode != 'CSS1Compat') ? document.body.scrollHeight : document.documentElement.scrollHeight;
657             var h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
658             return h;
659         },
660         
661         /**
662          * Returns the width of the document.
663          * @method getDocumentWidth
664          * @return {Int} The width of the actual document (which includes the body and its margin).
665          */
666         getDocumentWidth: function() {
667             var scrollWidth = (document.compatMode != 'CSS1Compat') ? document.body.scrollWidth : document.documentElement.scrollWidth;
668             var w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
669             return w;
670         },
672         /**
673          * Returns the current height of the viewport.
674          * @method getViewportHeight
675          * @return {Int} The height of the viewable area of the page (excludes scrollbars).
676          */
677         getViewportHeight: function() {
678             var height = self.innerHeight; // Safari, Opera
679             var mode = document.compatMode;
680         
681             if ( (mode || isIE) && !isOpera ) { // IE, Gecko
682                 height = (mode == 'CSS1Compat') ?
683                         document.documentElement.clientHeight : // Standards
684                         document.body.clientHeight; // Quirks
685             }
686         
687             return height;
688         },
689         
690         /**
691          * Returns the current width of the viewport.
692          * @method getViewportWidth
693          * @return {Int} The width of the viewable area of the page (excludes scrollbars).
694          */
695         
696         getViewportWidth: function() {
697             var width = self.innerWidth;  // Safari
698             var mode = document.compatMode;
699             
700             if (mode || isIE) { // IE, Gecko, Opera
701                 width = (mode == 'CSS1Compat') ?
702                         document.documentElement.clientWidth : // Standards
703                         document.body.clientWidth; // Quirks
704             }
705             return width;
706         }
707     };
708 })();
710  * A region is a representation of an object on a grid.  It is defined
711  * by the top, right, bottom, left extents, so is rectangular by default.  If 
712  * other shapes are required, this class could be extended to support it.
713  * @namespace YAHOO.util
714  * @class Region
715  * @param {Int} t the top extent
716  * @param {Int} r the right extent
717  * @param {Int} b the bottom extent
718  * @param {Int} l the left extent
719  * @constructor
720  */
721 YAHOO.util.Region = function(t, r, b, l) {
723     /**
724      * The region's top extent
725      * @property top
726      * @type Int
727      */
728     this.top = t;
729     
730     /**
731      * The region's top extent as index, for symmetry with set/getXY
732      * @property 1
733      * @type Int
734      */
735     this[1] = t;
737     /**
738      * The region's right extent
739      * @property right
740      * @type int
741      */
742     this.right = r;
744     /**
745      * The region's bottom extent
746      * @property bottom
747      * @type Int
748      */
749     this.bottom = b;
751     /**
752      * The region's left extent
753      * @property left
754      * @type Int
755      */
756     this.left = l;
757     
758     /**
759      * The region's left extent as index, for symmetry with set/getXY
760      * @property 0
761      * @type Int
762      */
763     this[0] = l;
767  * Returns true if this region contains the region passed in
768  * @method contains
769  * @param  {Region}  region The region to evaluate
770  * @return {Boolean}        True if the region is contained with this region, 
771  *                          else false
772  */
773 YAHOO.util.Region.prototype.contains = function(region) {
774     return ( region.left   >= this.left   && 
775              region.right  <= this.right  && 
776              region.top    >= this.top    && 
777              region.bottom <= this.bottom    );
782  * Returns the area of the region
783  * @method getArea
784  * @return {Int} the region's area
785  */
786 YAHOO.util.Region.prototype.getArea = function() {
787     return ( (this.bottom - this.top) * (this.right - this.left) );
791  * Returns the region where the passed in region overlaps with this one
792  * @method intersect
793  * @param  {Region} region The region that intersects
794  * @return {Region}        The overlap region, or null if there is no overlap
795  */
796 YAHOO.util.Region.prototype.intersect = function(region) {
797     var t = Math.max( this.top,    region.top    );
798     var r = Math.min( this.right,  region.right  );
799     var b = Math.min( this.bottom, region.bottom );
800     var l = Math.max( this.left,   region.left   );
801     
802     if (b >= t && r >= l) {
803         return new YAHOO.util.Region(t, r, b, l);
804     } else {
805         return null;
806     }
810  * Returns the region representing the smallest region that can contain both
811  * the passed in region and this region.
812  * @method union
813  * @param  {Region} region The region that to create the union with
814  * @return {Region}        The union region
815  */
816 YAHOO.util.Region.prototype.union = function(region) {
817     var t = Math.min( this.top,    region.top    );
818     var r = Math.max( this.right,  region.right  );
819     var b = Math.max( this.bottom, region.bottom );
820     var l = Math.min( this.left,   region.left   );
822     return new YAHOO.util.Region(t, r, b, l);
826  * toString
827  * @method toString
828  * @return string the region properties
829  */
830 YAHOO.util.Region.prototype.toString = function() {
831     return ( "Region {"    +
832              "top: "       + this.top    + 
833              ", right: "   + this.right  + 
834              ", bottom: "  + this.bottom + 
835              ", left: "    + this.left   + 
836              "}" );
840  * Returns a region that is occupied by the DOM element
841  * @method getRegion
842  * @param  {HTMLElement} el The element
843  * @return {Region}         The region that the element occupies
844  * @static
845  */
846 YAHOO.util.Region.getRegion = function(el) {
847     var p = YAHOO.util.Dom.getXY(el);
849     var t = p[1];
850     var r = p[0] + el.offsetWidth;
851     var b = p[1] + el.offsetHeight;
852     var l = p[0];
854     return new YAHOO.util.Region(t, r, b, l);
857 /////////////////////////////////////////////////////////////////////////////
860  * A point is a region that is special in that it represents a single point on 
861  * the grid.
862  * @namespace YAHOO.util
863  * @class Point
864  * @param {Int} x The X position of the point
865  * @param {Int} y The Y position of the point
866  * @constructor
867  * @extends YAHOO.util.Region
868  */
869 YAHOO.util.Point = function(x, y) {
870    if (x instanceof Array) { // accept output from Dom.getXY
871       y = x[1];
872       x = x[0];
873    }
874    
875     /**
876      * The X position of the point, which is also the right, left and index zero (for Dom.getXY symmetry)
877      * @property x
878      * @type Int
879      */
881     this.x = this.right = this.left = this[0] = x;
882      
883     /**
884      * The Y position of the point, which is also the top, bottom and index one (for Dom.getXY symmetry)
885      * @property y
886      * @type Int
887      */
888     this.y = this.top = this.bottom = this[1] = y;
891 YAHOO.util.Point.prototype = new YAHOO.util.Region();