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