Project

General

Profile

« Previous | Next » 

Revision 1263

Added by Dietmar over 14 years ago

updated YUI 2.4.1 to 2.8.0r4

View differences:

dom-debug.js
1 1
/*
2
Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 3
Code licensed under the BSD License:
4 4
http://developer.yahoo.net/yui/license.txt
5
version: 2.4.1
5
version: 2.8.0r4
6 6
*/
7 7
/**
8 8
 * The dom module provides helper methods for manipulating Dom elements.
......
11 11
 */
12 12

  
13 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
14
    // for use with generateId (global to save state if Dom is overwritten)
15
    YAHOO.env._id_counter = YAHOO.env._id_counter || 0;
16

  
17
    // internal shorthand
18
    var Y = YAHOO.util,
19
        lang = YAHOO.lang,
20
        UA = YAHOO.env.ua,
21
        trim = YAHOO.lang.trim,
18 22
        propertyCache = {}, // for faster hyphen converts
19
        reClassNameCache = {},          // cache regexes for className
20
        document = window.document;     // cache for faster lookups
23
        reCache = {}, // cache className regexes
24
        RE_TABLE = /^t(?:able|d|h)$/i, // for _calcBorders
25
        RE_COLOR = /color$/i,
26

  
27
        // DOM aliases 
28
        document = window.document,     
29
        documentElement = document.documentElement,
30

  
31
        // string constants
32
        OWNER_DOCUMENT = 'ownerDocument',
33
        DEFAULT_VIEW = 'defaultView',
34
        DOCUMENT_ELEMENT = 'documentElement',
35
        COMPAT_MODE = 'compatMode',
36
        OFFSET_LEFT = 'offsetLeft',
37
        OFFSET_TOP = 'offsetTop',
38
        OFFSET_PARENT = 'offsetParent',
39
        PARENT_NODE = 'parentNode',
40
        NODE_TYPE = 'nodeType',
41
        TAG_NAME = 'tagName',
42
        SCROLL_LEFT = 'scrollLeft',
43
        SCROLL_TOP = 'scrollTop',
44
        GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
45
        GET_COMPUTED_STYLE = 'getComputedStyle',
46
        CURRENT_STYLE = 'currentStyle',
47
        CSS1_COMPAT = 'CSS1Compat',
48
        _BACK_COMPAT = 'BackCompat',
49
        _CLASS = 'class', // underscore due to reserved word
50
        CLASS_NAME = 'className',
51
        EMPTY = '',
52
        SPACE = ' ',
53
        C_START = '(?:^|\\s)',
54
        C_END = '(?= |$)',
55
        G = 'g',
56
        POSITION = 'position',
57
        FIXED = 'fixed',
58
        RELATIVE = 'relative',
59
        LEFT = 'left',
60
        TOP = 'top',
61
        MEDIUM = 'medium',
62
        BORDER_LEFT_WIDTH = 'borderLeftWidth',
63
        BORDER_TOP_WIDTH = 'borderTopWidth',
21 64
    
22 65
    // brower detection
23
    var isOpera = YAHOO.env.ua.opera,
24
        isSafari = YAHOO.env.ua.webkit, 
25
        isGecko = YAHOO.env.ua.gecko,
26
        isIE = YAHOO.env.ua.ie; 
66
        isOpera = UA.opera,
67
        isSafari = UA.webkit, 
68
        isGecko = UA.gecko, 
69
        isIE = UA.ie; 
27 70
    
28
    // regex cache
29
    var patterns = {
30
        HYPHEN: /(-[a-z])/i, // to normalize get/setStyle
31
        ROOT_TAG: /^body|html$/i // body for quirks mode, html for standards
32
    };
33

  
34
    var toCamel = function(property) {
35
        if ( !patterns.HYPHEN.test(property) ) {
36
            return property; // no hyphens
37
        }
38
        
39
        if (propertyCache[property]) { // already converted
40
            return propertyCache[property];
41
        }
42
       
43
        var converted = property;
44
 
45
        while( patterns.HYPHEN.exec(converted) ) {
46
            converted = converted.replace(RegExp.$1,
47
                    RegExp.$1.substr(1).toUpperCase());
48
        }
49
        
50
        propertyCache[property] = converted;
51
        return converted;
52
        //return property.replace(/-([a-z])/gi, function(m0, m1) {return m1.toUpperCase()}) // cant use function as 2nd arg yet due to safari bug
53
    };
54
    
55
    var getClassRegEx = function(className) {
56
        var re = reClassNameCache[className];
57
        if (!re) {
58
            re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
59
            reClassNameCache[className] = re;
60
        }
61
        return re;
62
    };
63

  
64
    // branching at load instead of runtime
65
    if (document.defaultView && document.defaultView.getComputedStyle) { // W3C DOM method
66
        getStyle = function(el, property) {
67
            var value = null;
68
            
69
            if (property == 'float') { // fix reserved word
70
                property = 'cssFloat';
71
            }
72

  
73
            var computed = document.defaultView.getComputedStyle(el, '');
74
            if (computed) { // test computed before touching for safari
75
                value = computed[toCamel(property)];
76
            }
77
            
78
            return el.style[property] || value;
79
        };
80
    } else if (document.documentElement.currentStyle && isIE) { // IE method
81
        getStyle = function(el, property) {                         
82
            switch( toCamel(property) ) {
83
                case 'opacity' :// IE opacity uses filter
84
                    var val = 100;
85
                    try { // will error if no DXImageTransform
86
                        val = el.filters['DXImageTransform.Microsoft.Alpha'].opacity;
87

  
88
                    } catch(e) {
89
                        try { // make sure its in the document
90
                            val = el.filters('alpha').opacity;
91
                        } catch(e) {
92
                            YAHOO.log('getStyle: IE filter failed',
93
                                    'error', 'Dom');
94
                        }
95
                    }
96
                    return val / 100;
97
                case 'float': // fix reserved word
98
                    property = 'styleFloat'; // fall through
99
                default: 
100
                    // test currentStyle before touching
101
                    var value = el.currentStyle ? el.currentStyle[property] : null;
102
                    return ( el.style[property] || value );
103
            }
104
        };
105
    } else { // default to inline only
106
        getStyle = function(el, property) { return el.style[property]; };
107
    }
108
    
109
    if (isIE) {
110
        setStyle = function(el, property, val) {
111
            switch (property) {
112
                case 'opacity':
113
                    if ( YAHOO.lang.isString(el.style.filter) ) { // in case not appended
114
                        el.style.filter = 'alpha(opacity=' + val * 100 + ')';
115
                        
116
                        if (!el.currentStyle || !el.currentStyle.hasLayout) {
117
                            el.style.zoom = 1; // when no layout or cant tell
118
                        }
119
                    }
120
                    break;
121
                case 'float':
122
                    property = 'styleFloat';
123
                default:
124
                el.style[property] = val;
125
            }
126
        };
127
    } else {
128
        setStyle = function(el, property, val) {
129
            if (property == 'float') {
130
                property = 'cssFloat';
131
            }
132
            el.style[property] = val;
133
        };
134
    }
135

  
136
    var testElement = function(node, method) {
137
        return node && node.nodeType == 1 && ( !method || method(node) );
138
    };
139

  
140 71
    /**
141 72
     * Provides helper methods for DOM elements.
142 73
     * @namespace YAHOO.util
143 74
     * @class Dom
75
     * @requires yahoo, event
144 76
     */
145
    YAHOO.util.Dom = {
77
    Y.Dom = {
78
        CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
79
            'for': 'htmlFor',
80
            'class': CLASS_NAME
81
        } : { // w3c
82
            'htmlFor': 'for',
83
            'className': _CLASS
84
        },
85

  
86
        DOT_ATTRIBUTES: {},
87

  
146 88
        /**
147 89
         * Returns an HTMLElement reference.
148 90
         * @method get
......
150 92
         * @return {HTMLElement | Array} A DOM reference to an HTML element or an array of HTMLElements.
151 93
         */
152 94
        get: function(el) {
153
            if (el && (el.tagName || el.item)) { // HTMLElement, or HTMLCollection
154
                return el;
155
            }
95
            var id, nodes, c, i, len, attr;
156 96

  
157
            if (YAHOO.lang.isString(el) || !el) { // HTMLElement or null
158
                return document.getElementById(el);
159
            }
160
            
161
            if (el.length !== undefined) { // array-like 
162
                var c = [];
163
                for (var i = 0, len = el.length; i < len; ++i) {
164
                    c[c.length] = Y.Dom.get(el[i]);
97
            if (el) {
98
                if (el[NODE_TYPE] || el.item) { // Node, or NodeList
99
                    return el;
165 100
                }
101

  
102
                if (typeof el === 'string') { // id
103
                    id = el;
104
                    el = document.getElementById(el);
105
                    attr = (el) ? el.attributes : null;
106
                    if (el && attr && attr.id && attr.id.value === id) { // IE: avoid false match on "name" attribute
107
                        return el;
108
                    } else if (el && document.all) { // filter by name
109
                        el = null;
110
                        nodes = document.all[id];
111
                        for (i = 0, len = nodes.length; i < len; ++i) {
112
                            if (nodes[i].id === id) {
113
                                return nodes[i];
114
                            }
115
                        }
116
                    }
117
                    return el;
118
                }
166 119
                
167
                return c;
120
                if (YAHOO.util.Element && el instanceof YAHOO.util.Element) {
121
                    el = el.get('element');
122
                }
123

  
124
                if ('length' in el) { // array-like 
125
                    c = [];
126
                    for (i = 0, len = el.length; i < len; ++i) {
127
                        c[c.length] = Y.Dom.get(el[i]);
128
                    }
129
                    
130
                    return c;
131
                }
132

  
133
                return el; // some other object, just pass it back
168 134
            }
169 135

  
170
            return el; // some other object, just pass it back
136
            return null;
171 137
        },
172 138
    
139
        getComputedStyle: function(el, property) {
140
            if (window[GET_COMPUTED_STYLE]) {
141
                return el[OWNER_DOCUMENT][DEFAULT_VIEW][GET_COMPUTED_STYLE](el, null)[property];
142
            } else if (el[CURRENT_STYLE]) {
143
                return Y.Dom.IE_ComputedStyle.get(el, property);
144
            }
145
        },
146

  
173 147
        /**
174 148
         * Normalizes currentStyle and ComputedStyle.
175 149
         * @method getStyle
......
178 152
         * @return {String | Array} The current value of the style property for the element(s).
179 153
         */
180 154
        getStyle: function(el, property) {
181
            property = toCamel(property);
182
            
183
            var f = function(element) {
184
                return getStyle(element, property);
185
            };
186
            
187
            return Y.Dom.batch(el, f, Y.Dom, true);
155
            return Y.Dom.batch(el, Y.Dom._getStyle, property);
188 156
        },
157

  
158
        // branching at load instead of runtime
159
        _getStyle: function() {
160
            if (window[GET_COMPUTED_STYLE]) { // W3C DOM method
161
                return function(el, property) {
162
                    property = (property === 'float') ? property = 'cssFloat' :
163
                            Y.Dom._toCamel(property);
164

  
165
                    var value = el.style[property],
166
                        computed;
167
                    
168
                    if (!value) {
169
                        computed = el[OWNER_DOCUMENT][DEFAULT_VIEW][GET_COMPUTED_STYLE](el, null);
170
                        if (computed) { // test computed before touching for safari
171
                            value = computed[property];
172
                        }
173
                    }
174
                    
175
                    return value;
176
                };
177
            } else if (documentElement[CURRENT_STYLE]) {
178
                return function(el, property) {                         
179
                    var value;
180

  
181
                    switch(property) {
182
                        case 'opacity' :// IE opacity uses filter
183
                            value = 100;
184
                            try { // will error if no DXImageTransform
185
                                value = el.filters['DXImageTransform.Microsoft.Alpha'].opacity;
186

  
187
                            } catch(e) {
188
                                try { // make sure its in the document
189
                                    value = el.filters('alpha').opacity;
190
                                } catch(err) {
191
                                    YAHOO.log('getStyle: IE filter failed',
192
                                            'error', 'Dom');
193
                                }
194
                            }
195
                            return value / 100;
196
                        case 'float': // fix reserved word
197
                            property = 'styleFloat'; // fall through
198
                        default: 
199
                            property = Y.Dom._toCamel(property);
200
                            value = el[CURRENT_STYLE] ? el[CURRENT_STYLE][property] : null;
201
                            return ( el.style[property] || value );
202
                    }
203
                };
204
            }
205
        }(),
189 206
    
190 207
        /**
191 208
         * Wrapper for setting style properties of HTMLElements.  Normalizes "opacity" across modern browsers.
......
195 212
         * @param {String} val The value to apply to the given property.
196 213
         */
197 214
        setStyle: function(el, property, val) {
198
            property = toCamel(property);
199
            
200
            var f = function(element) {
201
                setStyle(element, property, val);
202
                YAHOO.log('setStyle setting ' + property + ' to ' + val, 'info', 'Dom');
203
                
204
            };
205
            
206
            Y.Dom.batch(el, f, Y.Dom, true);
215
            Y.Dom.batch(el, Y.Dom._setStyle, { prop: property, val: val });
207 216
        },
217

  
218
        _setStyle: function() {
219
            if (isIE) {
220
                return function(el, args) {
221
                    var property = Y.Dom._toCamel(args.prop),
222
                        val = args.val;
223

  
224
                    if (el) {
225
                        switch (property) {
226
                            case 'opacity':
227
                                if ( lang.isString(el.style.filter) ) { // in case not appended
228
                                    el.style.filter = 'alpha(opacity=' + val * 100 + ')';
229
                                    
230
                                    if (!el[CURRENT_STYLE] || !el[CURRENT_STYLE].hasLayout) {
231
                                        el.style.zoom = 1; // when no layout or cant tell
232
                                    }
233
                                }
234
                                break;
235
                            case 'float':
236
                                property = 'styleFloat';
237
                            default:
238
                            el.style[property] = val;
239
                        }
240
                    } else {
241
                        YAHOO.log('element ' + el + ' is undefined', 'error', 'Dom');
242
                    }
243
                };
244
            } else {
245
                return function(el, args) {
246
                    var property = Y.Dom._toCamel(args.prop),
247
                        val = args.val;
248
                    if (el) {
249
                        if (property == 'float') {
250
                            property = 'cssFloat';
251
                        }
252
                        el.style[property] = val;
253
                    } else {
254
                        YAHOO.log('element ' + el + ' is undefined', 'error', 'Dom');
255
                    }
256
                };
257
            }
258

  
259
        }(),
208 260
        
209 261
        /**
210
         * 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).
262
         * Gets the current position of an element based on page coordinates. 
263
         * Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
211 264
         * @method getXY
212
         * @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
265
         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM
266
         * reference, or an Array of IDs and/or HTMLElements
213 267
         * @return {Array} The XY position of the element(s)
214 268
         */
215 269
        getXY: function(el) {
216
            var f = function(el) {
217
                // has to be part of document to have pageXY
218
                if ( (el.parentNode === null || el.offsetParent === null ||
219
                        this.getStyle(el, 'display') == 'none') && el != el.ownerDocument.body) {
220
                    YAHOO.log('getXY failed: element not available', 'error', 'Dom');
221
                    return false;
222
                }
223
                
224
                YAHOO.log('getXY returning ' + getXY(el), 'info', 'Dom');
225
                return getXY(el);
226
            };
227
            
228
            return Y.Dom.batch(el, f, Y.Dom, true);
270
            return Y.Dom.batch(el, Y.Dom._getXY);
229 271
        },
272

  
273
        _canPosition: function(el) {
274
            return ( Y.Dom._getStyle(el, 'display') !== 'none' && Y.Dom._inDoc(el) );
275
        },
276

  
277
        _getXY: function() {
278
            if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
279
                return function(node) {
280
                    var scrollLeft, scrollTop, box, doc,
281
                        off1, off2, mode, bLeft, bTop,
282
                        floor = Math.floor, // TODO: round?
283
                        xy = false;
284

  
285
                    if (Y.Dom._canPosition(node)) {
286
                        box = node[GET_BOUNDING_CLIENT_RECT]();
287
                        doc = node[OWNER_DOCUMENT];
288
                        scrollLeft = Y.Dom.getDocumentScrollLeft(doc);
289
                        scrollTop = Y.Dom.getDocumentScrollTop(doc);
290
                        xy = [floor(box[LEFT]), floor(box[TOP])];
291

  
292
                        if (isIE && UA.ie < 8) { // IE < 8: viewport off by 2
293
                            off1 = 2;
294
                            off2 = 2;
295
                            mode = doc[COMPAT_MODE];
296

  
297
                            if (UA.ie === 6) {
298
                                if (mode !== _BACK_COMPAT) {
299
                                    off1 = 0;
300
                                    off2 = 0;
301
                                }
302
                            }
303
                            
304
                            if ((mode === _BACK_COMPAT)) {
305
                                bLeft = _getComputedStyle(doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH);
306
                                bTop = _getComputedStyle(doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
307
                                if (bLeft !== MEDIUM) {
308
                                    off1 = parseInt(bLeft, 10);
309
                                }
310
                                if (bTop !== MEDIUM) {
311
                                    off2 = parseInt(bTop, 10);
312
                                }
313
                            }
314
                            
315
                            xy[0] -= off1;
316
                            xy[1] -= off2;
317

  
318
                        }
319

  
320
                        if ((scrollTop || scrollLeft)) {
321
                            xy[0] += scrollLeft;
322
                            xy[1] += scrollTop;
323
                        }
324

  
325
                        // gecko may return sub-pixel (non-int) values
326
                        xy[0] = floor(xy[0]);
327
                        xy[1] = floor(xy[1]);
328
                    } else {
329
                        YAHOO.log('getXY failed: element not positionable (either not in a document or not displayed)', 'error', 'Dom');
330
                    }
331

  
332
                    return xy;
333
                };
334
            } else {
335
                return function(node) { // ff2, safari: manually calculate by crawling up offsetParents
336
                    var docScrollLeft, docScrollTop,
337
                        scrollTop, scrollLeft,
338
                        bCheck,
339
                        xy = false,
340
                        parentNode = node;
341

  
342
                    if  (Y.Dom._canPosition(node) ) {
343
                        xy = [node[OFFSET_LEFT], node[OFFSET_TOP]];
344
                        docScrollLeft = Y.Dom.getDocumentScrollLeft(node[OWNER_DOCUMENT]);
345
                        docScrollTop = Y.Dom.getDocumentScrollTop(node[OWNER_DOCUMENT]);
346

  
347
                        // TODO: refactor with !! or just falsey
348
                        bCheck = ((isGecko || UA.webkit > 519) ? true : false);
349

  
350
                        // TODO: worth refactoring for TOP/LEFT only?
351
                        while ((parentNode = parentNode[OFFSET_PARENT])) {
352
                            xy[0] += parentNode[OFFSET_LEFT];
353
                            xy[1] += parentNode[OFFSET_TOP];
354
                            if (bCheck) {
355
                                xy = Y.Dom._calcBorders(parentNode, xy);
356
                            }
357
                        }
358

  
359
                        // account for any scrolled ancestors
360
                        if (Y.Dom._getStyle(node, POSITION) !== FIXED) {
361
                            parentNode = node;
362

  
363
                            while ((parentNode = parentNode[PARENT_NODE]) && parentNode[TAG_NAME]) {
364
                                scrollTop = parentNode[SCROLL_TOP];
365
                                scrollLeft = parentNode[SCROLL_LEFT];
366

  
367
                                //Firefox does something funky with borders when overflow is not visible.
368
                                if (isGecko && (Y.Dom._getStyle(parentNode, 'overflow') !== 'visible')) {
369
                                        xy = Y.Dom._calcBorders(parentNode, xy);
370
                                }
371

  
372
                                if (scrollTop || scrollLeft) {
373
                                    xy[0] -= scrollLeft;
374
                                    xy[1] -= scrollTop;
375
                                }
376
                            }
377
                            xy[0] += docScrollLeft;
378
                            xy[1] += docScrollTop;
379

  
380
                        } else {
381
                            //Fix FIXED position -- add scrollbars
382
                            if (isOpera) {
383
                                xy[0] -= docScrollLeft;
384
                                xy[1] -= docScrollTop;
385
                            } else if (isSafari || isGecko) {
386
                                xy[0] += docScrollLeft;
387
                                xy[1] += docScrollTop;
388
                            }
389
                        }
390
                        //Round the numbers so we get sane data back
391
                        xy[0] = Math.floor(xy[0]);
392
                        xy[1] = Math.floor(xy[1]);
393
                    } else {
394
                        YAHOO.log('getXY failed: element not positionable (either not in a document or not displayed)', 'error', 'Dom');
395
                    }
396
                    return xy;                
397
                };
398
            }
399
        }(), // NOTE: Executing for loadtime branching
230 400
        
231 401
        /**
232 402
         * 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).
......
265 435
         * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
266 436
         */
267 437
        setXY: function(el, pos, noRetry) {
268
            var f = function(el) {
269
                var style_pos = this.getStyle(el, 'position');
270
                if (style_pos == 'static') { // default to relative
271
                    this.setStyle(el, 'position', 'relative');
272
                    style_pos = 'relative';
273
                }
438
            Y.Dom.batch(el, Y.Dom._setXY, { pos: pos, noRetry: noRetry });
439
        },
274 440

  
275
                var pageXY = this.getXY(el);
276
                if (pageXY === false) { // has to be part of doc to have pageXY
277
                    YAHOO.log('setXY failed: element not available', 'error', 'Dom');
278
                    return false; 
279
                }
280
                
281
                var delta = [ // assuming pixels; if not we will have to retry
282
                    parseInt( this.getStyle(el, 'left'), 10 ),
283
                    parseInt( this.getStyle(el, 'top'), 10 )
284
                ];
285
            
286
                if ( isNaN(delta[0]) ) {// in case of 'auto'
287
                    delta[0] = (style_pos == 'relative') ? 0 : el.offsetLeft;
288
                } 
289
                if ( isNaN(delta[1]) ) { // in case of 'auto'
290
                    delta[1] = (style_pos == 'relative') ? 0 : el.offsetTop;
291
                } 
441
        _setXY: function(node, args) {
442
            var pos = Y.Dom._getStyle(node, POSITION),
443
                setStyle = Y.Dom.setStyle,
444
                xy = args.pos,
445
                noRetry = args.noRetry,
446

  
447
                delta = [ // assuming pixels; if not we will have to retry
448
                    parseInt( Y.Dom.getComputedStyle(node, LEFT), 10 ),
449
                    parseInt( Y.Dom.getComputedStyle(node, TOP), 10 )
450
                ],
451

  
452
                currentXY,
453
                newXY;
292 454
        
293
                if (pos[0] !== null) { el.style.left = pos[0] - pageXY[0] + delta[0] + 'px'; }
294
                if (pos[1] !== null) { el.style.top = pos[1] - pageXY[1] + delta[1] + 'px'; }
295
              
296
                if (!noRetry) {
297
                    var newXY = this.getXY(el);
455
            if (pos == 'static') { // default to relative
456
                pos = RELATIVE;
457
                setStyle(node, POSITION, pos);
458
            }
298 459

  
299
                    // if retry is true, try one more time if we miss 
300
                   if ( (pos[0] !== null && newXY[0] != pos[0]) || 
301
                        (pos[1] !== null && newXY[1] != pos[1]) ) {
302
                       this.setXY(el, pos, true);
303
                   }
304
                }        
305
        
306
                YAHOO.log('setXY setting position to ' + pos, 'info', 'Dom');
307
            };
460
            currentXY = Y.Dom._getXY(node);
461

  
462
            if (!xy || currentXY === false) { // has to be part of doc to have xy
463
                YAHOO.log('xy failed: node not available', 'error', 'Node');
464
                return false; 
465
            }
308 466
            
309
            Y.Dom.batch(el, f, Y.Dom, true);
467
            if ( isNaN(delta[0]) ) {// in case of 'auto'
468
                delta[0] = (pos == RELATIVE) ? 0 : node[OFFSET_LEFT];
469
            } 
470
            if ( isNaN(delta[1]) ) { // in case of 'auto'
471
                delta[1] = (pos == RELATIVE) ? 0 : node[OFFSET_TOP];
472
            } 
473

  
474
            if (xy[0] !== null) { // from setX
475
                setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
476
            }
477

  
478
            if (xy[1] !== null) { // from setY
479
                setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
480
            }
481
          
482
            if (!noRetry) {
483
                newXY = Y.Dom._getXY(node);
484

  
485
                // if retry is true, try one more time if we miss 
486
               if ( (xy[0] !== null && newXY[0] != xy[0]) || 
487
                    (xy[1] !== null && newXY[1] != xy[1]) ) {
488
                   Y.Dom._setXY(node, { pos: xy, noRetry: true });
489
               }
490
            }        
491

  
492
            YAHOO.log('setXY setting position to ' + xy, 'info', 'Node');
310 493
        },
311 494
        
312 495
        /**
......
340 523
         */
341 524
        getRegion: function(el) {
342 525
            var f = function(el) {
343
                if ( (el.parentNode === null || el.offsetParent === null ||
344
                        this.getStyle(el, 'display') == 'none') && el != document.body) {
345
                    YAHOO.log('getRegion failed: element not available', 'error', 'Dom');
346
                    return false;
526
                var region = false;
527
                if ( Y.Dom._canPosition(el) ) {
528
                    region = Y.Region.getRegion(el);
529
                    YAHOO.log('getRegion returning ' + region, 'info', 'Dom');
530
                } else {
531
                    YAHOO.log('getRegion failed: element not positionable (either not in a document or not displayed)', 'error', 'Dom');
347 532
                }
348 533

  
349
                var region = Y.Region.getRegion(el);
350
                YAHOO.log('getRegion returning ' + region, 'info', 'Dom');
351 534
                return region;
352 535
            };
353 536
            
......
375 558
        },
376 559

  
377 560
        /**
378
         * Returns a array of HTMLElements with the given class.
561
         * Returns an array of HTMLElements with the given class.
379 562
         * For optimized performance, include a tag and/or root node when possible.
563
         * Note: This method operates against a live collection, so modifying the 
564
         * collection in the callback (removing/appending nodes, etc.) will have
565
         * side effects.  Instead you should iterate the returned nodes array,
566
         * as you would with the native "getElementsByTagName" method. 
380 567
         * @method getElementsByClassName
381 568
         * @param {String} className The class name to match against
382 569
         * @param {String} tag (optional) The tag name of the elements being collected
383
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
570
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point.
571
         * This element is not included in the className scan.
384 572
         * @param {Function} apply (optional) A function to apply to each element when found 
573
         * @param {Any} o (optional) An optional arg that is passed to the supplied method
574
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
385 575
         * @return {Array} An array of elements that have the given class name
386 576
         */
387
        getElementsByClassName: function(className, tag, root, apply) {
577
        getElementsByClassName: function(className, tag, root, apply, o, overrides) {
388 578
            tag = tag || '*';
389 579
            root = (root) ? Y.Dom.get(root) : null || document; 
390 580
            if (!root) {
......
393 583

  
394 584
            var nodes = [],
395 585
                elements = root.getElementsByTagName(tag),
396
                re = getClassRegEx(className);
586
                hasClass = Y.Dom.hasClass;
397 587

  
398 588
            for (var i = 0, len = elements.length; i < len; ++i) {
399
                if ( re.test(elements[i].className) ) {
589
                if ( hasClass(elements[i], className) ) {
400 590
                    nodes[nodes.length] = elements[i];
401
                    if (apply) {
402
                        apply.call(elements[i], elements[i]);
403
                    }
404 591
                }
405 592
            }
406 593
            
594
            if (apply) {
595
                Y.Dom.batch(nodes, apply, o, overrides);
596
            }
597

  
407 598
            return nodes;
408 599
        },
409 600

  
......
415 606
         * @return {Boolean | Array} A boolean value or array of boolean values
416 607
         */
417 608
        hasClass: function(el, className) {
418
            var re = getClassRegEx(className);
609
            return Y.Dom.batch(el, Y.Dom._hasClass, className);
610
        },
419 611

  
420
            var f = function(el) {
421
                YAHOO.log('hasClass returning ' + re.test(el.className), 'info', 'Dom');
422
                return re.test(el.className);
423
            };
612
        _hasClass: function(el, className) {
613
            var ret = false,
614
                current;
424 615
            
425
            return Y.Dom.batch(el, f, Y.Dom, true);
616
            if (el && className) {
617
                current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
618
                if (className.exec) {
619
                    ret = className.test(current);
620
                } else {
621
                    ret = className && (SPACE + current + SPACE).
622
                        indexOf(SPACE + className + SPACE) > -1;
623
                }
624
            } else {
625
                YAHOO.log('hasClass called with invalid arguments', 'warn', 'Dom');
626
            }
627

  
628
            return ret;
426 629
        },
427 630
    
428 631
        /**
......
433 636
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
434 637
         */
435 638
        addClass: function(el, className) {
436
            var f = function(el) {
437
                if (this.hasClass(el, className)) {
438
                    return false; // already present
639
            return Y.Dom.batch(el, Y.Dom._addClass, className);
640
        },
641

  
642
        _addClass: function(el, className) {
643
            var ret = false,
644
                current;
645

  
646
            if (el && className) {
647
                current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
648
                if ( !Y.Dom._hasClass(el, className) ) {
649
                    Y.Dom.setAttribute(el, CLASS_NAME, trim(current + SPACE + className));
650
                    ret = true;
439 651
                }
440
                
441
                YAHOO.log('addClass adding ' + className, 'info', 'Dom');
442
                
443
                el.className = YAHOO.lang.trim([el.className, className].join(' '));
444
                return true;
445
            };
446
            
447
            return Y.Dom.batch(el, f, Y.Dom, true);
652
            } else {
653
                YAHOO.log('addClass called with invalid arguments', 'warn', 'Dom');
654
            }
655

  
656
            return ret;
448 657
        },
449 658
    
450 659
        /**
......
455 664
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
456 665
         */
457 666
        removeClass: function(el, className) {
458
            var re = getClassRegEx(className);
459
            
460
            var f = function(el) {
461
                if (!this.hasClass(el, className)) {
462
                    return false; // not present
463
                }                 
667
            return Y.Dom.batch(el, Y.Dom._removeClass, className);
668
        },
669
        
670
        _removeClass: function(el, className) {
671
            var ret = false,
672
                current,
673
                newClass,
674
                attr;
464 675

  
465
                YAHOO.log('removeClass removing ' + className, 'info', 'Dom');
466
                
467
                var c = el.className;
468
                el.className = c.replace(re, ' ');
469
                if ( this.hasClass(el, className) ) { // in case of multiple adjacent
470
                    this.removeClass(el, className);
676
            if (el && className) {
677
                current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
678
                Y.Dom.setAttribute(el, CLASS_NAME, current.replace(Y.Dom._getClassRegex(className), EMPTY));
679

  
680
                newClass = Y.Dom._getAttribute(el, CLASS_NAME);
681
                if (current !== newClass) { // else nothing changed
682
                    Y.Dom.setAttribute(el, CLASS_NAME, trim(newClass)); // trim after comparing to current class
683
                    ret = true;
684

  
685
                    if (Y.Dom._getAttribute(el, CLASS_NAME) === '') { // remove class attribute if empty
686
                        attr = (el.hasAttribute && el.hasAttribute(_CLASS)) ? _CLASS : CLASS_NAME;
687
                        YAHOO.log('removeClass removing empty class attribute', 'info', 'Dom');
688
                        el.removeAttribute(attr);
689
                    }
471 690
                }
472 691

  
473
                el.className = YAHOO.lang.trim(el.className); // remove any trailing spaces
474
                return true;
475
            };
476
            
477
            return Y.Dom.batch(el, f, Y.Dom, true);
692
            } else {
693
                YAHOO.log('removeClass called with invalid arguments', 'warn', 'Dom');
694
            }
695

  
696
            return ret;
478 697
        },
479 698
        
480 699
        /**
......
487 706
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
488 707
         */
489 708
        replaceClass: function(el, oldClassName, newClassName) {
490
            if (!newClassName || oldClassName === newClassName) { // avoid infinite loop
491
                return false;
492
            }
493
            
494
            var re = getClassRegEx(oldClassName);
709
            return Y.Dom.batch(el, Y.Dom._replaceClass, { from: oldClassName, to: newClassName });
710
        },
495 711

  
496
            var f = function(el) {
497
                YAHOO.log('replaceClass replacing ' + oldClassName + ' with ' + newClassName, 'info', 'Dom');
498
            
499
                if ( !this.hasClass(el, oldClassName) ) {
500
                    this.addClass(el, newClassName); // just add it if nothing to replace
501
                    return true; // NOTE: return
502
                }
503
            
504
                el.className = el.className.replace(re, ' ' + newClassName + ' ');
712
        _replaceClass: function(el, classObj) {
713
            var className,
714
                from,
715
                to,
716
                ret = false,
717
                current;
505 718

  
506
                if ( this.hasClass(el, oldClassName) ) { // in case of multiple adjacent
507
                    this.replaceClass(el, oldClassName, newClassName);
719
            if (el && classObj) {
720
                from = classObj.from;
721
                to = classObj.to;
722

  
723
                if (!to) {
724
                    ret = false;
725
                }  else if (!from) { // just add if no "from"
726
                    ret = Y.Dom._addClass(el, classObj.to);
727
                } else if (from !== to) { // else nothing to replace
728
                    // May need to lead with DBLSPACE?
729
                    current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
730
                    className = (SPACE + current.replace(Y.Dom._getClassRegex(from), SPACE + to)).
731
                               split(Y.Dom._getClassRegex(to));
732

  
733
                    // insert to into what would have been the first occurrence slot
734
                    className.splice(1, 0, SPACE + to);
735
                    Y.Dom.setAttribute(el, CLASS_NAME, trim(className.join(EMPTY)));
736
                    ret = true;
508 737
                }
738
            } else {
739
                YAHOO.log('replaceClass called with invalid arguments', 'warn', 'Dom');
740
            }
509 741

  
510
                el.className = YAHOO.lang.trim(el.className); // remove any trailing spaces
511
                return true;
512
            };
513
            
514
            return Y.Dom.batch(el, f, Y.Dom, true);
742
            return ret;
515 743
        },
516 744
        
517 745
        /**
......
528 756
                if (el && el.id) { // do not override existing ID
529 757
                    YAHOO.log('generateId returning existing id ' + el.id, 'info', 'Dom');
530 758
                    return el.id;
531
                } 
759
                }
532 760

  
533
                var id = prefix + id_counter++;
761
                var id = prefix + YAHOO.env._id_counter++;
534 762
                YAHOO.log('generateId generating ' + id, 'info', 'Dom');
535 763

  
536 764
                if (el) {
765
                    if (el[OWNER_DOCUMENT] && el[OWNER_DOCUMENT].getElementById(id)) { // in case one already exists
766
                        // use failed id plus prefix to help ensure uniqueness
767
                        return Y.Dom.generateId(el, id + prefix);
768
                    }
537 769
                    el.id = id;
538 770
                }
539 771
                
......
555 787
            haystack = Y.Dom.get(haystack);
556 788
            needle = Y.Dom.get(needle);
557 789
            
558
            if (!haystack || !needle) {
559
                return false;
560
            }
790
            var ret = false;
561 791

  
562
            if (haystack.contains && needle.nodeType && !isSafari) { // safari contains is broken
563
                YAHOO.log('isAncestor returning ' + haystack.contains(needle), 'info', 'Dom');
564
                return haystack.contains(needle);
792
            if ( (haystack && needle) && (haystack[NODE_TYPE] && needle[NODE_TYPE]) ) {
793
                if (haystack.contains && haystack !== needle) { // contains returns true when equal
794
                    ret = haystack.contains(needle);
795
                }
796
                else if (haystack.compareDocumentPosition) { // gecko
797
                    ret = !!(haystack.compareDocumentPosition(needle) & 16);
798
                }
799
            } else {
800
                YAHOO.log('isAncestor failed; invalid input: ' + haystack + ',' + needle, 'error', 'Dom');
565 801
            }
566
            else if ( haystack.compareDocumentPosition && needle.nodeType ) {
567
                YAHOO.log('isAncestor returning ' + !!(haystack.compareDocumentPosition(needle) & 16), 'info', 'Dom');
568
                return !!(haystack.compareDocumentPosition(needle) & 16);
569
            } else if (needle.nodeType) {
570
                // fallback to crawling up (safari)
571
                return !!this.getAncestorBy(needle, function(el) {
572
                    return el == haystack; 
573
                }); 
574
            }
575
            YAHOO.log('isAncestor failed; most likely needle is not an HTMLElement', 'error', 'Dom');
576
            return false;
802
            YAHOO.log('isAncestor(' + haystack + ',' + needle + ' returning ' + ret, 'info', 'Dom');
803
            return ret;
577 804
        },
578 805
        
579 806
        /**
580 807
         * Determines whether an HTMLElement is present in the current document.
581 808
         * @method inDocument         
582 809
         * @param {String | HTMLElement} el The element to search for
810
         * @param {Object} doc An optional document to search, defaults to element's owner document 
583 811
         * @return {Boolean} Whether or not the element is present in the current document
584 812
         */
585
        inDocument: function(el) {
586
            return this.isAncestor(document.documentElement, el);
813
        inDocument: function(el, doc) {
814
            return Y.Dom._inDoc(Y.Dom.get(el), doc);
587 815
        },
816

  
817
        _inDoc: function(el, doc) {
818
            var ret = false;
819
            if (el && el[TAG_NAME]) {
820
                doc = doc || el[OWNER_DOCUMENT]; 
821
                ret = Y.Dom.isAncestor(doc[DOCUMENT_ELEMENT], el);
822
            } else {
823
                YAHOO.log('inDocument failed: invalid input', 'error', 'Dom');
824
            }
825
            return ret;
826
        },
588 827
        
589 828
        /**
590
         * Returns a array of HTMLElements that pass the test applied by supplied boolean method.
829
         * Returns an array of HTMLElements that pass the test applied by supplied boolean method.
591 830
         * For optimized performance, include a tag and/or root node when possible.
831
         * Note: This method operates against a live collection, so modifying the 
832
         * collection in the callback (removing/appending nodes, etc.) will have
833
         * side effects.  Instead you should iterate the returned nodes array,
834
         * as you would with the native "getElementsByTagName" method. 
592 835
         * @method getElementsBy
593 836
         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
594 837
         * @param {String} tag (optional) The tag name of the elements being collected
595 838
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
596 839
         * @param {Function} apply (optional) A function to apply to each element when found 
840
         * @param {Any} o (optional) An optional arg that is passed to the supplied method
841
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
597 842
         * @return {Array} Array of HTMLElements
598 843
         */
599
        getElementsBy: function(method, tag, root, apply) {
844
        getElementsBy: function(method, tag, root, apply, o, overrides, firstOnly) {
600 845
            tag = tag || '*';
601 846
            root = (root) ? Y.Dom.get(root) : null || document; 
602 847

  
......
609 854
            
610 855
            for (var i = 0, len = elements.length; i < len; ++i) {
611 856
                if ( method(elements[i]) ) {
612
                    nodes[nodes.length] = elements[i];
613
                    if (apply) {
614
                        apply(elements[i]);
857
                    if (firstOnly) {
858
                        nodes = elements[i]; 
859
                        break;
860
                    } else {
861
                        nodes[nodes.length] = elements[i];
615 862
                    }
616 863
                }
617 864
            }
618 865

  
866
            if (apply) {
867
                Y.Dom.batch(nodes, apply, o, overrides);
868
            }
869

  
619 870
            YAHOO.log('getElementsBy returning ' + nodes, 'info', 'Dom');
620 871
            
621 872
            return nodes;
622 873
        },
623 874
        
624 875
        /**
876
         * Returns the first HTMLElement that passes the test applied by the supplied boolean method.
877
         * @method getElementBy
878
         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
879
         * @param {String} tag (optional) The tag name of the elements being collected
880
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
881
         * @return {HTMLElement}
882
         */
883
        getElementBy: function(method, tag, root) {
884
            return Y.Dom.getElementsBy(method, tag, root, null, null, null, true); 
885
        },
886

  
887
        /**
625 888
         * Runs the supplied method against each item in the Collection/Array.
626 889
         * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) ).
627 890
         * @method batch
628 891
         * @param {String | HTMLElement | Array} el (optional) An element or array of elements to apply the method to
629 892
         * @param {Function} method The method to apply to the element(s)
630 893
         * @param {Any} o (optional) An optional arg that is passed to the supplied method
631
         * @param {Boolean} override (optional) Whether or not to override the scope of "method" with "o"
894
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
632 895
         * @return {Any | Array} The return value(s) from the supplied method
633 896
         */
634
        batch: function(el, method, o, override) {
635
            el = (el && (el.tagName || el.item)) ? el : Y.Dom.get(el); // skip get() when possible
897
        batch: function(el, method, o, overrides) {
898
            var collection = [],
899
                scope = (overrides) ? o : window;
900
                
901
            el = (el && (el[TAG_NAME] || el.item)) ? el : Y.Dom.get(el); // skip get() when possible
902
            if (el && method) {
903
                if (el[TAG_NAME] || el.length === undefined) { // element or not array-like 
904
                    return method.call(scope, el, o);
905
                } 
636 906

  
637
            if (!el || !method) {
638
                YAHOO.log('batch failed: invalid arguments', 'error', 'Dom');
907
                for (var i = 0; i < el.length; ++i) {
908
                    collection[collection.length] = method.call(scope, el[i], o);
909
                }
910
            } else {
911
                YAHOO.log('batch called with invalid arguments', 'warn', 'Dom');
639 912
                return false;
640 913
            } 
641
            var scope = (override) ? o : window;
642
            
643
            if (el.tagName || el.length === undefined) { // element or not array-like 
644
                return method.call(scope, el, o);
645
            } 
646

  
647
            var collection = [];
648
            
649
            for (var i = 0, len = el.length; i < len; ++i) {
650
                collection[collection.length] = method.call(scope, el[i], o);
651
            }
652
            
653 914
            return collection;
654 915
        },
655 916
        
......
659 920
         * @return {Int} The height of the actual document (which includes the body and its margin).
660 921
         */
661 922
        getDocumentHeight: function() {
662
            var scrollHeight = (document.compatMode != 'CSS1Compat') ? document.body.scrollHeight : document.documentElement.scrollHeight;
923
            var scrollHeight = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollHeight : documentElement.scrollHeight,
924
                h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
663 925

  
664
            var h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
665 926
            YAHOO.log('getDocumentHeight returning ' + h, 'info', 'Dom');
666 927
            return h;
667 928
        },
......
672 933
         * @return {Int} The width of the actual document (which includes the body and its margin).
673 934
         */
674 935
        getDocumentWidth: function() {
675
            var scrollWidth = (document.compatMode != 'CSS1Compat') ? document.body.scrollWidth : document.documentElement.scrollWidth;
676
            var w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
936
            var scrollWidth = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollWidth : documentElement.scrollWidth,
937
                w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
677 938
            YAHOO.log('getDocumentWidth returning ' + w, 'info', 'Dom');
678 939
            return w;
679 940
        },
......
684 945
         * @return {Int} The height of the viewable area of the page (excludes scrollbars).
685 946
         */
686 947
        getViewportHeight: function() {
687
            var height = self.innerHeight; // Safari, Opera
688
            var mode = document.compatMode;
948
            var height = self.innerHeight, // Safari, Opera
949
                mode = document[COMPAT_MODE];
689 950
        
690 951
            if ( (mode || isIE) && !isOpera ) { // IE, Gecko
691
                height = (mode == 'CSS1Compat') ?
692
                        document.documentElement.clientHeight : // Standards
952
                height = (mode == CSS1_COMPAT) ?
953
                        documentElement.clientHeight : // Standards
693 954
                        document.body.clientHeight; // Quirks
694 955
            }
695 956
        
......
704 965
         */
705 966
        
706 967
        getViewportWidth: function() {
707
            var width = self.innerWidth;  // Safari
708
            var mode = document.compatMode;
968
            var width = self.innerWidth,  // Safari
969
                mode = document[COMPAT_MODE];
709 970
            
710 971
            if (mode || isIE) { // IE, Gecko, Opera
711
                width = (mode == 'CSS1Compat') ?
712
                        document.documentElement.clientWidth : // Standards
972
                width = (mode == CSS1_COMPAT) ?
973
                        documentElement.clientWidth : // Standards
713 974
                        document.body.clientWidth; // Quirks
714 975
            }
715 976
            YAHOO.log('getViewportWidth returning ' + width, 'info', 'Dom');
......
725 986
         * @return {Object} HTMLElement or null if not found
726 987
         */
727 988
        getAncestorBy: function(node, method) {
728
            while (node = node.parentNode) { // NOTE: assignment
729
                if ( testElement(node, method) ) {
989
            while ( (node = node[PARENT_NODE]) ) { // NOTE: assignment
990
                if ( Y.Dom._testElement(node, method) ) {
730 991
                    YAHOO.log('getAncestorBy returning ' + node, 'info', 'Dom');
731 992
                    return node;
732 993
                }
......
767 1028
                return null;
768 1029
            }
769 1030
            var method = function(el) {
770
                 return el.tagName && el.tagName.toUpperCase() == tagName.toUpperCase();
1031
                 return el[TAG_NAME] && el[TAG_NAME].toUpperCase() == tagName.toUpperCase();
771 1032
            };
772 1033

  
773 1034
            return Y.Dom.getAncestorBy(node, method);
......
786 1047
        getPreviousSiblingBy: function(node, method) {
787 1048
            while (node) {
788 1049
                node = node.previousSibling;
789
                if ( testElement(node, method) ) {
1050
                if ( Y.Dom._testElement(node, method) ) {
790 1051
                    return node;
791 1052
                }
792 1053
            }
......
822 1083
        getNextSiblingBy: function(node, method) {
823 1084
            while (node) {
824 1085
                node = node.nextSibling;
825
                if ( testElement(node, method) ) {
1086
                if ( Y.Dom._testElement(node, method) ) {
826 1087
                    return node;
827 1088
                }
828 1089
            }
......
854 1115
         * @return {Object} HTMLElement or null if not found
855 1116
         */
856 1117
        getFirstChildBy: function(node, method) {
857
            var child = ( testElement(node.firstChild, method) ) ? node.firstChild : null;
1118
            var child = ( Y.Dom._testElement(node.firstChild, method) ) ? node.firstChild : null;
858 1119
            return child || Y.Dom.getNextSiblingBy(node.firstChild, method);
859 1120
        }, 
860 1121

  
......
886 1147
                YAHOO.log('getLastChild failed: invalid node argument', 'error', 'Dom');
887 1148
                return null;
888 1149
            }
889
            var child = ( testElement(node.lastChild, method) ) ? node.lastChild : null;
1150
            var child = ( Y.Dom._testElement(node.lastChild, method) ) ? node.lastChild : null;
890 1151
            return child || Y.Dom.getPreviousSiblingBy(node.lastChild, method);
891 1152
        }, 
892 1153

  
......
910 1171
         * @return {Array} A static array of HTMLElements
911 1172
         */
912 1173
        getChildrenBy: function(node, method) {
913
            var child = Y.Dom.getFirstChildBy(node, method);
914
            var children = child ? [child] : [];
1174
            var child = Y.Dom.getFirstChildBy(node, method),
1175
                children = child ? [child] : [];
915 1176

  
916 1177
            Y.Dom.getNextSiblingBy(child, function(node) {
917 1178
                if ( !method || method(node) ) {
......
937 1198

  
938 1199
            return Y.Dom.getChildrenBy(node);
939 1200
        },
940
 
1201

  
941 1202
        /**
942 1203
         * Returns the left scroll value of the document 
943 1204
         * @method getDocumentScrollLeft
......
946 1207
         */
947 1208
        getDocumentScrollLeft: function(doc) {
948 1209
            doc = doc || document;
949
            return Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
1210
            return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
950 1211
        }, 
951 1212

  
952 1213
        /**
......
957 1218
         */
958 1219
        getDocumentScrollTop: function(doc) {
959 1220
            doc = doc || document;
960
            return Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
1221
            return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
961 1222
        },
962 1223

  
963 1224
        /**
......
971 1232
            newNode = Y.Dom.get(newNode); 
972 1233
            referenceNode = Y.Dom.get(referenceNode); 
973 1234
            
974
            if (!newNode || !referenceNode || !referenceNode.parentNode) {
1235
            if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
975 1236
                YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'Dom');
976 1237
                return null;
977 1238
            }       
978 1239

  
979
            return referenceNode.parentNode.insertBefore(newNode, referenceNode); 
1240
            return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode); 
980 1241
        },
981 1242

  
982 1243
        /**
......
990 1251
            newNode = Y.Dom.get(newNode); 
991 1252
            referenceNode = Y.Dom.get(referenceNode); 
992 1253
            
993
            if (!newNode || !referenceNode || !referenceNode.parentNode) {
1254
            if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
994 1255
                YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'Dom');
995 1256
                return null;
996 1257
            }       
997 1258

  
998 1259
            if (referenceNode.nextSibling) {
999
                return referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); 
1260
                return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode.nextSibling); 
1000 1261
            } else {
1001
                return referenceNode.parentNode.appendChild(newNode);
1262
                return referenceNode[PARENT_NODE].appendChild(newNode);
1002 1263
            }
1003 1264
        },
1004 1265

  
......
1014 1275
                b = Y.Dom.getViewportHeight() + t;
1015 1276

  
1016 1277
            return new Y.Region(t, r, b, l);
1017
        }
1018
    };
1019
    
1020
    var getXY = function() {
1021
        if (document.documentElement.getBoundingClientRect) { // IE
1022
            return function(el) {
1023
                var box = el.getBoundingClientRect();
1278
        },
1024 1279

  
1025
                var rootNode = el.ownerDocument;
1026
                return [box.left + Y.Dom.getDocumentScrollLeft(rootNode), box.top +
1027
                        Y.Dom.getDocumentScrollTop(rootNode)];
1028
            };
1029
        } else {
1030
            return function(el) { // manually calculate by crawling up offsetParents
1031
                var pos = [el.offsetLeft, el.offsetTop];
1032
                var parentNode = el.offsetParent;
1280
        /**
1281
         * Provides a normalized attribute interface. 
1282
         * @method setAttribute
1283
         * @param {String | HTMLElement} el The target element for the attribute.
1284
         * @param {String} attr The attribute to set.
1285
         * @param {String} val The value of the attribute.
1286
         */
1287
        setAttribute: function(el, attr, val) {
1288
            Y.Dom.batch(el, Y.Dom._setAttribute, { attr: attr, val: val });
1289
        },
1033 1290

  
1034
                // safari: subtract body offsets if el is abs (or any offsetParent), unless body is offsetParent
1035
                var accountForBody = (isSafari &&
1036
                        Y.Dom.getStyle(el, 'position') == 'absolute' &&
1037
                        el.offsetParent == el.ownerDocument.body);
1291
        _setAttribute: function(el, args) {
1292
            var attr = Y.Dom._toCamel(args.attr),
1293
                val = args.val;
1038 1294

  
1039
                if (parentNode != el) {
1040
                    while (parentNode) {
1041
                        pos[0] += parentNode.offsetLeft;
1042
                        pos[1] += parentNode.offsetTop;
1043
                        if (!accountForBody && isSafari && 
1044
                                Y.Dom.getStyle(parentNode,'position') == 'absolute' ) { 
1045
                            accountForBody = true;
1046
                        }
1047
                        parentNode = parentNode.offsetParent;
1048
                    }
1295
            if (el && el.setAttribute) {
1296
                if (Y.Dom.DOT_ATTRIBUTES[attr]) {
1297
                    el[attr] = val;
1298
                } else {
1299
                    attr = Y.Dom.CUSTOM_ATTRIBUTES[attr] || attr;
1300
                    el.setAttribute(attr, val);
1049 1301
                }
1302
            } else {
1303
                YAHOO.log('setAttribute method not available for ' + el, 'error', 'Dom');
1304
            }
1305
        },
1050 1306

  
1051
                if (accountForBody) { //safari doubles in this case
1052
                    pos[0] -= el.ownerDocument.body.offsetLeft;
1053
                    pos[1] -= el.ownerDocument.body.offsetTop;
1054
                } 
1055
                parentNode = el.parentNode;
1307
        /**
1308
         * Provides a normalized attribute interface. 
1309
         * @method getAttribute
1310
         * @param {String | HTMLElement} el The target element for the attribute.
1311
         * @param {String} attr The attribute to get.
1312
         * @return {String} The current value of the attribute. 
1313
         */
1314
        getAttribute: function(el, attr) {
1315
            return Y.Dom.batch(el, Y.Dom._getAttribute, attr);
1316
        },
1056 1317

  
1057
                // account for any scrolled ancestors
1058
                while ( parentNode.tagName && !patterns.ROOT_TAG.test(parentNode.tagName) ) 
1059
                {
1060
                   // work around opera inline/table scrollLeft/Top bug
1061
                   if (Y.Dom.getStyle(parentNode, 'display').search(/^inline|table-row.*$/i)) { 
1062
                        pos[0] -= parentNode.scrollLeft;
1063
                        pos[1] -= parentNode.scrollTop;
1318

  
1319
        _getAttribute: function(el, attr) {
1320
            var val;
1321
            attr = Y.Dom.CUSTOM_ATTRIBUTES[attr] || attr;
1322

  
1323
            if (el && el.getAttribute) {
1324
                val = el.getAttribute(attr, 2);
1325
            } else {
1326
                YAHOO.log('getAttribute method not available for ' + el, 'error', 'Dom');
1327
            }
1328

  
1329
            return val;
1330
        },
1331

  
1332
        _toCamel: function(property) {
1333
            var c = propertyCache;
1334

  
1335
            function tU(x,l) {
1336
                return l.toUpperCase();
1337
            }
1338

  
1339
            return c[property] || (c[property] = property.indexOf('-') === -1 ? 
1340
                                    property :
1341
                                    property.replace( /-([a-z])/gi, tU ));
1342
        },
1343

  
1344
        _getClassRegex: function(className) {
1345
            var re;
1346
            if (className !== undefined) { // allow empty string to pass
1347
                if (className.exec) { // already a RegExp
1348
                    re = className;
1349
                } else {
1350
                    re = reCache[className];
1351
                    if (!re) {
1352
                        // escape special chars (".", "[", etc.)
1353
                        className = className.replace(Y.Dom._patterns.CLASS_RE_TOKENS, '\\$1');
1354
                        re = reCache[className] = new RegExp(C_START + className + C_END, G);
1064 1355
                    }
1065
                    
1066
                    parentNode = parentNode.parentNode; 
1067 1356
                }
1357
            }
1358
            return re;
1359
        },
1068 1360

  
1069
                return pos;
1070
            };
1361
        _patterns: {
1362
            ROOT_TAG: /^body|html$/i, // body for quirks mode, html for standards,
1363
            CLASS_RE_TOKENS: /([\.\(\)\^\$\*\+\?\|\[\]\{\}\\])/g
1364
        },
1365

  
1366

  
1367
        _testElement: function(node, method) {
1368
            return node && node[NODE_TYPE] == 1 && ( !method || method(node) );
1369
        },
1370

  
1371
        _calcBorders: function(node, xy2) {
1372
            var t = parseInt(Y.Dom[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
1373
                l = parseInt(Y.Dom[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
1374
            if (isGecko) {
1375
                if (RE_TABLE.test(node[TAG_NAME])) {
1376
                    t = 0;
1377
                    l = 0;
1378
                }
1379
            }
1380
            xy2[0] += l;
1381
            xy2[1] += t;
1382
            return xy2;
1071 1383
        }
1072
    }() // NOTE: Executing for loadtime branching
1384
    };
1385
        
1386
    var _getComputedStyle = Y.Dom[GET_COMPUTED_STYLE];
1387
    // fix opera computedStyle default color unit (convert to rgb)
1388
    if (UA.opera) {
1389
        Y.Dom[GET_COMPUTED_STYLE] = function(node, att) {
1390
            var val = _getComputedStyle(node, att);
1391
            if (RE_COLOR.test(att)) {
1392
                val = Y.Dom.Color.toRGB(val);
1393
            }
1394

  
1395
            return val;
1396
        };
1397

  
1398
    }
1399

  
1400
    // safari converts transparent to rgba(), others use "transparent"
1401
    if (UA.webkit) {
1402
        Y.Dom[GET_COMPUTED_STYLE] = function(node, att) {
1403
            var val = _getComputedStyle(node, att);
1404

  
1405
            if (val === 'rgba(0, 0, 0, 0)') {
1406
                val = 'transparent'; 
1407
            }
1408

  
1409
            return val;
1410
        };
1411

  
1412
    }
1413

  
1414
    if (UA.ie && UA.ie >= 8 && document.documentElement.hasAttribute) { // IE 8 standards
1415
        Y.Dom.DOT_ATTRIBUTES.type = true; // IE 8 errors on input.setAttribute('type')
1416
    }
1073 1417
})();
1074 1418
/**
1075 1419
 * A region is a representation of an object on a grid.  It is defined
......
1093 1437
    this.top = t;
1094 1438
    
1095 1439
    /**
1440
     * The region's top extent
1441
     * @property y
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff