Project

General

Profile

1 601 doc
/*
2 1289 kweitzel
Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 601 doc
Code licensed under the BSD License:
4
http://developer.yahoo.net/yui/license.txt
5 1289 kweitzel
version: 2.8.0r4
6 601 doc
*/
7
/**
8
 * The dom module provides helper methods for manipulating Dom elements.
9
 * @module dom
10
 *
11
 */
12
13
(function() {
14 1289 kweitzel
    // 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 601 doc
        propertyCache = {}, // for faster hyphen converts
23 1289 kweitzel
        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 601 doc
65
    // brower detection
66 1289 kweitzel
        isOpera = UA.opera,
67
        isSafari = UA.webkit,
68
        isGecko = UA.gecko,
69
        isIE = UA.ie;
70 601 doc
71
    /**
72
     * Provides helper methods for DOM elements.
73
     * @namespace YAHOO.util
74
     * @class Dom
75 1289 kweitzel
     * @requires yahoo, event
76 601 doc
     */
77 1289 kweitzel
    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 601 doc
        /**
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 1289 kweitzel
            var id, nodes, c, i, len, attr;
96 601 doc
97 1289 kweitzel
            if (el) {
98
                if (el[NODE_TYPE] || el.item) { // Node, or NodeList
99
                    return el;
100 601 doc
                }
101 1289 kweitzel
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 601 doc
120 1289 kweitzel
                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 601 doc
            }
135
136 1289 kweitzel
            return null;
137 601 doc
        },
138
139 1289 kweitzel
        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 601 doc
        /**
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 1289 kweitzel
            return Y.Dom.batch(el, Y.Dom._getStyle, property);
156 601 doc
        },
157 1289 kweitzel
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 601 doc
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 1289 kweitzel
            Y.Dom.batch(el, Y.Dom._setStyle, { prop: property, val: val });
216 601 doc
        },
217 1289 kweitzel
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 601 doc
261
        /**
262 1289 kweitzel
         * 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 601 doc
         * @method getXY
265 1289 kweitzel
         * @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 601 doc
         * @return {Array} The XY position of the element(s)
268
         */
269
        getXY: function(el) {
270 1289 kweitzel
            return Y.Dom.batch(el, Y.Dom._getXY);
271 601 doc
        },
272 1289 kweitzel
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 601 doc
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 1289 kweitzel
            Y.Dom.batch(el, Y.Dom._setXY, { pos: pos, noRetry: noRetry });
439
        },
440 601 doc
441 1289 kweitzel
        _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 601 doc
455 1289 kweitzel
            if (pos == 'static') { // default to relative
456
                pos = RELATIVE;
457
                setStyle(node, POSITION, pos);
458
            }
459 601 doc
460 1289 kweitzel
            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 601 doc
467 1289 kweitzel
            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 601 doc
        },
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 1289 kweitzel
                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 601 doc
                }
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 1289 kweitzel
         * Returns an array of HTMLElements with the given class.
562 601 doc
         * For optimized performance, include a tag and/or root node when possible.
563 1289 kweitzel
         * 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 601 doc
         * @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 1289 kweitzel
         * @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 601 doc
         * @param {Function} apply (optional) A function to apply to each element when found
573 1289 kweitzel
         * @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 601 doc
         * @return {Array} An array of elements that have the given class name
576
         */
577 1289 kweitzel
        getElementsByClassName: function(className, tag, root, apply, o, overrides) {
578 601 doc
            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 1289 kweitzel
                hasClass = Y.Dom.hasClass;
587 601 doc
588
            for (var i = 0, len = elements.length; i < len; ++i) {
589 1289 kweitzel
                if ( hasClass(elements[i], className) ) {
590 601 doc
                    nodes[nodes.length] = elements[i];
591
                }
592
            }
593
594 1289 kweitzel
            if (apply) {
595
                Y.Dom.batch(nodes, apply, o, overrides);
596
            }
597
598 601 doc
            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 1289 kweitzel
            return Y.Dom.batch(el, Y.Dom._hasClass, className);
610
        },
611 601 doc
612 1289 kweitzel
        _hasClass: function(el, className) {
613
            var ret = false,
614
                current;
615 601 doc
616 1289 kweitzel
            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 601 doc
        },
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 1289 kweitzel
            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 601 doc
                }
652 1289 kweitzel
            } else {
653
                YAHOO.log('addClass called with invalid arguments', 'warn', 'Dom');
654
            }
655
656
            return ret;
657 601 doc
        },
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 1289 kweitzel
            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 601 doc
676 1289 kweitzel
            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 601 doc
                }
691
692 1289 kweitzel
            } else {
693
                YAHOO.log('removeClass called with invalid arguments', 'warn', 'Dom');
694
            }
695
696
            return ret;
697 601 doc
        },
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 1289 kweitzel
            return Y.Dom.batch(el, Y.Dom._replaceClass, { from: oldClassName, to: newClassName });
710
        },
711 601 doc
712 1289 kweitzel
        _replaceClass: function(el, classObj) {
713
            var className,
714
                from,
715
                to,
716
                ret = false,
717
                current;
718 601 doc
719 1289 kweitzel
            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 601 doc
                }
738 1289 kweitzel
            } else {
739
                YAHOO.log('replaceClass called with invalid arguments', 'warn', 'Dom');
740
            }
741 601 doc
742 1289 kweitzel
            return ret;
743 601 doc
        },
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 1289 kweitzel
                }
760 601 doc
761 1289 kweitzel
                var id = prefix + YAHOO.env._id_counter++;
762 601 doc
                YAHOO.log('generateId generating ' + id, 'info', 'Dom');
763
764
                if (el) {
765 1289 kweitzel
                    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 601 doc
                    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 1289 kweitzel
            var ret = false;
791 601 doc
792 1289 kweitzel
            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 601 doc
            }
802 1289 kweitzel
            YAHOO.log('isAncestor(' + haystack + ',' + needle + ' returning ' + ret, 'info', 'Dom');
803
            return ret;
804 601 doc
        },
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 1289 kweitzel
         * @param {Object} doc An optional document to search, defaults to element's owner document
811 601 doc
         * @return {Boolean} Whether or not the element is present in the current document
812
         */
813 1289 kweitzel
        inDocument: function(el, doc) {
814
            return Y.Dom._inDoc(Y.Dom.get(el), doc);
815 601 doc
        },
816 1289 kweitzel
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 601 doc
828
        /**
829 1289 kweitzel
         * Returns an array of HTMLElements that pass the test applied by supplied boolean method.
830 601 doc
         * For optimized performance, include a tag and/or root node when possible.
831 1289 kweitzel
         * 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 601 doc
         * @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 1289 kweitzel
         * @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 601 doc
         * @return {Array} Array of HTMLElements
843
         */
844 1289 kweitzel
        getElementsBy: function(method, tag, root, apply, o, overrides, firstOnly) {
845 601 doc
            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 1289 kweitzel
                    if (firstOnly) {
858
                        nodes = elements[i];
859
                        break;
860
                    } else {
861
                        nodes[nodes.length] = elements[i];
862 601 doc
                    }
863
                }
864
            }
865
866 1289 kweitzel
            if (apply) {
867
                Y.Dom.batch(nodes, apply, o, overrides);
868
            }
869
870 601 doc
            YAHOO.log('getElementsBy returning ' + nodes, 'info', 'Dom');
871
872
            return nodes;
873
        },
874
875
        /**
876 1289 kweitzel
         * 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 601 doc
         * 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 1289 kweitzel
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
895 601 doc
         * @return {Any | Array} The return value(s) from the supplied method
896
         */
897 1289 kweitzel
        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 601 doc
907 1289 kweitzel
                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 601 doc
                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 1289 kweitzel
            var scrollHeight = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollHeight : documentElement.scrollHeight,
924
                h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
925 601 doc
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 1289 kweitzel
            var scrollWidth = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollWidth : documentElement.scrollWidth,
937
                w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
938 601 doc
            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 1289 kweitzel
            var height = self.innerHeight, // Safari, Opera
949
                mode = document[COMPAT_MODE];
950 601 doc
951
            if ( (mode || isIE) && !isOpera ) { // IE, Gecko
952 1289 kweitzel
                height = (mode == CSS1_COMPAT) ?
953
                        documentElement.clientHeight : // Standards
954 601 doc
                        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 1289 kweitzel
            var width = self.innerWidth,  // Safari
969
                mode = document[COMPAT_MODE];
970 601 doc
971
            if (mode || isIE) { // IE, Gecko, Opera
972 1289 kweitzel
                width = (mode == CSS1_COMPAT) ?
973
                        documentElement.clientWidth : // Standards
974 601 doc
                        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 1289 kweitzel
            while ( (node = node[PARENT_NODE]) ) { // NOTE: assignment
990
                if ( Y.Dom._testElement(node, method) ) {
991 601 doc
                    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 1289 kweitzel
                 return el[TAG_NAME] && el[TAG_NAME].toUpperCase() == tagName.toUpperCase();
1032 601 doc
            };
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 1289 kweitzel
                if ( Y.Dom._testElement(node, method) ) {
1051 601 doc
                    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 1289 kweitzel
                if ( Y.Dom._testElement(node, method) ) {
1087 601 doc
                    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 1289 kweitzel
            var child = ( Y.Dom._testElement(node.firstChild, method) ) ? node.firstChild : null;
1119 601 doc
            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 1289 kweitzel
            var child = ( Y.Dom._testElement(node.lastChild, method) ) ? node.lastChild : null;
1151 601 doc
            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 1289 kweitzel
            var child = Y.Dom.getFirstChildBy(node, method),
1175
                children = child ? [child] : [];
1176 601 doc
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 1289 kweitzel
1202 601 doc
        /**
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 1289 kweitzel
            return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
1211 601 doc
        },
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 1289 kweitzel
            return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
1222 601 doc
        },
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 1289 kweitzel
            if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
1236 601 doc
                YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'Dom');
1237
                return null;
1238
            }
1239
1240 1289 kweitzel
            return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode);
1241 601 doc
        },
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 1289 kweitzel
            if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
1255 601 doc
                YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'Dom');
1256
                return null;
1257
            }
1258
1259
            if (referenceNode.nextSibling) {
1260 1289 kweitzel
                return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode.nextSibling);
1261 601 doc
            } else {
1262 1289 kweitzel
                return referenceNode[PARENT_NODE].appendChild(newNode);
1263 601 doc
            }
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 1289 kweitzel
        },
1279 601 doc
1280 1289 kweitzel
        /**
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 601 doc
1291 1289 kweitzel
        _setAttribute: function(el, args) {
1292
            var attr = Y.Dom._toCamel(args.attr),
1293
                val = args.val;
1294 601 doc
1295 1289 kweitzel
            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 601 doc
                }
1302 1289 kweitzel
            } else {
1303
                YAHOO.log('setAttribute method not available for ' + el, 'error', 'Dom');
1304
            }
1305
        },
1306 601 doc
1307 1289 kweitzel
        /**
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 601 doc
1318 1289 kweitzel
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 601 doc
                    }
1356
                }
1357 1289 kweitzel
            }
1358
            return re;
1359
        },
1360 601 doc
1361 1289 kweitzel
        _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 601 doc
        }
1384 1289 kweitzel
    };
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 601 doc
})();
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 1289 kweitzel
     * The region's top extent
1441
     * @property y
1442
     * @type Int
1443
     */
1444
    this.y = t;
1445
1446
    /**
1447 601 doc
     * 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 1289 kweitzel
     * The region's left extent
1476
     * @property x
1477
     * @type Int
1478
     */
1479
    this.x = l;
1480
1481
    /**
1482 601 doc
     * The region's left extent as index, for symmetry with set/getXY
1483
     * @property 0
1484
     * @type Int
1485
     */
1486
    this[0] = l;
1487 1289 kweitzel
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 601 doc
};
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 1289 kweitzel
    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 601 doc
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 1289 kweitzel
    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 601 doc
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 1289 kweitzel
             ", height: "  + this.height +
1575
             ", width: "    + this.width   +
1576 601 doc
             "}" );
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 1289 kweitzel
    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 601 doc
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 1289 kweitzel
1615
    YAHOO.util.Point.superclass.constructor.call(this, y, x, y, x);
1616 601 doc
};
1617
1618 1289 kweitzel
YAHOO.extend(YAHOO.util.Point, YAHOO.util.Region);
1619 601 doc
1620 1289 kweitzel
(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"});