Project

General

Profile

« Previous | Next » 

Revision 1289

Added by kweitzel over 14 years ago

Branch 2.8.1 merged back into Trunk

View differences:

dom.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
                        }
93
                    }
94
                    return val / 100;
95
                case 'float': // fix reserved word
96
                    property = 'styleFloat'; // fall through
97
                default: 
98
                    // test currentStyle before touching
99
                    var value = el.currentStyle ? el.currentStyle[property] : null;
100
                    return ( el.style[property] || value );
101
            }
102
        };
103
    } else { // default to inline only
104
        getStyle = function(el, property) { return el.style[property]; };
105
    }
106
    
107
    if (isIE) {
108
        setStyle = function(el, property, val) {
109
            switch (property) {
110
                case 'opacity':
111
                    if ( YAHOO.lang.isString(el.style.filter) ) { // in case not appended
112
                        el.style.filter = 'alpha(opacity=' + val * 100 + ')';
113
                        
114
                        if (!el.currentStyle || !el.currentStyle.hasLayout) {
115
                            el.style.zoom = 1; // when no layout or cant tell
116
                        }
117
                    }
118
                    break;
119
                case 'float':
120
                    property = 'styleFloat';
121
                default:
122
                el.style[property] = val;
123
            }
124
        };
125
    } else {
126
        setStyle = function(el, property, val) {
127
            if (property == 'float') {
128
                property = 'cssFloat';
129
            }
130
            el.style[property] = val;
131
        };
132
    }
133

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

  
138 71
    /**
139 72
     * Provides helper methods for DOM elements.
140 73
     * @namespace YAHOO.util
141 74
     * @class Dom
75
     * @requires yahoo, event
142 76
     */
143
    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

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

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

  
168
            return el; // some other object, just pass it back
136
            return null;
169 137
        },
170 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

  
171 147
        /**
172 148
         * Normalizes currentStyle and ComputedStyle.
173 149
         * @method getStyle
......
176 152
         * @return {String | Array} The current value of the style property for the element(s).
177 153
         */
178 154
        getStyle: function(el, property) {
179
            property = toCamel(property);
180
            
181
            var f = function(element) {
182
                return getStyle(element, property);
183
            };
184
            
185
            return Y.Dom.batch(el, f, Y.Dom, true);
155
            return Y.Dom.batch(el, Y.Dom._getStyle, property);
186 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
                                }
192
                            }
193
                            return value / 100;
194
                        case 'float': // fix reserved word
195
                            property = 'styleFloat'; // fall through
196
                        default: 
197
                            property = Y.Dom._toCamel(property);
198
                            value = el[CURRENT_STYLE] ? el[CURRENT_STYLE][property] : null;
199
                            return ( el.style[property] || value );
200
                    }
201
                };
202
            }
203
        }(),
187 204
    
188 205
        /**
189 206
         * Wrapper for setting style properties of HTMLElements.  Normalizes "opacity" across modern browsers.
......
193 210
         * @param {String} val The value to apply to the given property.
194 211
         */
195 212
        setStyle: function(el, property, val) {
196
            property = toCamel(property);
197
            
198
            var f = function(element) {
199
                setStyle(element, property, val);
200
                
201
            };
202
            
203
            Y.Dom.batch(el, f, Y.Dom, true);
213
            Y.Dom.batch(el, Y.Dom._setStyle, { prop: property, val: val });
204 214
        },
215

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

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

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

  
269
        _canPosition: function(el) {
270
            return ( Y.Dom._getStyle(el, 'display') !== 'none' && Y.Dom._inDoc(el) );
271
        },
272

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

  
281
                    if (Y.Dom._canPosition(node)) {
282
                        box = node[GET_BOUNDING_CLIENT_RECT]();
283
                        doc = node[OWNER_DOCUMENT];
284
                        scrollLeft = Y.Dom.getDocumentScrollLeft(doc);
285
                        scrollTop = Y.Dom.getDocumentScrollTop(doc);
286
                        xy = [floor(box[LEFT]), floor(box[TOP])];
287

  
288
                        if (isIE && UA.ie < 8) { // IE < 8: viewport off by 2
289
                            off1 = 2;
290
                            off2 = 2;
291
                            mode = doc[COMPAT_MODE];
292

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

  
314
                        }
315

  
316
                        if ((scrollTop || scrollLeft)) {
317
                            xy[0] += scrollLeft;
318
                            xy[1] += scrollTop;
319
                        }
320

  
321
                        // gecko may return sub-pixel (non-int) values
322
                        xy[0] = floor(xy[0]);
323
                        xy[1] = floor(xy[1]);
324
                    } else {
325
                    }
326

  
327
                    return xy;
328
                };
329
            } else {
330
                return function(node) { // ff2, safari: manually calculate by crawling up offsetParents
331
                    var docScrollLeft, docScrollTop,
332
                        scrollTop, scrollLeft,
333
                        bCheck,
334
                        xy = false,
335
                        parentNode = node;
336

  
337
                    if  (Y.Dom._canPosition(node) ) {
338
                        xy = [node[OFFSET_LEFT], node[OFFSET_TOP]];
339
                        docScrollLeft = Y.Dom.getDocumentScrollLeft(node[OWNER_DOCUMENT]);
340
                        docScrollTop = Y.Dom.getDocumentScrollTop(node[OWNER_DOCUMENT]);
341

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

  
345
                        // TODO: worth refactoring for TOP/LEFT only?
346
                        while ((parentNode = parentNode[OFFSET_PARENT])) {
347
                            xy[0] += parentNode[OFFSET_LEFT];
348
                            xy[1] += parentNode[OFFSET_TOP];
349
                            if (bCheck) {
350
                                xy = Y.Dom._calcBorders(parentNode, xy);
351
                            }
352
                        }
353

  
354
                        // account for any scrolled ancestors
355
                        if (Y.Dom._getStyle(node, POSITION) !== FIXED) {
356
                            parentNode = node;
357

  
358
                            while ((parentNode = parentNode[PARENT_NODE]) && parentNode[TAG_NAME]) {
359
                                scrollTop = parentNode[SCROLL_TOP];
360
                                scrollLeft = parentNode[SCROLL_LEFT];
361

  
362
                                //Firefox does something funky with borders when overflow is not visible.
363
                                if (isGecko && (Y.Dom._getStyle(parentNode, 'overflow') !== 'visible')) {
364
                                        xy = Y.Dom._calcBorders(parentNode, xy);
365
                                }
366

  
367
                                if (scrollTop || scrollLeft) {
368
                                    xy[0] -= scrollLeft;
369
                                    xy[1] -= scrollTop;
370
                                }
371
                            }
372
                            xy[0] += docScrollLeft;
373
                            xy[1] += docScrollTop;
374

  
375
                        } else {
376
                            //Fix FIXED position -- add scrollbars
377
                            if (isOpera) {
378
                                xy[0] -= docScrollLeft;
379
                                xy[1] -= docScrollTop;
380
                            } else if (isSafari || isGecko) {
381
                                xy[0] += docScrollLeft;
382
                                xy[1] += docScrollTop;
383
                            }
384
                        }
385
                        //Round the numbers so we get sane data back
386
                        xy[0] = Math.floor(xy[0]);
387
                        xy[1] = Math.floor(xy[1]);
388
                    } else {
389
                    }
390
                    return xy;                
391
                };
392
            }
393
        }(), // NOTE: Executing for loadtime branching
225 394
        
226 395
        /**
227 396
         * 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).
......
260 429
         * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
261 430
         */
262 431
        setXY: function(el, pos, noRetry) {
263
            var f = function(el) {
264
                var style_pos = this.getStyle(el, 'position');
265
                if (style_pos == 'static') { // default to relative
266
                    this.setStyle(el, 'position', 'relative');
267
                    style_pos = 'relative';
268
                }
432
            Y.Dom.batch(el, Y.Dom._setXY, { pos: pos, noRetry: noRetry });
433
        },
269 434

  
270
                var pageXY = this.getXY(el);
271
                if (pageXY === false) { // has to be part of doc to have pageXY
272
                    return false; 
273
                }
274
                
275
                var delta = [ // assuming pixels; if not we will have to retry
276
                    parseInt( this.getStyle(el, 'left'), 10 ),
277
                    parseInt( this.getStyle(el, 'top'), 10 )
278
                ];
279
            
280
                if ( isNaN(delta[0]) ) {// in case of 'auto'
281
                    delta[0] = (style_pos == 'relative') ? 0 : el.offsetLeft;
282
                } 
283
                if ( isNaN(delta[1]) ) { // in case of 'auto'
284
                    delta[1] = (style_pos == 'relative') ? 0 : el.offsetTop;
285
                } 
435
        _setXY: function(node, args) {
436
            var pos = Y.Dom._getStyle(node, POSITION),
437
                setStyle = Y.Dom.setStyle,
438
                xy = args.pos,
439
                noRetry = args.noRetry,
440

  
441
                delta = [ // assuming pixels; if not we will have to retry
442
                    parseInt( Y.Dom.getComputedStyle(node, LEFT), 10 ),
443
                    parseInt( Y.Dom.getComputedStyle(node, TOP), 10 )
444
                ],
445

  
446
                currentXY,
447
                newXY;
286 448
        
287
                if (pos[0] !== null) { el.style.left = pos[0] - pageXY[0] + delta[0] + 'px'; }
288
                if (pos[1] !== null) { el.style.top = pos[1] - pageXY[1] + delta[1] + 'px'; }
289
              
290
                if (!noRetry) {
291
                    var newXY = this.getXY(el);
449
            if (pos == 'static') { // default to relative
450
                pos = RELATIVE;
451
                setStyle(node, POSITION, pos);
452
            }
292 453

  
293
                    // if retry is true, try one more time if we miss 
294
                   if ( (pos[0] !== null && newXY[0] != pos[0]) || 
295
                        (pos[1] !== null && newXY[1] != pos[1]) ) {
296
                       this.setXY(el, pos, true);
297
                   }
298
                }        
299
        
300
            };
454
            currentXY = Y.Dom._getXY(node);
455

  
456
            if (!xy || currentXY === false) { // has to be part of doc to have xy
457
                return false; 
458
            }
301 459
            
302
            Y.Dom.batch(el, f, Y.Dom, true);
460
            if ( isNaN(delta[0]) ) {// in case of 'auto'
461
                delta[0] = (pos == RELATIVE) ? 0 : node[OFFSET_LEFT];
462
            } 
463
            if ( isNaN(delta[1]) ) { // in case of 'auto'
464
                delta[1] = (pos == RELATIVE) ? 0 : node[OFFSET_TOP];
465
            } 
466

  
467
            if (xy[0] !== null) { // from setX
468
                setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
469
            }
470

  
471
            if (xy[1] !== null) { // from setY
472
                setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
473
            }
474
          
475
            if (!noRetry) {
476
                newXY = Y.Dom._getXY(node);
477

  
478
                // if retry is true, try one more time if we miss 
479
               if ( (xy[0] !== null && newXY[0] != xy[0]) || 
480
                    (xy[1] !== null && newXY[1] != xy[1]) ) {
481
                   Y.Dom._setXY(node, { pos: xy, noRetry: true });
482
               }
483
            }        
484

  
303 485
        },
304 486
        
305 487
        /**
......
333 515
         */
334 516
        getRegion: function(el) {
335 517
            var f = function(el) {
336
                if ( (el.parentNode === null || el.offsetParent === null ||
337
                        this.getStyle(el, 'display') == 'none') && el != document.body) {
338
                    return false;
518
                var region = false;
519
                if ( Y.Dom._canPosition(el) ) {
520
                    region = Y.Region.getRegion(el);
521
                } else {
339 522
                }
340 523

  
341
                var region = Y.Region.getRegion(el);
342 524
                return region;
343 525
            };
344 526
            
......
366 548
        },
367 549

  
368 550
        /**
369
         * Returns a array of HTMLElements with the given class.
551
         * Returns an array of HTMLElements with the given class.
370 552
         * For optimized performance, include a tag and/or root node when possible.
553
         * Note: This method operates against a live collection, so modifying the 
554
         * collection in the callback (removing/appending nodes, etc.) will have
555
         * side effects.  Instead you should iterate the returned nodes array,
556
         * as you would with the native "getElementsByTagName" method. 
371 557
         * @method getElementsByClassName
372 558
         * @param {String} className The class name to match against
373 559
         * @param {String} tag (optional) The tag name of the elements being collected
374
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
560
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point.
561
         * This element is not included in the className scan.
375 562
         * @param {Function} apply (optional) A function to apply to each element when found 
563
         * @param {Any} o (optional) An optional arg that is passed to the supplied method
564
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
376 565
         * @return {Array} An array of elements that have the given class name
377 566
         */
378
        getElementsByClassName: function(className, tag, root, apply) {
567
        getElementsByClassName: function(className, tag, root, apply, o, overrides) {
379 568
            tag = tag || '*';
380 569
            root = (root) ? Y.Dom.get(root) : null || document; 
381 570
            if (!root) {
......
384 573

  
385 574
            var nodes = [],
386 575
                elements = root.getElementsByTagName(tag),
387
                re = getClassRegEx(className);
576
                hasClass = Y.Dom.hasClass;
388 577

  
389 578
            for (var i = 0, len = elements.length; i < len; ++i) {
390
                if ( re.test(elements[i].className) ) {
579
                if ( hasClass(elements[i], className) ) {
391 580
                    nodes[nodes.length] = elements[i];
392
                    if (apply) {
393
                        apply.call(elements[i], elements[i]);
394
                    }
395 581
                }
396 582
            }
397 583
            
584
            if (apply) {
585
                Y.Dom.batch(nodes, apply, o, overrides);
586
            }
587

  
398 588
            return nodes;
399 589
        },
400 590

  
......
406 596
         * @return {Boolean | Array} A boolean value or array of boolean values
407 597
         */
408 598
        hasClass: function(el, className) {
409
            var re = getClassRegEx(className);
599
            return Y.Dom.batch(el, Y.Dom._hasClass, className);
600
        },
410 601

  
411
            var f = function(el) {
412
                return re.test(el.className);
413
            };
602
        _hasClass: function(el, className) {
603
            var ret = false,
604
                current;
414 605
            
415
            return Y.Dom.batch(el, f, Y.Dom, true);
606
            if (el && className) {
607
                current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
608
                if (className.exec) {
609
                    ret = className.test(current);
610
                } else {
611
                    ret = className && (SPACE + current + SPACE).
612
                        indexOf(SPACE + className + SPACE) > -1;
613
                }
614
            } else {
615
            }
616

  
617
            return ret;
416 618
        },
417 619
    
418 620
        /**
......
423 625
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
424 626
         */
425 627
        addClass: function(el, className) {
426
            var f = function(el) {
427
                if (this.hasClass(el, className)) {
428
                    return false; // already present
628
            return Y.Dom.batch(el, Y.Dom._addClass, className);
629
        },
630

  
631
        _addClass: function(el, className) {
632
            var ret = false,
633
                current;
634

  
635
            if (el && className) {
636
                current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
637
                if ( !Y.Dom._hasClass(el, className) ) {
638
                    Y.Dom.setAttribute(el, CLASS_NAME, trim(current + SPACE + className));
639
                    ret = true;
429 640
                }
430
                
431
                
432
                el.className = YAHOO.lang.trim([el.className, className].join(' '));
433
                return true;
434
            };
435
            
436
            return Y.Dom.batch(el, f, Y.Dom, true);
641
            } else {
642
            }
643

  
644
            return ret;
437 645
        },
438 646
    
439 647
        /**
......
444 652
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
445 653
         */
446 654
        removeClass: function(el, className) {
447
            var re = getClassRegEx(className);
448
            
449
            var f = function(el) {
450
                if (!this.hasClass(el, className)) {
451
                    return false; // not present
452
                }                 
655
            return Y.Dom.batch(el, Y.Dom._removeClass, className);
656
        },
657
        
658
        _removeClass: function(el, className) {
659
            var ret = false,
660
                current,
661
                newClass,
662
                attr;
453 663

  
454
                
455
                var c = el.className;
456
                el.className = c.replace(re, ' ');
457
                if ( this.hasClass(el, className) ) { // in case of multiple adjacent
458
                    this.removeClass(el, className);
664
            if (el && className) {
665
                current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
666
                Y.Dom.setAttribute(el, CLASS_NAME, current.replace(Y.Dom._getClassRegex(className), EMPTY));
667

  
668
                newClass = Y.Dom._getAttribute(el, CLASS_NAME);
669
                if (current !== newClass) { // else nothing changed
670
                    Y.Dom.setAttribute(el, CLASS_NAME, trim(newClass)); // trim after comparing to current class
671
                    ret = true;
672

  
673
                    if (Y.Dom._getAttribute(el, CLASS_NAME) === '') { // remove class attribute if empty
674
                        attr = (el.hasAttribute && el.hasAttribute(_CLASS)) ? _CLASS : CLASS_NAME;
675
                        el.removeAttribute(attr);
676
                    }
459 677
                }
460 678

  
461
                el.className = YAHOO.lang.trim(el.className); // remove any trailing spaces
462
                return true;
463
            };
464
            
465
            return Y.Dom.batch(el, f, Y.Dom, true);
679
            } else {
680
            }
681

  
682
            return ret;
466 683
        },
467 684
        
468 685
        /**
......
475 692
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
476 693
         */
477 694
        replaceClass: function(el, oldClassName, newClassName) {
478
            if (!newClassName || oldClassName === newClassName) { // avoid infinite loop
479
                return false;
480
            }
481
            
482
            var re = getClassRegEx(oldClassName);
695
            return Y.Dom.batch(el, Y.Dom._replaceClass, { from: oldClassName, to: newClassName });
696
        },
483 697

  
484
            var f = function(el) {
485
            
486
                if ( !this.hasClass(el, oldClassName) ) {
487
                    this.addClass(el, newClassName); // just add it if nothing to replace
488
                    return true; // NOTE: return
489
                }
490
            
491
                el.className = el.className.replace(re, ' ' + newClassName + ' ');
698
        _replaceClass: function(el, classObj) {
699
            var className,
700
                from,
701
                to,
702
                ret = false,
703
                current;
492 704

  
493
                if ( this.hasClass(el, oldClassName) ) { // in case of multiple adjacent
494
                    this.replaceClass(el, oldClassName, newClassName);
705
            if (el && classObj) {
706
                from = classObj.from;
707
                to = classObj.to;
708

  
709
                if (!to) {
710
                    ret = false;
711
                }  else if (!from) { // just add if no "from"
712
                    ret = Y.Dom._addClass(el, classObj.to);
713
                } else if (from !== to) { // else nothing to replace
714
                    // May need to lead with DBLSPACE?
715
                    current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
716
                    className = (SPACE + current.replace(Y.Dom._getClassRegex(from), SPACE + to)).
717
                               split(Y.Dom._getClassRegex(to));
718

  
719
                    // insert to into what would have been the first occurrence slot
720
                    className.splice(1, 0, SPACE + to);
721
                    Y.Dom.setAttribute(el, CLASS_NAME, trim(className.join(EMPTY)));
722
                    ret = true;
495 723
                }
724
            } else {
725
            }
496 726

  
497
                el.className = YAHOO.lang.trim(el.className); // remove any trailing spaces
498
                return true;
499
            };
500
            
501
            return Y.Dom.batch(el, f, Y.Dom, true);
727
            return ret;
502 728
        },
503 729
        
504 730
        /**
......
514 740
            var f = function(el) {
515 741
                if (el && el.id) { // do not override existing ID
516 742
                    return el.id;
517
                } 
743
                }
518 744

  
519
                var id = prefix + id_counter++;
745
                var id = prefix + YAHOO.env._id_counter++;
520 746

  
521 747
                if (el) {
748
                    if (el[OWNER_DOCUMENT] && el[OWNER_DOCUMENT].getElementById(id)) { // in case one already exists
749
                        // use failed id plus prefix to help ensure uniqueness
750
                        return Y.Dom.generateId(el, id + prefix);
751
                    }
522 752
                    el.id = id;
523 753
                }
524 754
                
......
540 770
            haystack = Y.Dom.get(haystack);
541 771
            needle = Y.Dom.get(needle);
542 772
            
543
            if (!haystack || !needle) {
544
                return false;
545
            }
773
            var ret = false;
546 774

  
547
            if (haystack.contains && needle.nodeType && !isSafari) { // safari contains is broken
548
                return haystack.contains(needle);
775
            if ( (haystack && needle) && (haystack[NODE_TYPE] && needle[NODE_TYPE]) ) {
776
                if (haystack.contains && haystack !== needle) { // contains returns true when equal
777
                    ret = haystack.contains(needle);
778
                }
779
                else if (haystack.compareDocumentPosition) { // gecko
780
                    ret = !!(haystack.compareDocumentPosition(needle) & 16);
781
                }
782
            } else {
549 783
            }
550
            else if ( haystack.compareDocumentPosition && needle.nodeType ) {
551
                return !!(haystack.compareDocumentPosition(needle) & 16);
552
            } else if (needle.nodeType) {
553
                // fallback to crawling up (safari)
554
                return !!this.getAncestorBy(needle, function(el) {
555
                    return el == haystack; 
556
                }); 
557
            }
558
            return false;
784
            return ret;
559 785
        },
560 786
        
561 787
        /**
562 788
         * Determines whether an HTMLElement is present in the current document.
563 789
         * @method inDocument         
564 790
         * @param {String | HTMLElement} el The element to search for
791
         * @param {Object} doc An optional document to search, defaults to element's owner document 
565 792
         * @return {Boolean} Whether or not the element is present in the current document
566 793
         */
567
        inDocument: function(el) {
568
            return this.isAncestor(document.documentElement, el);
794
        inDocument: function(el, doc) {
795
            return Y.Dom._inDoc(Y.Dom.get(el), doc);
569 796
        },
797

  
798
        _inDoc: function(el, doc) {
799
            var ret = false;
800
            if (el && el[TAG_NAME]) {
801
                doc = doc || el[OWNER_DOCUMENT]; 
802
                ret = Y.Dom.isAncestor(doc[DOCUMENT_ELEMENT], el);
803
            } else {
804
            }
805
            return ret;
806
        },
570 807
        
571 808
        /**
572
         * Returns a array of HTMLElements that pass the test applied by supplied boolean method.
809
         * Returns an array of HTMLElements that pass the test applied by supplied boolean method.
573 810
         * For optimized performance, include a tag and/or root node when possible.
811
         * Note: This method operates against a live collection, so modifying the 
812
         * collection in the callback (removing/appending nodes, etc.) will have
813
         * side effects.  Instead you should iterate the returned nodes array,
814
         * as you would with the native "getElementsByTagName" method. 
574 815
         * @method getElementsBy
575 816
         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
576 817
         * @param {String} tag (optional) The tag name of the elements being collected
577 818
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
578 819
         * @param {Function} apply (optional) A function to apply to each element when found 
820
         * @param {Any} o (optional) An optional arg that is passed to the supplied method
821
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
579 822
         * @return {Array} Array of HTMLElements
580 823
         */
581
        getElementsBy: function(method, tag, root, apply) {
824
        getElementsBy: function(method, tag, root, apply, o, overrides, firstOnly) {
582 825
            tag = tag || '*';
583 826
            root = (root) ? Y.Dom.get(root) : null || document; 
584 827

  
......
591 834
            
592 835
            for (var i = 0, len = elements.length; i < len; ++i) {
593 836
                if ( method(elements[i]) ) {
594
                    nodes[nodes.length] = elements[i];
595
                    if (apply) {
596
                        apply(elements[i]);
837
                    if (firstOnly) {
838
                        nodes = elements[i]; 
839
                        break;
840
                    } else {
841
                        nodes[nodes.length] = elements[i];
597 842
                    }
598 843
                }
599 844
            }
600 845

  
846
            if (apply) {
847
                Y.Dom.batch(nodes, apply, o, overrides);
848
            }
849

  
601 850
            
602 851
            return nodes;
603 852
        },
604 853
        
605 854
        /**
855
         * Returns the first HTMLElement that passes the test applied by the supplied boolean method.
856
         * @method getElementBy
857
         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
858
         * @param {String} tag (optional) The tag name of the elements being collected
859
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
860
         * @return {HTMLElement}
861
         */
862
        getElementBy: function(method, tag, root) {
863
            return Y.Dom.getElementsBy(method, tag, root, null, null, null, true); 
864
        },
865

  
866
        /**
606 867
         * Runs the supplied method against each item in the Collection/Array.
607 868
         * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) ).
608 869
         * @method batch
609 870
         * @param {String | HTMLElement | Array} el (optional) An element or array of elements to apply the method to
610 871
         * @param {Function} method The method to apply to the element(s)
611 872
         * @param {Any} o (optional) An optional arg that is passed to the supplied method
612
         * @param {Boolean} override (optional) Whether or not to override the scope of "method" with "o"
873
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
613 874
         * @return {Any | Array} The return value(s) from the supplied method
614 875
         */
615
        batch: function(el, method, o, override) {
616
            el = (el && (el.tagName || el.item)) ? el : Y.Dom.get(el); // skip get() when possible
876
        batch: function(el, method, o, overrides) {
877
            var collection = [],
878
                scope = (overrides) ? o : window;
879
                
880
            el = (el && (el[TAG_NAME] || el.item)) ? el : Y.Dom.get(el); // skip get() when possible
881
            if (el && method) {
882
                if (el[TAG_NAME] || el.length === undefined) { // element or not array-like 
883
                    return method.call(scope, el, o);
884
                } 
617 885

  
618
            if (!el || !method) {
886
                for (var i = 0; i < el.length; ++i) {
887
                    collection[collection.length] = method.call(scope, el[i], o);
888
                }
889
            } else {
619 890
                return false;
620 891
            } 
621
            var scope = (override) ? o : window;
622
            
623
            if (el.tagName || el.length === undefined) { // element or not array-like 
624
                return method.call(scope, el, o);
625
            } 
626

  
627
            var collection = [];
628
            
629
            for (var i = 0, len = el.length; i < len; ++i) {
630
                collection[collection.length] = method.call(scope, el[i], o);
631
            }
632
            
633 892
            return collection;
634 893
        },
635 894
        
......
639 898
         * @return {Int} The height of the actual document (which includes the body and its margin).
640 899
         */
641 900
        getDocumentHeight: function() {
642
            var scrollHeight = (document.compatMode != 'CSS1Compat') ? document.body.scrollHeight : document.documentElement.scrollHeight;
901
            var scrollHeight = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollHeight : documentElement.scrollHeight,
902
                h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
643 903

  
644
            var h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
645 904
            return h;
646 905
        },
647 906
        
......
651 910
         * @return {Int} The width of the actual document (which includes the body and its margin).
652 911
         */
653 912
        getDocumentWidth: function() {
654
            var scrollWidth = (document.compatMode != 'CSS1Compat') ? document.body.scrollWidth : document.documentElement.scrollWidth;
655
            var w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
913
            var scrollWidth = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollWidth : documentElement.scrollWidth,
914
                w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
656 915
            return w;
657 916
        },
658 917

  
......
662 921
         * @return {Int} The height of the viewable area of the page (excludes scrollbars).
663 922
         */
664 923
        getViewportHeight: function() {
665
            var height = self.innerHeight; // Safari, Opera
666
            var mode = document.compatMode;
924
            var height = self.innerHeight, // Safari, Opera
925
                mode = document[COMPAT_MODE];
667 926
        
668 927
            if ( (mode || isIE) && !isOpera ) { // IE, Gecko
669
                height = (mode == 'CSS1Compat') ?
670
                        document.documentElement.clientHeight : // Standards
928
                height = (mode == CSS1_COMPAT) ?
929
                        documentElement.clientHeight : // Standards
671 930
                        document.body.clientHeight; // Quirks
672 931
            }
673 932
        
......
681 940
         */
682 941
        
683 942
        getViewportWidth: function() {
684
            var width = self.innerWidth;  // Safari
685
            var mode = document.compatMode;
943
            var width = self.innerWidth,  // Safari
944
                mode = document[COMPAT_MODE];
686 945
            
687 946
            if (mode || isIE) { // IE, Gecko, Opera
688
                width = (mode == 'CSS1Compat') ?
689
                        document.documentElement.clientWidth : // Standards
947
                width = (mode == CSS1_COMPAT) ?
948
                        documentElement.clientWidth : // Standards
690 949
                        document.body.clientWidth; // Quirks
691 950
            }
692 951
            return width;
......
701 960
         * @return {Object} HTMLElement or null if not found
702 961
         */
703 962
        getAncestorBy: function(node, method) {
704
            while (node = node.parentNode) { // NOTE: assignment
705
                if ( testElement(node, method) ) {
963
            while ( (node = node[PARENT_NODE]) ) { // NOTE: assignment
964
                if ( Y.Dom._testElement(node, method) ) {
706 965
                    return node;
707 966
                }
708 967
            } 
......
739 998
                return null;
740 999
            }
741 1000
            var method = function(el) {
742
                 return el.tagName && el.tagName.toUpperCase() == tagName.toUpperCase();
1001
                 return el[TAG_NAME] && el[TAG_NAME].toUpperCase() == tagName.toUpperCase();
743 1002
            };
744 1003

  
745 1004
            return Y.Dom.getAncestorBy(node, method);
......
758 1017
        getPreviousSiblingBy: function(node, method) {
759 1018
            while (node) {
760 1019
                node = node.previousSibling;
761
                if ( testElement(node, method) ) {
1020
                if ( Y.Dom._testElement(node, method) ) {
762 1021
                    return node;
763 1022
                }
764 1023
            }
......
793 1052
        getNextSiblingBy: function(node, method) {
794 1053
            while (node) {
795 1054
                node = node.nextSibling;
796
                if ( testElement(node, method) ) {
1055
                if ( Y.Dom._testElement(node, method) ) {
797 1056
                    return node;
798 1057
                }
799 1058
            }
......
824 1083
         * @return {Object} HTMLElement or null if not found
825 1084
         */
826 1085
        getFirstChildBy: function(node, method) {
827
            var child = ( testElement(node.firstChild, method) ) ? node.firstChild : null;
1086
            var child = ( Y.Dom._testElement(node.firstChild, method) ) ? node.firstChild : null;
828 1087
            return child || Y.Dom.getNextSiblingBy(node.firstChild, method);
829 1088
        }, 
830 1089

  
......
854 1113
            if (!node) {
855 1114
                return null;
856 1115
            }
857
            var child = ( testElement(node.lastChild, method) ) ? node.lastChild : null;
1116
            var child = ( Y.Dom._testElement(node.lastChild, method) ) ? node.lastChild : null;
858 1117
            return child || Y.Dom.getPreviousSiblingBy(node.lastChild, method);
859 1118
        }, 
860 1119

  
......
878 1137
         * @return {Array} A static array of HTMLElements
879 1138
         */
880 1139
        getChildrenBy: function(node, method) {
881
            var child = Y.Dom.getFirstChildBy(node, method);
882
            var children = child ? [child] : [];
1140
            var child = Y.Dom.getFirstChildBy(node, method),
1141
                children = child ? [child] : [];
883 1142

  
884 1143
            Y.Dom.getNextSiblingBy(child, function(node) {
885 1144
                if ( !method || method(node) ) {
......
904 1163

  
905 1164
            return Y.Dom.getChildrenBy(node);
906 1165
        },
907
 
1166

  
908 1167
        /**
909 1168
         * Returns the left scroll value of the document 
910 1169
         * @method getDocumentScrollLeft
......
913 1172
         */
914 1173
        getDocumentScrollLeft: function(doc) {
915 1174
            doc = doc || document;
916
            return Math.max(doc.documentElement.scrollLeft, doc.body.scrollLeft);
1175
            return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
917 1176
        }, 
918 1177

  
919 1178
        /**
......
924 1183
         */
925 1184
        getDocumentScrollTop: function(doc) {
926 1185
            doc = doc || document;
927
            return Math.max(doc.documentElement.scrollTop, doc.body.scrollTop);
1186
            return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
928 1187
        },
929 1188

  
930 1189
        /**
......
938 1197
            newNode = Y.Dom.get(newNode); 
939 1198
            referenceNode = Y.Dom.get(referenceNode); 
940 1199
            
941
            if (!newNode || !referenceNode || !referenceNode.parentNode) {
1200
            if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
942 1201
                return null;
943 1202
            }       
944 1203

  
945
            return referenceNode.parentNode.insertBefore(newNode, referenceNode); 
1204
            return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode); 
946 1205
        },
947 1206

  
948 1207
        /**
......
956 1215
            newNode = Y.Dom.get(newNode); 
957 1216
            referenceNode = Y.Dom.get(referenceNode); 
958 1217
            
959
            if (!newNode || !referenceNode || !referenceNode.parentNode) {
1218
            if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
960 1219
                return null;
961 1220
            }       
962 1221

  
963 1222
            if (referenceNode.nextSibling) {
964
                return referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling); 
1223
                return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode.nextSibling); 
965 1224
            } else {
966
                return referenceNode.parentNode.appendChild(newNode);
1225
                return referenceNode[PARENT_NODE].appendChild(newNode);
967 1226
            }
968 1227
        },
969 1228

  
......
979 1238
                b = Y.Dom.getViewportHeight() + t;
980 1239

  
981 1240
            return new Y.Region(t, r, b, l);
982
        }
983
    };
984
    
985
    var getXY = function() {
986
        if (document.documentElement.getBoundingClientRect) { // IE
987
            return function(el) {
988
                var box = el.getBoundingClientRect();
1241
        },
989 1242

  
990
                var rootNode = el.ownerDocument;
991
                return [box.left + Y.Dom.getDocumentScrollLeft(rootNode), box.top +
992
                        Y.Dom.getDocumentScrollTop(rootNode)];
993
            };
994
        } else {
995
            return function(el) { // manually calculate by crawling up offsetParents
996
                var pos = [el.offsetLeft, el.offsetTop];
997
                var parentNode = el.offsetParent;
1243
        /**
1244
         * Provides a normalized attribute interface. 
1245
         * @method setAttribute
1246
         * @param {String | HTMLElement} el The target element for the attribute.
1247
         * @param {String} attr The attribute to set.
1248
         * @param {String} val The value of the attribute.
1249
         */
1250
        setAttribute: function(el, attr, val) {
1251
            Y.Dom.batch(el, Y.Dom._setAttribute, { attr: attr, val: val });
1252
        },
998 1253

  
999
                // safari: subtract body offsets if el is abs (or any offsetParent), unless body is offsetParent
1000
                var accountForBody = (isSafari &&
1001
                        Y.Dom.getStyle(el, 'position') == 'absolute' &&
1002
                        el.offsetParent == el.ownerDocument.body);
1254
        _setAttribute: function(el, args) {
1255
            var attr = Y.Dom._toCamel(args.attr),
1256
                val = args.val;
1003 1257

  
1004
                if (parentNode != el) {
1005
                    while (parentNode) {
1006
                        pos[0] += parentNode.offsetLeft;
1007
                        pos[1] += parentNode.offsetTop;
1008
                        if (!accountForBody && isSafari && 
1009
                                Y.Dom.getStyle(parentNode,'position') == 'absolute' ) { 
1010
                            accountForBody = true;
1011
                        }
1012
                        parentNode = parentNode.offsetParent;
1013
                    }
1258
            if (el && el.setAttribute) {
1259
                if (Y.Dom.DOT_ATTRIBUTES[attr]) {
1260
                    el[attr] = val;
1261
                } else {
1262
                    attr = Y.Dom.CUSTOM_ATTRIBUTES[attr] || attr;
1263
                    el.setAttribute(attr, val);
1014 1264
                }
1265
            } else {
1266
            }
1267
        },
1015 1268

  
1016
                if (accountForBody) { //safari doubles in this case
1017
                    pos[0] -= el.ownerDocument.body.offsetLeft;
1018
                    pos[1] -= el.ownerDocument.body.offsetTop;
1019
                } 
1020
                parentNode = el.parentNode;
1269
        /**
1270
         * Provides a normalized attribute interface. 
1271
         * @method getAttribute
1272
         * @param {String | HTMLElement} el The target element for the attribute.
1273
         * @param {String} attr The attribute to get.
1274
         * @return {String} The current value of the attribute. 
1275
         */
1276
        getAttribute: function(el, attr) {
1277
            return Y.Dom.batch(el, Y.Dom._getAttribute, attr);
1278
        },
1021 1279

  
1022
                // account for any scrolled ancestors
1023
                while ( parentNode.tagName && !patterns.ROOT_TAG.test(parentNode.tagName) ) 
1024
                {
1025
                   // work around opera inline/table scrollLeft/Top bug
1026
                   if (Y.Dom.getStyle(parentNode, 'display').search(/^inline|table-row.*$/i)) { 
1027
                        pos[0] -= parentNode.scrollLeft;
1028
                        pos[1] -= parentNode.scrollTop;
1280

  
1281
        _getAttribute: function(el, attr) {
1282
            var val;
1283
            attr = Y.Dom.CUSTOM_ATTRIBUTES[attr] || attr;
1284

  
1285
            if (el && el.getAttribute) {
1286
                val = el.getAttribute(attr, 2);
1287
            } else {
1288
            }
1289

  
1290
            return val;
1291
        },
1292

  
1293
        _toCamel: function(property) {
1294
            var c = propertyCache;
1295

  
1296
            function tU(x,l) {
1297
                return l.toUpperCase();
1298
            }
1299

  
1300
            return c[property] || (c[property] = property.indexOf('-') === -1 ? 
1301
                                    property :
1302
                                    property.replace( /-([a-z])/gi, tU ));
1303
        },
1304

  
1305
        _getClassRegex: function(className) {
1306
            var re;
1307
            if (className !== undefined) { // allow empty string to pass
1308
                if (className.exec) { // already a RegExp
1309
                    re = className;
1310
                } else {
1311
                    re = reCache[className];
1312
                    if (!re) {
1313
                        // escape special chars (".", "[", etc.)
1314
                        className = className.replace(Y.Dom._patterns.CLASS_RE_TOKENS, '\\$1');
1315
                        re = reCache[className] = new RegExp(C_START + className + C_END, G);
1029 1316
                    }
1030
                    
1031
                    parentNode = parentNode.parentNode; 
1032 1317
                }
1318
            }
1319
            return re;
1320
        },
1033 1321

  
1034
                return pos;
1035
            };
1322
        _patterns: {
1323
            ROOT_TAG: /^body|html$/i, // body for quirks mode, html for standards,
1324
            CLASS_RE_TOKENS: /([\.\(\)\^\$\*\+\?\|\[\]\{\}\\])/g
1325
        },
1326

  
1327

  
1328
        _testElement: function(node, method) {
1329
            return node && node[NODE_TYPE] == 1 && ( !method || method(node) );
1330
        },
1331

  
1332
        _calcBorders: function(node, xy2) {
1333
            var t = parseInt(Y.Dom[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
1334
                l = parseInt(Y.Dom[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
1335
            if (isGecko) {
1336
                if (RE_TABLE.test(node[TAG_NAME])) {
1337
                    t = 0;
1338
                    l = 0;
1339
                }
1340
            }
1341
            xy2[0] += l;
1342
            xy2[1] += t;
1343
            return xy2;
1036 1344
        }
1037
    }() // NOTE: Executing for loadtime branching
1345
    };
1346
        
1347
    var _getComputedStyle = Y.Dom[GET_COMPUTED_STYLE];
1348
    // fix opera computedStyle default color unit (convert to rgb)
1349
    if (UA.opera) {
1350
        Y.Dom[GET_COMPUTED_STYLE] = function(node, att) {
1351
            var val = _getComputedStyle(node, att);
1352
            if (RE_COLOR.test(att)) {
1353
                val = Y.Dom.Color.toRGB(val);
1354
            }
1355

  
1356
            return val;
1357
        };
1358

  
1359
    }
1360

  
1361
    // safari converts transparent to rgba(), others use "transparent"
1362
    if (UA.webkit) {
1363
        Y.Dom[GET_COMPUTED_STYLE] = function(node, att) {
1364
            var val = _getComputedStyle(node, att);
1365

  
1366
            if (val === 'rgba(0, 0, 0, 0)') {
1367
                val = 'transparent'; 
1368
            }
1369

  
1370
            return val;
1371
        };
1372

  
1373
    }
1374

  
1375
    if (UA.ie && UA.ie >= 8 && document.documentElement.hasAttribute) { // IE 8 standards
1376
        Y.Dom.DOT_ATTRIBUTES.type = true; // IE 8 errors on input.setAttribute('type')
1377
    }
1038 1378
})();
1039 1379
/**
1040 1380
 * A region is a representation of an object on a grid.  It is defined
......
1058 1398
    this.top = t;
1059 1399
    
1060 1400
    /**
1401
     * The region's top extent
1402
     * @property y
1403
     * @type Int
1404
     */
1405
    this.y = t;
1406
    
1407
    /**
1061 1408
     * The region's top extent as index, for symmetry with set/getXY
1062 1409
     * @property 1
1063 1410
     * @type Int
......
1086 1433
    this.left = l;
1087 1434
    
1088 1435
    /**
1436
     * The region's left extent
1437
     * @property x
1438
     * @type Int
1439
     */
1440
    this.x = l;
1441
    
1442
    /**
1089 1443
     * The region's left extent as index, for symmetry with set/getXY
1090 1444
     * @property 0
1091 1445
     * @type Int
1092 1446
     */
1093 1447
    this[0] = l;
1448

  
1449
    /**
1450
     * The region's total width 
1451
     * @property width 
1452
     * @type Int
1453
     */
1454
    this.width = this.right - this.left;
1455

  
1456
    /**
1457
     * The region's total height 
1458
     * @property height 
1459
     * @type Int
1460
     */
1461
    this.height = this.bottom - this.top;
1094 1462
};
1095 1463

  
1096 1464
/**
......
1124 1492
 * @return {Region}        The overlap region, or null if there is no overlap
... This diff was truncated because it exceeds the maximum size that can be displayed.

Also available in: Unified diff