Project

General

Profile

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

    
13
(function() {
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,
22
        propertyCache = {}, // for faster hyphen converts
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',
64
    
65
    // brower detection
66
        isOpera = UA.opera,
67
        isSafari = UA.webkit, 
68
        isGecko = UA.gecko, 
69
        isIE = UA.ie; 
70
    
71
    /**
72
     * Provides helper methods for DOM elements.
73
     * @namespace YAHOO.util
74
     * @class Dom
75
     * @requires yahoo, event
76
     */
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

    
88
        /**
89
         * Returns an HTMLElement reference.
90
         * @method get
91
         * @param {String | HTMLElement |Array} el Accepts a string to use as an ID for getting a DOM reference, an actual DOM reference, or an Array of IDs and/or HTMLElements.
92
         * @return {HTMLElement | Array} A DOM reference to an HTML element or an array of HTMLElements.
93
         */
94
        get: function(el) {
95
            var id, nodes, c, i, len, attr;
96

    
97
            if (el) {
98
                if (el[NODE_TYPE] || el.item) { // Node, or NodeList
99
                    return el;
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
                }
119
                
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
134
            }
135

    
136
            return null;
137
        },
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

    
147
        /**
148
         * Normalizes currentStyle and ComputedStyle.
149
         * @method getStyle
150
         * @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.
151
         * @param {String} property The style property whose value is returned.
152
         * @return {String | Array} The current value of the style property for the element(s).
153
         */
154
        getStyle: function(el, property) {
155
            return Y.Dom.batch(el, Y.Dom._getStyle, property);
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
        }(),
206
    
207
        /**
208
         * Wrapper for setting style properties of HTMLElements.  Normalizes "opacity" across modern browsers.
209
         * @method setStyle
210
         * @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.
211
         * @param {String} property The style property to be set.
212
         * @param {String} val The value to apply to the given property.
213
         */
214
        setStyle: function(el, property, val) {
215
            Y.Dom.batch(el, Y.Dom._setStyle, { prop: property, val: val });
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
        }(),
260
        
261
        /**
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).
264
         * @method getXY
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
267
         * @return {Array} The XY position of the element(s)
268
         */
269
        getXY: function(el) {
270
            return Y.Dom.batch(el, Y.Dom._getXY);
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
400
        
401
        /**
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).
403
         * @method getX
404
         * @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
405
         * @return {Number | Array} The X position of the element(s)
406
         */
407
        getX: function(el) {
408
            var f = function(el) {
409
                return Y.Dom.getXY(el)[0];
410
            };
411
            
412
            return Y.Dom.batch(el, f, Y.Dom, true);
413
        },
414
        
415
        /**
416
         * Gets the current Y position of an element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
417
         * @method getY
418
         * @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
419
         * @return {Number | Array} The Y position of the element(s)
420
         */
421
        getY: function(el) {
422
            var f = function(el) {
423
                return Y.Dom.getXY(el)[1];
424
            };
425
            
426
            return Y.Dom.batch(el, f, Y.Dom, true);
427
        },
428
        
429
        /**
430
         * Set the position of an html element in page coordinates, regardless of how the element is positioned.
431
         * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
432
         * @method setXY
433
         * @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
434
         * @param {Array} pos Contains X & Y values for new position (coordinates are page-based)
435
         * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
436
         */
437
        setXY: function(el, pos, noRetry) {
438
            Y.Dom.batch(el, Y.Dom._setXY, { pos: pos, noRetry: noRetry });
439
        },
440

    
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;
454
        
455
            if (pos == 'static') { // default to relative
456
                pos = RELATIVE;
457
                setStyle(node, POSITION, pos);
458
            }
459

    
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
            }
466
            
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');
493
        },
494
        
495
        /**
496
         * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
497
         * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
498
         * @method setX
499
         * @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.
500
         * @param {Int} x The value to use as the X coordinate for the element(s).
501
         */
502
        setX: function(el, x) {
503
            Y.Dom.setXY(el, [x, null]);
504
        },
505
        
506
        /**
507
         * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
508
         * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
509
         * @method setY
510
         * @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.
511
         * @param {Int} x To use as the Y coordinate for the element(s).
512
         */
513
        setY: function(el, y) {
514
            Y.Dom.setXY(el, [null, y]);
515
        },
516
        
517
        /**
518
         * Returns the region position of the given element.
519
         * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
520
         * @method getRegion
521
         * @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.
522
         * @return {Region | Array} A Region or array of Region instances containing "top, left, bottom, right" member data.
523
         */
524
        getRegion: function(el) {
525
            var f = function(el) {
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');
532
                }
533

    
534
                return region;
535
            };
536
            
537
            return Y.Dom.batch(el, f, Y.Dom, true);
538
        },
539
        
540
        /**
541
         * Returns the width of the client (viewport).
542
         * @method getClientWidth
543
         * @deprecated Now using getViewportWidth.  This interface left intact for back compat.
544
         * @return {Int} The width of the viewable area of the page.
545
         */
546
        getClientWidth: function() {
547
            return Y.Dom.getViewportWidth();
548
        },
549
        
550
        /**
551
         * Returns the height of the client (viewport).
552
         * @method getClientHeight
553
         * @deprecated Now using getViewportHeight.  This interface left intact for back compat.
554
         * @return {Int} The height of the viewable area of the page.
555
         */
556
        getClientHeight: function() {
557
            return Y.Dom.getViewportHeight();
558
        },
559

    
560
        /**
561
         * Returns an array of HTMLElements with the given class.
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. 
567
         * @method getElementsByClassName
568
         * @param {String} className The class name to match against
569
         * @param {String} tag (optional) The tag name of the elements being collected
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.
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"
575
         * @return {Array} An array of elements that have the given class name
576
         */
577
        getElementsByClassName: function(className, tag, root, apply, o, overrides) {
578
            tag = tag || '*';
579
            root = (root) ? Y.Dom.get(root) : null || document; 
580
            if (!root) {
581
                return [];
582
            }
583

    
584
            var nodes = [],
585
                elements = root.getElementsByTagName(tag),
586
                hasClass = Y.Dom.hasClass;
587

    
588
            for (var i = 0, len = elements.length; i < len; ++i) {
589
                if ( hasClass(elements[i], className) ) {
590
                    nodes[nodes.length] = elements[i];
591
                }
592
            }
593
            
594
            if (apply) {
595
                Y.Dom.batch(nodes, apply, o, overrides);
596
            }
597

    
598
            return nodes;
599
        },
600

    
601
        /**
602
         * Determines whether an HTMLElement has the given className.
603
         * @method hasClass
604
         * @param {String | HTMLElement | Array} el The element or collection to test
605
         * @param {String} className the class name to search for
606
         * @return {Boolean | Array} A boolean value or array of boolean values
607
         */
608
        hasClass: function(el, className) {
609
            return Y.Dom.batch(el, Y.Dom._hasClass, className);
610
        },
611

    
612
        _hasClass: function(el, className) {
613
            var ret = false,
614
                current;
615
            
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;
629
        },
630
    
631
        /**
632
         * Adds a class name to a given element or collection of elements.
633
         * @method addClass         
634
         * @param {String | HTMLElement | Array} el The element or collection to add the class to
635
         * @param {String} className the class name to add to the class attribute
636
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
637
         */
638
        addClass: function(el, className) {
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;
651
                }
652
            } else {
653
                YAHOO.log('addClass called with invalid arguments', 'warn', 'Dom');
654
            }
655

    
656
            return ret;
657
        },
658
    
659
        /**
660
         * Removes a class name from a given element or collection of elements.
661
         * @method removeClass         
662
         * @param {String | HTMLElement | Array} el The element or collection to remove the class from
663
         * @param {String} className the class name to remove from the class attribute
664
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
665
         */
666
        removeClass: function(el, className) {
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;
675

    
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
                    }
690
                }
691

    
692
            } else {
693
                YAHOO.log('removeClass called with invalid arguments', 'warn', 'Dom');
694
            }
695

    
696
            return ret;
697
        },
698
        
699
        /**
700
         * Replace a class with another class for a given element or collection of elements.
701
         * If no oldClassName is present, the newClassName is simply added.
702
         * @method replaceClass  
703
         * @param {String | HTMLElement | Array} el The element or collection to remove the class from
704
         * @param {String} oldClassName the class name to be replaced
705
         * @param {String} newClassName the class name that will be replacing the old class name
706
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
707
         */
708
        replaceClass: function(el, oldClassName, newClassName) {
709
            return Y.Dom.batch(el, Y.Dom._replaceClass, { from: oldClassName, to: newClassName });
710
        },
711

    
712
        _replaceClass: function(el, classObj) {
713
            var className,
714
                from,
715
                to,
716
                ret = false,
717
                current;
718

    
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;
737
                }
738
            } else {
739
                YAHOO.log('replaceClass called with invalid arguments', 'warn', 'Dom');
740
            }
741

    
742
            return ret;
743
        },
744
        
745
        /**
746
         * Returns an ID and applies it to the element "el", if provided.
747
         * @method generateId  
748
         * @param {String | HTMLElement | Array} el (optional) An optional element array of elements to add an ID to (no ID is added if one is already present).
749
         * @param {String} prefix (optional) an optional prefix to use (defaults to "yui-gen").
750
         * @return {String | Array} The generated ID, or array of generated IDs (or original ID if already present on an element)
751
         */
752
        generateId: function(el, prefix) {
753
            prefix = prefix || 'yui-gen';
754

    
755
            var f = function(el) {
756
                if (el && el.id) { // do not override existing ID
757
                    YAHOO.log('generateId returning existing id ' + el.id, 'info', 'Dom');
758
                    return el.id;
759
                }
760

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

    
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
                    }
769
                    el.id = id;
770
                }
771
                
772
                return id;
773
            };
774

    
775
            // batch fails when no element, so just generate and return single ID
776
            return Y.Dom.batch(el, f, Y.Dom, true) || f.apply(Y.Dom, arguments);
777
        },
778
        
779
        /**
780
         * Determines whether an HTMLElement is an ancestor of another HTML element in the DOM hierarchy.
781
         * @method isAncestor
782
         * @param {String | HTMLElement} haystack The possible ancestor
783
         * @param {String | HTMLElement} needle The possible descendent
784
         * @return {Boolean} Whether or not the haystack is an ancestor of needle
785
         */
786
        isAncestor: function(haystack, needle) {
787
            haystack = Y.Dom.get(haystack);
788
            needle = Y.Dom.get(needle);
789
            
790
            var ret = false;
791

    
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');
801
            }
802
            YAHOO.log('isAncestor(' + haystack + ',' + needle + ' returning ' + ret, 'info', 'Dom');
803
            return ret;
804
        },
805
        
806
        /**
807
         * Determines whether an HTMLElement is present in the current document.
808
         * @method inDocument         
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 
811
         * @return {Boolean} Whether or not the element is present in the current document
812
         */
813
        inDocument: function(el, doc) {
814
            return Y.Dom._inDoc(Y.Dom.get(el), doc);
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
        },
827
        
828
        /**
829
         * Returns an array of HTMLElements that pass the test applied by supplied boolean method.
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. 
835
         * @method getElementsBy
836
         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
837
         * @param {String} tag (optional) The tag name of the elements being collected
838
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point 
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"
842
         * @return {Array} Array of HTMLElements
843
         */
844
        getElementsBy: function(method, tag, root, apply, o, overrides, firstOnly) {
845
            tag = tag || '*';
846
            root = (root) ? Y.Dom.get(root) : null || document; 
847

    
848
            if (!root) {
849
                return [];
850
            }
851

    
852
            var nodes = [],
853
                elements = root.getElementsByTagName(tag);
854
            
855
            for (var i = 0, len = elements.length; i < len; ++i) {
856
                if ( method(elements[i]) ) {
857
                    if (firstOnly) {
858
                        nodes = elements[i]; 
859
                        break;
860
                    } else {
861
                        nodes[nodes.length] = elements[i];
862
                    }
863
                }
864
            }
865

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

    
870
            YAHOO.log('getElementsBy returning ' + nodes, 'info', 'Dom');
871
            
872
            return nodes;
873
        },
874
        
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
        /**
888
         * Runs the supplied method against each item in the Collection/Array.
889
         * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) ).
890
         * @method batch
891
         * @param {String | HTMLElement | Array} el (optional) An element or array of elements to apply the method to
892
         * @param {Function} method The method to apply to the element(s)
893
         * @param {Any} o (optional) An optional arg that is passed to the supplied method
894
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
895
         * @return {Any | Array} The return value(s) from the supplied method
896
         */
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
                } 
906

    
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');
912
                return false;
913
            } 
914
            return collection;
915
        },
916
        
917
        /**
918
         * Returns the height of the document.
919
         * @method getDocumentHeight
920
         * @return {Int} The height of the actual document (which includes the body and its margin).
921
         */
922
        getDocumentHeight: function() {
923
            var scrollHeight = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollHeight : documentElement.scrollHeight,
924
                h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
925

    
926
            YAHOO.log('getDocumentHeight returning ' + h, 'info', 'Dom');
927
            return h;
928
        },
929
        
930
        /**
931
         * Returns the width of the document.
932
         * @method getDocumentWidth
933
         * @return {Int} The width of the actual document (which includes the body and its margin).
934
         */
935
        getDocumentWidth: function() {
936
            var scrollWidth = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollWidth : documentElement.scrollWidth,
937
                w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
938
            YAHOO.log('getDocumentWidth returning ' + w, 'info', 'Dom');
939
            return w;
940
        },
941

    
942
        /**
943
         * Returns the current height of the viewport.
944
         * @method getViewportHeight
945
         * @return {Int} The height of the viewable area of the page (excludes scrollbars).
946
         */
947
        getViewportHeight: function() {
948
            var height = self.innerHeight, // Safari, Opera
949
                mode = document[COMPAT_MODE];
950
        
951
            if ( (mode || isIE) && !isOpera ) { // IE, Gecko
952
                height = (mode == CSS1_COMPAT) ?
953
                        documentElement.clientHeight : // Standards
954
                        document.body.clientHeight; // Quirks
955
            }
956
        
957
            YAHOO.log('getViewportHeight returning ' + height, 'info', 'Dom');
958
            return height;
959
        },
960
        
961
        /**
962
         * Returns the current width of the viewport.
963
         * @method getViewportWidth
964
         * @return {Int} The width of the viewable area of the page (excludes scrollbars).
965
         */
966
        
967
        getViewportWidth: function() {
968
            var width = self.innerWidth,  // Safari
969
                mode = document[COMPAT_MODE];
970
            
971
            if (mode || isIE) { // IE, Gecko, Opera
972
                width = (mode == CSS1_COMPAT) ?
973
                        documentElement.clientWidth : // Standards
974
                        document.body.clientWidth; // Quirks
975
            }
976
            YAHOO.log('getViewportWidth returning ' + width, 'info', 'Dom');
977
            return width;
978
        },
979

    
980
       /**
981
         * Returns the nearest ancestor that passes the test applied by supplied boolean method.
982
         * For performance reasons, IDs are not accepted and argument validation omitted.
983
         * @method getAncestorBy
984
         * @param {HTMLElement} node The HTMLElement to use as the starting point 
985
         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
986
         * @return {Object} HTMLElement or null if not found
987
         */
988
        getAncestorBy: function(node, method) {
989
            while ( (node = node[PARENT_NODE]) ) { // NOTE: assignment
990
                if ( Y.Dom._testElement(node, method) ) {
991
                    YAHOO.log('getAncestorBy returning ' + node, 'info', 'Dom');
992
                    return node;
993
                }
994
            } 
995

    
996
            YAHOO.log('getAncestorBy returning null (no ancestor passed test)', 'error', 'Dom');
997
            return null;
998
        },
999
        
1000
        /**
1001
         * Returns the nearest ancestor with the given className.
1002
         * @method getAncestorByClassName
1003
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
1004
         * @param {String} className
1005
         * @return {Object} HTMLElement
1006
         */
1007
        getAncestorByClassName: function(node, className) {
1008
            node = Y.Dom.get(node);
1009
            if (!node) {
1010
                YAHOO.log('getAncestorByClassName failed: invalid node argument', 'error', 'Dom');
1011
                return null;
1012
            }
1013
            var method = function(el) { return Y.Dom.hasClass(el, className); };
1014
            return Y.Dom.getAncestorBy(node, method);
1015
        },
1016

    
1017
        /**
1018
         * Returns the nearest ancestor with the given tagName.
1019
         * @method getAncestorByTagName
1020
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
1021
         * @param {String} tagName
1022
         * @return {Object} HTMLElement
1023
         */
1024
        getAncestorByTagName: function(node, tagName) {
1025
            node = Y.Dom.get(node);
1026
            if (!node) {
1027
                YAHOO.log('getAncestorByTagName failed: invalid node argument', 'error', 'Dom');
1028
                return null;
1029
            }
1030
            var method = function(el) {
1031
                 return el[TAG_NAME] && el[TAG_NAME].toUpperCase() == tagName.toUpperCase();
1032
            };
1033

    
1034
            return Y.Dom.getAncestorBy(node, method);
1035
        },
1036

    
1037
        /**
1038
         * Returns the previous sibling that is an HTMLElement. 
1039
         * For performance reasons, IDs are not accepted and argument validation omitted.
1040
         * Returns the nearest HTMLElement sibling if no method provided.
1041
         * @method getPreviousSiblingBy
1042
         * @param {HTMLElement} node The HTMLElement to use as the starting point 
1043
         * @param {Function} method A boolean function used to test siblings
1044
         * that receives the sibling node being tested as its only argument
1045
         * @return {Object} HTMLElement or null if not found
1046
         */
1047
        getPreviousSiblingBy: function(node, method) {
1048
            while (node) {
1049
                node = node.previousSibling;
1050
                if ( Y.Dom._testElement(node, method) ) {
1051
                    return node;
1052
                }
1053
            }
1054
            return null;
1055
        }, 
1056

    
1057
        /**
1058
         * Returns the previous sibling that is an HTMLElement 
1059
         * @method getPreviousSibling
1060
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
1061
         * @return {Object} HTMLElement or null if not found
1062
         */
1063
        getPreviousSibling: function(node) {
1064
            node = Y.Dom.get(node);
1065
            if (!node) {
1066
                YAHOO.log('getPreviousSibling failed: invalid node argument', 'error', 'Dom');
1067
                return null;
1068
            }
1069

    
1070
            return Y.Dom.getPreviousSiblingBy(node);
1071
        }, 
1072

    
1073
        /**
1074
         * Returns the next HTMLElement sibling that passes the boolean method. 
1075
         * For performance reasons, IDs are not accepted and argument validation omitted.
1076
         * Returns the nearest HTMLElement sibling if no method provided.
1077
         * @method getNextSiblingBy
1078
         * @param {HTMLElement} node The HTMLElement to use as the starting point 
1079
         * @param {Function} method A boolean function used to test siblings
1080
         * that receives the sibling node being tested as its only argument
1081
         * @return {Object} HTMLElement or null if not found
1082
         */
1083
        getNextSiblingBy: function(node, method) {
1084
            while (node) {
1085
                node = node.nextSibling;
1086
                if ( Y.Dom._testElement(node, method) ) {
1087
                    return node;
1088
                }
1089
            }
1090
            return null;
1091
        }, 
1092

    
1093
        /**
1094
         * Returns the next sibling that is an HTMLElement 
1095
         * @method getNextSibling
1096
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
1097
         * @return {Object} HTMLElement or null if not found
1098
         */
1099
        getNextSibling: function(node) {
1100
            node = Y.Dom.get(node);
1101
            if (!node) {
1102
                YAHOO.log('getNextSibling failed: invalid node argument', 'error', 'Dom');
1103
                return null;
1104
            }
1105

    
1106
            return Y.Dom.getNextSiblingBy(node);
1107
        }, 
1108

    
1109
        /**
1110
         * Returns the first HTMLElement child that passes the test method. 
1111
         * @method getFirstChildBy
1112
         * @param {HTMLElement} node The HTMLElement to use as the starting point 
1113
         * @param {Function} method A boolean function used to test children
1114
         * that receives the node being tested as its only argument
1115
         * @return {Object} HTMLElement or null if not found
1116
         */
1117
        getFirstChildBy: function(node, method) {
1118
            var child = ( Y.Dom._testElement(node.firstChild, method) ) ? node.firstChild : null;
1119
            return child || Y.Dom.getNextSiblingBy(node.firstChild, method);
1120
        }, 
1121

    
1122
        /**
1123
         * Returns the first HTMLElement child. 
1124
         * @method getFirstChild
1125
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
1126
         * @return {Object} HTMLElement or null if not found
1127
         */
1128
        getFirstChild: function(node, method) {
1129
            node = Y.Dom.get(node);
1130
            if (!node) {
1131
                YAHOO.log('getFirstChild failed: invalid node argument', 'error', 'Dom');
1132
                return null;
1133
            }
1134
            return Y.Dom.getFirstChildBy(node);
1135
        }, 
1136

    
1137
        /**
1138
         * Returns the last HTMLElement child that passes the test method. 
1139
         * @method getLastChildBy
1140
         * @param {HTMLElement} node The HTMLElement to use as the starting point 
1141
         * @param {Function} method A boolean function used to test children
1142
         * that receives the node being tested as its only argument
1143
         * @return {Object} HTMLElement or null if not found
1144
         */
1145
        getLastChildBy: function(node, method) {
1146
            if (!node) {
1147
                YAHOO.log('getLastChild failed: invalid node argument', 'error', 'Dom');
1148
                return null;
1149
            }
1150
            var child = ( Y.Dom._testElement(node.lastChild, method) ) ? node.lastChild : null;
1151
            return child || Y.Dom.getPreviousSiblingBy(node.lastChild, method);
1152
        }, 
1153

    
1154
        /**
1155
         * Returns the last HTMLElement child. 
1156
         * @method getLastChild
1157
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
1158
         * @return {Object} HTMLElement or null if not found
1159
         */
1160
        getLastChild: function(node) {
1161
            node = Y.Dom.get(node);
1162
            return Y.Dom.getLastChildBy(node);
1163
        }, 
1164

    
1165
        /**
1166
         * Returns an array of HTMLElement childNodes that pass the test method. 
1167
         * @method getChildrenBy
1168
         * @param {HTMLElement} node The HTMLElement to start from
1169
         * @param {Function} method A boolean function used to test children
1170
         * that receives the node being tested as its only argument
1171
         * @return {Array} A static array of HTMLElements
1172
         */
1173
        getChildrenBy: function(node, method) {
1174
            var child = Y.Dom.getFirstChildBy(node, method),
1175
                children = child ? [child] : [];
1176

    
1177
            Y.Dom.getNextSiblingBy(child, function(node) {
1178
                if ( !method || method(node) ) {
1179
                    children[children.length] = node;
1180
                }
1181
                return false; // fail test to collect all children
1182
            });
1183

    
1184
            return children;
1185
        },
1186
 
1187
        /**
1188
         * Returns an array of HTMLElement childNodes. 
1189
         * @method getChildren
1190
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point 
1191
         * @return {Array} A static array of HTMLElements
1192
         */
1193
        getChildren: function(node) {
1194
            node = Y.Dom.get(node);
1195
            if (!node) {
1196
                YAHOO.log('getChildren failed: invalid node argument', 'error', 'Dom');
1197
            }
1198

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

    
1202
        /**
1203
         * Returns the left scroll value of the document 
1204
         * @method getDocumentScrollLeft
1205
         * @param {HTMLDocument} document (optional) The document to get the scroll value of
1206
         * @return {Int}  The amount that the document is scrolled to the left
1207
         */
1208
        getDocumentScrollLeft: function(doc) {
1209
            doc = doc || document;
1210
            return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
1211
        }, 
1212

    
1213
        /**
1214
         * Returns the top scroll value of the document 
1215
         * @method getDocumentScrollTop
1216
         * @param {HTMLDocument} document (optional) The document to get the scroll value of
1217
         * @return {Int}  The amount that the document is scrolled to the top
1218
         */
1219
        getDocumentScrollTop: function(doc) {
1220
            doc = doc || document;
1221
            return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
1222
        },
1223

    
1224
        /**
1225
         * Inserts the new node as the previous sibling of the reference node 
1226
         * @method insertBefore
1227
         * @param {String | HTMLElement} newNode The node to be inserted
1228
         * @param {String | HTMLElement} referenceNode The node to insert the new node before 
1229
         * @return {HTMLElement} The node that was inserted (or null if insert fails) 
1230
         */
1231
        insertBefore: function(newNode, referenceNode) {
1232
            newNode = Y.Dom.get(newNode); 
1233
            referenceNode = Y.Dom.get(referenceNode); 
1234
            
1235
            if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
1236
                YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'Dom');
1237
                return null;
1238
            }       
1239

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

    
1243
        /**
1244
         * Inserts the new node as the next sibling of the reference node 
1245
         * @method insertAfter
1246
         * @param {String | HTMLElement} newNode The node to be inserted
1247
         * @param {String | HTMLElement} referenceNode The node to insert the new node after 
1248
         * @return {HTMLElement} The node that was inserted (or null if insert fails) 
1249
         */
1250
        insertAfter: function(newNode, referenceNode) {
1251
            newNode = Y.Dom.get(newNode); 
1252
            referenceNode = Y.Dom.get(referenceNode); 
1253
            
1254
            if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
1255
                YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'Dom');
1256
                return null;
1257
            }       
1258

    
1259
            if (referenceNode.nextSibling) {
1260
                return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode.nextSibling); 
1261
            } else {
1262
                return referenceNode[PARENT_NODE].appendChild(newNode);
1263
            }
1264
        },
1265

    
1266
        /**
1267
         * Creates a Region based on the viewport relative to the document. 
1268
         * @method getClientRegion
1269
         * @return {Region} A Region object representing the viewport which accounts for document scroll
1270
         */
1271
        getClientRegion: function() {
1272
            var t = Y.Dom.getDocumentScrollTop(),
1273
                l = Y.Dom.getDocumentScrollLeft(),
1274
                r = Y.Dom.getViewportWidth() + l,
1275
                b = Y.Dom.getViewportHeight() + t;
1276

    
1277
            return new Y.Region(t, r, b, l);
1278
        },
1279

    
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
        },
1290

    
1291
        _setAttribute: function(el, args) {
1292
            var attr = Y.Dom._toCamel(args.attr),
1293
                val = args.val;
1294

    
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);
1301
                }
1302
            } else {
1303
                YAHOO.log('setAttribute method not available for ' + el, 'error', 'Dom');
1304
            }
1305
        },
1306

    
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
        },
1317

    
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);
1355
                    }
1356
                }
1357
            }
1358
            return re;
1359
        },
1360

    
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;
1383
        }
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
    }
1417
})();
1418
/**
1419
 * A region is a representation of an object on a grid.  It is defined
1420
 * by the top, right, bottom, left extents, so is rectangular by default.  If 
1421
 * other shapes are required, this class could be extended to support it.
1422
 * @namespace YAHOO.util
1423
 * @class Region
1424
 * @param {Int} t the top extent
1425
 * @param {Int} r the right extent
1426
 * @param {Int} b the bottom extent
1427
 * @param {Int} l the left extent
1428
 * @constructor
1429
 */
1430
YAHOO.util.Region = function(t, r, b, l) {
1431

    
1432
    /**
1433
     * The region's top extent
1434
     * @property top
1435
     * @type Int
1436
     */
1437
    this.top = t;
1438
    
1439
    /**
1440
     * The region's top extent
1441
     * @property y
1442
     * @type Int
1443
     */
1444
    this.y = t;
1445
    
1446
    /**
1447
     * The region's top extent as index, for symmetry with set/getXY
1448
     * @property 1
1449
     * @type Int
1450
     */
1451
    this[1] = t;
1452

    
1453
    /**
1454
     * The region's right extent
1455
     * @property right
1456
     * @type int
1457
     */
1458
    this.right = r;
1459

    
1460
    /**
1461
     * The region's bottom extent
1462
     * @property bottom
1463
     * @type Int
1464
     */
1465
    this.bottom = b;
1466

    
1467
    /**
1468
     * The region's left extent
1469
     * @property left
1470
     * @type Int
1471
     */
1472
    this.left = l;
1473
    
1474
    /**
1475
     * The region's left extent
1476
     * @property x
1477
     * @type Int
1478
     */
1479
    this.x = l;
1480
    
1481
    /**
1482
     * The region's left extent as index, for symmetry with set/getXY
1483
     * @property 0
1484
     * @type Int
1485
     */
1486
    this[0] = l;
1487

    
1488
    /**
1489
     * The region's total width 
1490
     * @property width 
1491
     * @type Int
1492
     */
1493
    this.width = this.right - this.left;
1494

    
1495
    /**
1496
     * The region's total height 
1497
     * @property height 
1498
     * @type Int
1499
     */
1500
    this.height = this.bottom - this.top;
1501
};
1502

    
1503
/**
1504
 * Returns true if this region contains the region passed in
1505
 * @method contains
1506
 * @param  {Region}  region The region to evaluate
1507
 * @return {Boolean}        True if the region is contained with this region, 
1508
 *                          else false
1509
 */
1510
YAHOO.util.Region.prototype.contains = function(region) {
1511
    return ( region.left   >= this.left   && 
1512
             region.right  <= this.right  && 
1513
             region.top    >= this.top    && 
1514
             region.bottom <= this.bottom    );
1515

    
1516
    // this.logger.debug("does " + this + " contain " + region + " ... " + ret);
1517
};
1518

    
1519
/**
1520
 * Returns the area of the region
1521
 * @method getArea
1522
 * @return {Int} the region's area
1523
 */
1524
YAHOO.util.Region.prototype.getArea = function() {
1525
    return ( (this.bottom - this.top) * (this.right - this.left) );
1526
};
1527

    
1528
/**
1529
 * Returns the region where the passed in region overlaps with this one
1530
 * @method intersect
1531
 * @param  {Region} region The region that intersects
1532
 * @return {Region}        The overlap region, or null if there is no overlap
1533
 */
1534
YAHOO.util.Region.prototype.intersect = function(region) {
1535
    var t = Math.max( this.top,    region.top    ),
1536
        r = Math.min( this.right,  region.right  ),
1537
        b = Math.min( this.bottom, region.bottom ),
1538
        l = Math.max( this.left,   region.left   );
1539
    
1540
    if (b >= t && r >= l) {
1541
        return new YAHOO.util.Region(t, r, b, l);
1542
    } else {
1543
        return null;
1544
    }
1545
};
1546

    
1547
/**
1548
 * Returns the region representing the smallest region that can contain both
1549
 * the passed in region and this region.
1550
 * @method union
1551
 * @param  {Region} region The region that to create the union with
1552
 * @return {Region}        The union region
1553
 */
1554
YAHOO.util.Region.prototype.union = function(region) {
1555
    var t = Math.min( this.top,    region.top    ),
1556
        r = Math.max( this.right,  region.right  ),
1557
        b = Math.max( this.bottom, region.bottom ),
1558
        l = Math.min( this.left,   region.left   );
1559

    
1560
    return new YAHOO.util.Region(t, r, b, l);
1561
};
1562

    
1563
/**
1564
 * toString
1565
 * @method toString
1566
 * @return string the region properties
1567
 */
1568
YAHOO.util.Region.prototype.toString = function() {
1569
    return ( "Region {"    +
1570
             "top: "       + this.top    + 
1571
             ", right: "   + this.right  + 
1572
             ", bottom: "  + this.bottom + 
1573
             ", left: "    + this.left   + 
1574
             ", height: "  + this.height + 
1575
             ", width: "    + this.width   + 
1576
             "}" );
1577
};
1578

    
1579
/**
1580
 * Returns a region that is occupied by the DOM element
1581
 * @method getRegion
1582
 * @param  {HTMLElement} el The element
1583
 * @return {Region}         The region that the element occupies
1584
 * @static
1585
 */
1586
YAHOO.util.Region.getRegion = function(el) {
1587
    var p = YAHOO.util.Dom.getXY(el),
1588
        t = p[1],
1589
        r = p[0] + el.offsetWidth,
1590
        b = p[1] + el.offsetHeight,
1591
        l = p[0];
1592

    
1593
    return new YAHOO.util.Region(t, r, b, l);
1594
};
1595

    
1596
/////////////////////////////////////////////////////////////////////////////
1597

    
1598

    
1599
/**
1600
 * A point is a region that is special in that it represents a single point on 
1601
 * the grid.
1602
 * @namespace YAHOO.util
1603
 * @class Point
1604
 * @param {Int} x The X position of the point
1605
 * @param {Int} y The Y position of the point
1606
 * @constructor
1607
 * @extends YAHOO.util.Region
1608
 */
1609
YAHOO.util.Point = function(x, y) {
1610
   if (YAHOO.lang.isArray(x)) { // accept input from Dom.getXY, Event.getXY, etc.
1611
      y = x[1]; // dont blow away x yet
1612
      x = x[0];
1613
   }
1614
 
1615
    YAHOO.util.Point.superclass.constructor.call(this, y, x, y, x);
1616
};
1617

    
1618
YAHOO.extend(YAHOO.util.Point, YAHOO.util.Region);
1619

    
1620
(function() {
1621
/**
1622
 * Add style management functionality to DOM.
1623
 * @module dom
1624
 * @for Dom
1625
 */
1626

    
1627
var Y = YAHOO.util, 
1628
    CLIENT_TOP = 'clientTop',
1629
    CLIENT_LEFT = 'clientLeft',
1630
    PARENT_NODE = 'parentNode',
1631
    RIGHT = 'right',
1632
    HAS_LAYOUT = 'hasLayout',
1633
    PX = 'px',
1634
    OPACITY = 'opacity',
1635
    AUTO = 'auto',
1636
    BORDER_LEFT_WIDTH = 'borderLeftWidth',
1637
    BORDER_TOP_WIDTH = 'borderTopWidth',
1638
    BORDER_RIGHT_WIDTH = 'borderRightWidth',
1639
    BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
1640
    VISIBLE = 'visible',
1641
    TRANSPARENT = 'transparent',
1642
    HEIGHT = 'height',
1643
    WIDTH = 'width',
1644
    STYLE = 'style',
1645
    CURRENT_STYLE = 'currentStyle',
1646

    
1647
// IE getComputedStyle
1648
// TODO: unit-less lineHeight (e.g. 1.22)
1649
    re_size = /^width|height$/,
1650
    re_unit = /^(\d[.\d]*)+(em|ex|px|gd|rem|vw|vh|vm|ch|mm|cm|in|pt|pc|deg|rad|ms|s|hz|khz|%){1}?/i,
1651

    
1652
    ComputedStyle = {
1653
        get: function(el, property) {
1654
            var value = '',
1655
                current = el[CURRENT_STYLE][property];
1656

    
1657
            if (property === OPACITY) {
1658
                value = Y.Dom.getStyle(el, OPACITY);        
1659
            } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
1660
                value = current;
1661
            } else if (Y.Dom.IE_COMPUTED[property]) { // use compute function
1662
                value = Y.Dom.IE_COMPUTED[property](el, property);
1663
            } else if (re_unit.test(current)) { // convert to pixel
1664
                value = Y.Dom.IE.ComputedStyle.getPixel(el, property);
1665
            } else {
1666
                value = current;
1667
            }
1668

    
1669
            return value;
1670
        },
1671

    
1672
        getOffset: function(el, prop) {
1673
            var current = el[CURRENT_STYLE][prop],                        // value of "width", "top", etc.
1674
                capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
1675
                offset = 'offset' + capped,                             // "offsetWidth", "offsetTop", etc.
1676
                pixel = 'pixel' + capped,                               // "pixelWidth", "pixelTop", etc.
1677
                value = '',
1678
                actual;
1679

    
1680
            if (current == AUTO) {
1681
                actual = el[offset]; // offsetHeight/Top etc.
1682
                if (actual === undefined) { // likely "right" or "bottom"
1683
                    value = 0;
1684
                }
1685

    
1686
                value = actual;
1687
                if (re_size.test(prop)) { // account for box model diff 
1688
                    el[STYLE][prop] = actual; 
1689
                    if (el[offset] > actual) {
1690
                        // the difference is padding + border (works in Standards & Quirks modes)
1691
                        value = actual - (el[offset] - actual);
1692
                    }
1693
                    el[STYLE][prop] = AUTO; // revert to auto
1694
                }
1695
            } else { // convert units to px
1696
                if (!el[STYLE][pixel] && !el[STYLE][prop]) { // need to map style.width to currentStyle (no currentStyle.pixelWidth)
1697
                    el[STYLE][prop] = current;              // no style.pixelWidth if no style.width
1698
                }
1699
                value = el[STYLE][pixel];
1700
            }
1701
            return value + PX;
1702
        },
1703

    
1704
        getBorderWidth: function(el, property) {
1705
            // clientHeight/Width = paddingBox (e.g. offsetWidth - borderWidth)
1706
            // clientTop/Left = borderWidth
1707
            var value = null;
1708
            if (!el[CURRENT_STYLE][HAS_LAYOUT]) { // TODO: unset layout?
1709
                el[STYLE].zoom = 1; // need layout to measure client
1710
            }
1711

    
1712
            switch(property) {
1713
                case BORDER_TOP_WIDTH:
1714
                    value = el[CLIENT_TOP];
1715
                    break;
1716
                case BORDER_BOTTOM_WIDTH:
1717
                    value = el.offsetHeight - el.clientHeight - el[CLIENT_TOP];
1718
                    break;
1719
                case BORDER_LEFT_WIDTH:
1720
                    value = el[CLIENT_LEFT];
1721
                    break;
1722
                case BORDER_RIGHT_WIDTH:
1723
                    value = el.offsetWidth - el.clientWidth - el[CLIENT_LEFT];
1724
                    break;
1725
            }
1726
            return value + PX;
1727
        },
1728

    
1729
        getPixel: function(node, att) {
1730
            // use pixelRight to convert to px
1731
            var val = null,
1732
                styleRight = node[CURRENT_STYLE][RIGHT],
1733
                current = node[CURRENT_STYLE][att];
1734

    
1735
            node[STYLE][RIGHT] = current;
1736
            val = node[STYLE].pixelRight;
1737
            node[STYLE][RIGHT] = styleRight; // revert
1738

    
1739
            return val + PX;
1740
        },
1741

    
1742
        getMargin: function(node, att) {
1743
            var val;
1744
            if (node[CURRENT_STYLE][att] == AUTO) {
1745
                val = 0 + PX;
1746
            } else {
1747
                val = Y.Dom.IE.ComputedStyle.getPixel(node, att);
1748
            }
1749
            return val;
1750
        },
1751

    
1752
        getVisibility: function(node, att) {
1753
            var current;
1754
            while ( (current = node[CURRENT_STYLE]) && current[att] == 'inherit') { // NOTE: assignment in test
1755
                node = node[PARENT_NODE];
1756
            }
1757
            return (current) ? current[att] : VISIBLE;
1758
        },
1759

    
1760
        getColor: function(node, att) {
1761
            return Y.Dom.Color.toRGB(node[CURRENT_STYLE][att]) || TRANSPARENT;
1762
        },
1763

    
1764
        getBorderColor: function(node, att) {
1765
            var current = node[CURRENT_STYLE],
1766
                val = current[att] || current.color;
1767
            return Y.Dom.Color.toRGB(Y.Dom.Color.toHex(val));
1768
        }
1769

    
1770
    },
1771

    
1772
//fontSize: getPixelFont,
1773
    IEComputed = {};
1774

    
1775
IEComputed.top = IEComputed.right = IEComputed.bottom = IEComputed.left = 
1776
        IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
1777

    
1778
IEComputed.color = ComputedStyle.getColor;
1779

    
1780
IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
1781
        IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
1782
        ComputedStyle.getBorderWidth;
1783

    
1784
IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
1785
        IEComputed.marginLeft = ComputedStyle.getMargin;
1786

    
1787
IEComputed.visibility = ComputedStyle.getVisibility;
1788
IEComputed.borderColor = IEComputed.borderTopColor =
1789
        IEComputed.borderRightColor = IEComputed.borderBottomColor =
1790
        IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
1791

    
1792
Y.Dom.IE_COMPUTED = IEComputed;
1793
Y.Dom.IE_ComputedStyle = ComputedStyle;
1794
})();
1795
(function() {
1796
/**
1797
 * Add style management functionality to DOM.
1798
 * @module dom
1799
 * @for Dom
1800
 */
1801

    
1802
var TO_STRING = 'toString',
1803
    PARSE_INT = parseInt,
1804
    RE = RegExp,
1805
    Y = YAHOO.util;
1806

    
1807
Y.Dom.Color = {
1808
    KEYWORDS: {
1809
        black: '000',
1810
        silver: 'c0c0c0',
1811
        gray: '808080',
1812
        white: 'fff',
1813
        maroon: '800000',
1814
        red: 'f00',
1815
        purple: '800080',
1816
        fuchsia: 'f0f',
1817
        green: '008000',
1818
        lime: '0f0',
1819
        olive: '808000',
1820
        yellow: 'ff0',
1821
        navy: '000080',
1822
        blue: '00f',
1823
        teal: '008080',
1824
        aqua: '0ff'
1825
    },
1826

    
1827
    re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
1828
    re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
1829
    re_hex3: /([0-9A-F])/gi,
1830

    
1831
    toRGB: function(val) {
1832
        if (!Y.Dom.Color.re_RGB.test(val)) {
1833
            val = Y.Dom.Color.toHex(val);
1834
        }
1835

    
1836
        if(Y.Dom.Color.re_hex.exec(val)) {
1837
            val = 'rgb(' + [
1838
                PARSE_INT(RE.$1, 16),
1839
                PARSE_INT(RE.$2, 16),
1840
                PARSE_INT(RE.$3, 16)
1841
            ].join(', ') + ')';
1842
        }
1843
        return val;
1844
    },
1845

    
1846
    toHex: function(val) {
1847
        val = Y.Dom.Color.KEYWORDS[val] || val;
1848
        if (Y.Dom.Color.re_RGB.exec(val)) {
1849
            var r = (RE.$1.length === 1) ? '0' + RE.$1 : Number(RE.$1),
1850
                g = (RE.$2.length === 1) ? '0' + RE.$2 : Number(RE.$2),
1851
                b = (RE.$3.length === 1) ? '0' + RE.$3 : Number(RE.$3);
1852

    
1853
            val = [
1854
                r[TO_STRING](16),
1855
                g[TO_STRING](16),
1856
                b[TO_STRING](16)
1857
            ].join('');
1858
        }
1859

    
1860
        if (val.length < 6) {
1861
            val = val.replace(Y.Dom.Color.re_hex3, '$1$1');
1862
        }
1863

    
1864
        if (val !== 'transparent' && val.indexOf('#') < 0) {
1865
            val = '#' + val;
1866
        }
1867

    
1868
        return val.toLowerCase();
1869
    }
1870
};
1871
}());
1872
YAHOO.register("dom", YAHOO.util.Dom, {version: "2.8.0r4", build: "2449"});
(2-2/5)