Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
YUI.add('yui2-dom', function(Y) {
2
    var YAHOO    = Y.YUI2;
3
    /*
4
Copyright (c) 2011, Yahoo! Inc. All rights reserved.
5
Code licensed under the BSD License:
6
http://developer.yahoo.com/yui/license.html
7
version: 2.9.0
8
*/
9
/**
10
 * The dom module provides helper methods for manipulating Dom elements.
11
 * @module dom
12
 *
13
 */
14
 
15
(function() {
16
    // for use with generateId (global to save state if Dom is overwritten)
17
    YAHOO.env._id_counter = YAHOO.env._id_counter || 0;
18
 
19
    // internal shorthand
20
    var Y = YAHOO.util,
21
        lang = YAHOO.lang,
22
        UA = YAHOO.env.ua,
23
        trim = YAHOO.lang.trim,
24
        propertyCache = {}, // for faster hyphen converts
25
        reCache = {}, // cache className regexes
26
        RE_TABLE = /^t(?:able|d|h)$/i, // for _calcBorders
27
        RE_COLOR = /color$/i,
28
 
29
        // DOM aliases
30
        document = window.document,
31
        documentElement = document.documentElement,
32
 
33
        // string constants
34
        OWNER_DOCUMENT = 'ownerDocument',
35
        DEFAULT_VIEW = 'defaultView',
36
        DOCUMENT_ELEMENT = 'documentElement',
37
        COMPAT_MODE = 'compatMode',
38
        OFFSET_LEFT = 'offsetLeft',
39
        OFFSET_TOP = 'offsetTop',
40
        OFFSET_PARENT = 'offsetParent',
41
        PARENT_NODE = 'parentNode',
42
        NODE_TYPE = 'nodeType',
43
        TAG_NAME = 'tagName',
44
        SCROLL_LEFT = 'scrollLeft',
45
        SCROLL_TOP = 'scrollTop',
46
        GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
47
        GET_COMPUTED_STYLE = 'getComputedStyle',
48
        CURRENT_STYLE = 'currentStyle',
49
        CSS1_COMPAT = 'CSS1Compat',
50
        _BACK_COMPAT = 'BackCompat',
51
        _CLASS = 'class', // underscore due to reserved word
52
        CLASS_NAME = 'className',
53
        EMPTY = '',
54
        SPACE = ' ',
55
        C_START = '(?:^|\\s)',
56
        C_END = '(?= |$)',
57
        G = 'g',
58
        POSITION = 'position',
59
        FIXED = 'fixed',
60
        RELATIVE = 'relative',
61
        LEFT = 'left',
62
        TOP = 'top',
63
        MEDIUM = 'medium',
64
        BORDER_LEFT_WIDTH = 'borderLeftWidth',
65
        BORDER_TOP_WIDTH = 'borderTopWidth',
66
 
67
    // brower detection
68
        isOpera = UA.opera,
69
        isSafari = UA.webkit,
70
        isGecko = UA.gecko,
71
        isIE = UA.ie;
72
 
73
    /**
74
     * Provides helper methods for DOM elements.
75
     * @namespace YAHOO.util
76
     * @class Dom
77
     * @requires yahoo, event
78
     */
79
    Y.Dom = {
80
        CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
81
            'for': 'htmlFor',
82
            'class': CLASS_NAME
83
        } : { // w3c
84
            'htmlFor': 'for',
85
            'className': _CLASS
86
        },
87
 
88
        DOT_ATTRIBUTES: {
89
            checked: true
90
        },
91
 
92
        /**
93
         * Returns an HTMLElement reference.
94
         * @method get
95
         * @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.
96
         * @return {HTMLElement | Array} A DOM reference to an HTML element or an array of HTMLElements.
97
         */
98
        get: function(el) {
99
            var id, nodes, c, i, len, attr, ret = null;
100
 
101
            if (el) {
102
                if (typeof el == 'string' || typeof el == 'number') { // 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
                        if (nodes && nodes.length) {
112
                            for (i = 0, len = nodes.length; i < len; ++i) {
113
                                if (nodes[i].id === id) {
114
                                    return nodes[i];
115
                                }
116
                            }
117
                        }
118
                    }
119
                } else if (Y.Element && el instanceof Y.Element) {
120
                    el = el.get('element');
121
                } else if (!el.nodeType && 'length' in el) { // array-like
122
                    c = [];
123
                    for (i = 0, len = el.length; i < len; ++i) {
124
                        c[c.length] = Y.Dom.get(el[i]);
125
                    }
126
 
127
                    el = c;
128
                }
129
 
130
                ret = el;
131
            }
132
 
133
            return ret;
134
        },
135
 
136
        getComputedStyle: function(el, property) {
137
            if (window[GET_COMPUTED_STYLE]) {
138
                return el[OWNER_DOCUMENT][DEFAULT_VIEW][GET_COMPUTED_STYLE](el, null)[property];
139
            } else if (el[CURRENT_STYLE]) {
140
                return Y.Dom.IE_ComputedStyle.get(el, property);
141
            }
142
        },
143
 
144
        /**
145
         * Normalizes currentStyle and ComputedStyle.
146
         * @method getStyle
147
         * @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.
148
         * @param {String} property The style property whose value is returned.
149
         * @return {String | Array} The current value of the style property for the element(s).
150
         */
151
        getStyle: function(el, property) {
152
            return Y.Dom.batch(el, Y.Dom._getStyle, property);
153
        },
154
 
155
        // branching at load instead of runtime
156
        _getStyle: function() {
157
            if (window[GET_COMPUTED_STYLE]) { // W3C DOM method
158
                return function(el, property) {
159
                    property = (property === 'float') ? property = 'cssFloat' :
160
                            Y.Dom._toCamel(property);
161
 
162
                    var value = el.style[property],
163
                        computed;
164
 
165
                    if (!value) {
166
                        computed = el[OWNER_DOCUMENT][DEFAULT_VIEW][GET_COMPUTED_STYLE](el, null);
167
                        if (computed) { // test computed before touching for safari
168
                            value = computed[property];
169
                        }
170
                    }
171
 
172
                    return value;
173
                };
174
            } else if (documentElement[CURRENT_STYLE]) {
175
                return function(el, property) {
176
                    var value;
177
 
178
                    switch(property) {
179
                        case 'opacity' :// IE opacity uses filter
180
                            value = 100;
181
                            try { // will error if no DXImageTransform
182
                                value = el.filters['DXImageTransform.Microsoft.Alpha'].opacity;
183
 
184
                            } catch(e) {
185
                                try { // make sure its in the document
186
                                    value = el.filters('alpha').opacity;
187
                                } catch(err) {
188
                                }
189
                            }
190
                            return value / 100;
191
                        case 'float': // fix reserved word
192
                            property = 'styleFloat'; // fall through
193
                        default:
194
                            property = Y.Dom._toCamel(property);
195
                            value = el[CURRENT_STYLE] ? el[CURRENT_STYLE][property] : null;
196
                            return ( el.style[property] || value );
197
                    }
198
                };
199
            }
200
        }(),
201
 
202
        /**
203
         * Wrapper for setting style properties of HTMLElements.  Normalizes "opacity" across modern browsers.
204
         * @method setStyle
205
         * @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.
206
         * @param {String} property The style property to be set.
207
         * @param {String} val The value to apply to the given property.
208
         */
209
        setStyle: function(el, property, val) {
210
            Y.Dom.batch(el, Y.Dom._setStyle, { prop: property, val: val });
211
        },
212
 
213
        _setStyle: function() {
214
            if (!window.getComputedStyle && document.documentElement.currentStyle) {
215
                return function(el, args) {
216
                    var property = Y.Dom._toCamel(args.prop),
217
                        val = args.val;
218
 
219
                    if (el) {
220
                        switch (property) {
221
                            case 'opacity':
222
                                // remove filter if unsetting or full opacity
223
                                if (val === '' || val === null || val === 1) {
224
                                    el.style.removeAttribute('filter');
225
                                } else if ( lang.isString(el.style.filter) ) { // in case not appended
226
                                    el.style.filter = 'alpha(opacity=' + val * 100 + ')';
227
 
228
                                    if (!el[CURRENT_STYLE] || !el[CURRENT_STYLE].hasLayout) {
229
                                        el.style.zoom = 1; // when no layout or cant tell
230
                                    }
231
                                }
232
                                break;
233
                            case 'float':
234
                                property = 'styleFloat';
235
                            default:
236
                            el.style[property] = val;
237
                        }
238
                    } else {
239
                    }
240
                };
241
            } else {
242
                return function(el, args) {
243
                    var property = Y.Dom._toCamel(args.prop),
244
                        val = args.val;
245
                    if (el) {
246
                        if (property == 'float') {
247
                            property = 'cssFloat';
248
                        }
249
                        el.style[property] = val;
250
                    } else {
251
                    }
252
                };
253
            }
254
 
255
        }(),
256
 
257
        /**
258
         * Gets the current position of an element based on page coordinates.
259
         * Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
260
         * @method getXY
261
         * @param {String | HTMLElement | Array} el Accepts a string to use as an ID, an actual DOM
262
         * reference, or an Array of IDs and/or HTMLElements
263
         * @return {Array} The XY position of the element(s)
264
         */
265
        getXY: function(el) {
266
            return Y.Dom.batch(el, Y.Dom._getXY);
267
        },
268
 
269
        _canPosition: function(el) {
270
            return ( Y.Dom._getStyle(el, 'display') !== 'none' && Y.Dom._inDoc(el) );
271
        },
272
 
273
        _getXY: function(node) {
274
            var scrollLeft, scrollTop, box, doc,
275
                clientTop, clientLeft,
276
                round = Math.round, // TODO: round?
277
                xy = false;
278
 
279
            if (Y.Dom._canPosition(node)) {
280
                box = node[GET_BOUNDING_CLIENT_RECT]();
281
                doc = node[OWNER_DOCUMENT];
282
                scrollLeft = Y.Dom.getDocumentScrollLeft(doc);
283
                scrollTop = Y.Dom.getDocumentScrollTop(doc);
284
                xy = [box[LEFT], box[TOP]];
285
 
286
                // remove IE default documentElement offset (border)
287
                if (clientTop || clientLeft) {
288
                    xy[0] -= clientLeft;
289
                    xy[1] -= clientTop;
290
                }
291
 
292
                if ((scrollTop || scrollLeft)) {
293
                    xy[0] += scrollLeft;
294
                    xy[1] += scrollTop;
295
                }
296
 
297
                // gecko may return sub-pixel (non-int) values
298
                xy[0] = round(xy[0]);
299
                xy[1] = round(xy[1]);
300
            } else {
301
            }
302
 
303
            return xy;
304
        },
305
 
306
        /**
307
         * 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).
308
         * @method getX
309
         * @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
310
         * @return {Number | Array} The X position of the element(s)
311
         */
312
        getX: function(el) {
313
            var f = function(el) {
314
                return Y.Dom.getXY(el)[0];
315
            };
316
 
317
            return Y.Dom.batch(el, f, Y.Dom, true);
318
        },
319
 
320
        /**
321
         * 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).
322
         * @method getY
323
         * @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
324
         * @return {Number | Array} The Y position of the element(s)
325
         */
326
        getY: function(el) {
327
            var f = function(el) {
328
                return Y.Dom.getXY(el)[1];
329
            };
330
 
331
            return Y.Dom.batch(el, f, Y.Dom, true);
332
        },
333
 
334
        /**
335
         * Set the position of an html element in page coordinates, regardless of how the element is positioned.
336
         * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
337
         * @method setXY
338
         * @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
339
         * @param {Array} pos Contains X & Y values for new position (coordinates are page-based)
340
         * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
341
         */
342
        setXY: function(el, pos, noRetry) {
343
            Y.Dom.batch(el, Y.Dom._setXY, { pos: pos, noRetry: noRetry });
344
        },
345
 
346
        _setXY: function(node, args) {
347
            var pos = Y.Dom._getStyle(node, POSITION),
348
                setStyle = Y.Dom.setStyle,
349
                xy = args.pos,
350
                noRetry = args.noRetry,
351
 
352
                delta = [ // assuming pixels; if not we will have to retry
353
                    parseInt( Y.Dom.getComputedStyle(node, LEFT), 10 ),
354
                    parseInt( Y.Dom.getComputedStyle(node, TOP), 10 )
355
                ],
356
 
357
                currentXY,
358
                newXY;
359
 
360
            currentXY = Y.Dom._getXY(node);
361
 
362
            if (!xy || currentXY === false) { // has to be part of doc to have xy
363
                return false;
364
            }
365
 
366
            if (pos == 'static') { // default to relative
367
                pos = RELATIVE;
368
                setStyle(node, POSITION, pos);
369
            }
370
 
371
            if ( isNaN(delta[0]) ) {// in case of 'auto'
372
                delta[0] = (pos == RELATIVE) ? 0 : node[OFFSET_LEFT];
373
            }
374
            if ( isNaN(delta[1]) ) { // in case of 'auto'
375
                delta[1] = (pos == RELATIVE) ? 0 : node[OFFSET_TOP];
376
            }
377
 
378
            if (xy[0] !== null) { // from setX
379
                setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
380
            }
381
 
382
            if (xy[1] !== null) { // from setY
383
                setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
384
            }
385
 
386
            if (!noRetry) {
387
                newXY = Y.Dom._getXY(node);
388
 
389
                // if retry is true, try one more time if we miss
390
               if ( (xy[0] !== null && newXY[0] != xy[0]) ||
391
                    (xy[1] !== null && newXY[1] != xy[1]) ) {
392
                   Y.Dom._setXY(node, { pos: xy, noRetry: true });
393
               }
394
            }
395
 
396
        },
397
 
398
        /**
399
         * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
400
         * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
401
         * @method setX
402
         * @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.
403
         * @param {Int} x The value to use as the X coordinate for the element(s).
404
         */
405
        setX: function(el, x) {
406
            Y.Dom.setXY(el, [x, null]);
407
        },
408
 
409
        /**
410
         * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
411
         * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
412
         * @method setY
413
         * @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.
414
         * @param {Int} x To use as the Y coordinate for the element(s).
415
         */
416
        setY: function(el, y) {
417
            Y.Dom.setXY(el, [null, y]);
418
        },
419
 
420
        /**
421
         * Returns the region position of the given element.
422
         * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
423
         * @method getRegion
424
         * @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.
425
         * @return {Region | Array} A Region or array of Region instances containing "top, left, bottom, right" member data.
426
         */
427
        getRegion: function(el) {
428
            var f = function(el) {
429
                var region = false;
430
                if ( Y.Dom._canPosition(el) ) {
431
                    region = Y.Region.getRegion(el);
432
                } else {
433
                }
434
 
435
                return region;
436
            };
437
 
438
            return Y.Dom.batch(el, f, Y.Dom, true);
439
        },
440
 
441
        /**
442
         * Returns the width of the client (viewport).
443
         * @method getClientWidth
444
         * @deprecated Now using getViewportWidth.  This interface left intact for back compat.
445
         * @return {Int} The width of the viewable area of the page.
446
         */
447
        getClientWidth: function() {
448
            return Y.Dom.getViewportWidth();
449
        },
450
 
451
        /**
452
         * Returns the height of the client (viewport).
453
         * @method getClientHeight
454
         * @deprecated Now using getViewportHeight.  This interface left intact for back compat.
455
         * @return {Int} The height of the viewable area of the page.
456
         */
457
        getClientHeight: function() {
458
            return Y.Dom.getViewportHeight();
459
        },
460
 
461
        /**
462
         * Returns an array of HTMLElements with the given class.
463
         * For optimized performance, include a tag and/or root node when possible.
464
         * Note: This method operates against a live collection, so modifying the
465
         * collection in the callback (removing/appending nodes, etc.) will have
466
         * side effects.  Instead you should iterate the returned nodes array,
467
         * as you would with the native "getElementsByTagName" method.
468
         * @method getElementsByClassName
469
         * @param {String} className The class name to match against
470
         * @param {String} tag (optional) The tag name of the elements being collected
471
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point.
472
         * This element is not included in the className scan.
473
         * @param {Function} apply (optional) A function to apply to each element when found
474
         * @param {Any} o (optional) An optional arg that is passed to the supplied method
475
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
476
         * @return {Array} An array of elements that have the given class name
477
         */
478
        getElementsByClassName: function(className, tag, root, apply, o, overrides) {
479
            tag = tag || '*';
480
            root = (root) ? Y.Dom.get(root) : null || document;
481
            if (!root) {
482
                return [];
483
            }
484
 
485
            var nodes = [],
486
                elements = root.getElementsByTagName(tag),
487
                hasClass = Y.Dom.hasClass;
488
 
489
            for (var i = 0, len = elements.length; i < len; ++i) {
490
                if ( hasClass(elements[i], className) ) {
491
                    nodes[nodes.length] = elements[i];
492
                }
493
            }
494
 
495
            if (apply) {
496
                Y.Dom.batch(nodes, apply, o, overrides);
497
            }
498
 
499
            return nodes;
500
        },
501
 
502
        /**
503
         * Determines whether an HTMLElement has the given className.
504
         * @method hasClass
505
         * @param {String | HTMLElement | Array} el The element or collection to test
506
         * @param {String | RegExp} className the class name to search for, or a regular
507
         * expression to match against
508
         * @return {Boolean | Array} A boolean value or array of boolean values
509
         */
510
        hasClass: function(el, className) {
511
            return Y.Dom.batch(el, Y.Dom._hasClass, className);
512
        },
513
 
514
        _hasClass: function(el, className) {
515
            var ret = false,
516
                current;
517
 
518
            if (el && className) {
519
                current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
520
                if (current) { // convert line breaks, tabs and other delims to spaces
521
                    current = current.replace(/\s+/g, SPACE);
522
                }
523
 
524
                if (className.exec) {
525
                    ret = className.test(current);
526
                } else {
527
                    ret = className && (SPACE + current + SPACE).
528
                        indexOf(SPACE + className + SPACE) > -1;
529
                }
530
            } else {
531
            }
532
 
533
            return ret;
534
        },
535
 
536
        /**
537
         * Adds a class name to a given element or collection of elements.
538
         * @method addClass
539
         * @param {String | HTMLElement | Array} el The element or collection to add the class to
540
         * @param {String} className the class name to add to the class attribute
541
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
542
         */
543
        addClass: function(el, className) {
544
            return Y.Dom.batch(el, Y.Dom._addClass, className);
545
        },
546
 
547
        _addClass: function(el, className) {
548
            var ret = false,
549
                current;
550
 
551
            if (el && className) {
552
                current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
553
                if ( !Y.Dom._hasClass(el, className) ) {
554
                    Y.Dom.setAttribute(el, CLASS_NAME, trim(current + SPACE + className));
555
                    ret = true;
556
                }
557
            } else {
558
            }
559
 
560
            return ret;
561
        },
562
 
563
        /**
564
         * Removes a class name from a given element or collection of elements.
565
         * @method removeClass
566
         * @param {String | HTMLElement | Array} el The element or collection to remove the class from
567
         * @param {String} className the class name to remove from the class attribute
568
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
569
         */
570
        removeClass: function(el, className) {
571
            return Y.Dom.batch(el, Y.Dom._removeClass, className);
572
        },
573
 
574
        _removeClass: function(el, className) {
575
            var ret = false,
576
                current,
577
                newClass,
578
                attr;
579
 
580
            if (el && className) {
581
                current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
582
                Y.Dom.setAttribute(el, CLASS_NAME, current.replace(Y.Dom._getClassRegex(className), EMPTY));
583
 
584
                newClass = Y.Dom._getAttribute(el, CLASS_NAME);
585
                if (current !== newClass) { // else nothing changed
586
                    Y.Dom.setAttribute(el, CLASS_NAME, trim(newClass)); // trim after comparing to current class
587
                    ret = true;
588
 
589
                    if (Y.Dom._getAttribute(el, CLASS_NAME) === '') { // remove class attribute if empty
590
                        attr = (el.hasAttribute && el.hasAttribute(_CLASS)) ? _CLASS : CLASS_NAME;
591
                        el.removeAttribute(attr);
592
                    }
593
                }
594
 
595
            } else {
596
            }
597
 
598
            return ret;
599
        },
600
 
601
        /**
602
         * Replace a class with another class for a given element or collection of elements.
603
         * If no oldClassName is present, the newClassName is simply added.
604
         * @method replaceClass
605
         * @param {String | HTMLElement | Array} el The element or collection to remove the class from
606
         * @param {String} oldClassName the class name to be replaced
607
         * @param {String} newClassName the class name that will be replacing the old class name
608
         * @return {Boolean | Array} A pass/fail boolean or array of booleans
609
         */
610
        replaceClass: function(el, oldClassName, newClassName) {
611
            return Y.Dom.batch(el, Y.Dom._replaceClass, { from: oldClassName, to: newClassName });
612
        },
613
 
614
        _replaceClass: function(el, classObj) {
615
            var className,
616
                from,
617
                to,
618
                ret = false,
619
                current;
620
 
621
            if (el && classObj) {
622
                from = classObj.from;
623
                to = classObj.to;
624
 
625
                if (!to) {
626
                    ret = false;
627
                }  else if (!from) { // just add if no "from"
628
                    ret = Y.Dom._addClass(el, classObj.to);
629
                } else if (from !== to) { // else nothing to replace
630
                    // May need to lead with DBLSPACE?
631
                    current = Y.Dom._getAttribute(el, CLASS_NAME) || EMPTY;
632
                    className = (SPACE + current.replace(Y.Dom._getClassRegex(from), SPACE + to).
633
                            replace(/\s+/g, SPACE)). // normalize white space
634
                            split(Y.Dom._getClassRegex(to));
635
 
636
                    // insert to into what would have been the first occurrence slot
637
                    className.splice(1, 0, SPACE + to);
638
                    Y.Dom.setAttribute(el, CLASS_NAME, trim(className.join(EMPTY)));
639
                    ret = true;
640
                }
641
            } else {
642
            }
643
 
644
            return ret;
645
        },
646
 
647
        /**
648
         * Returns an ID and applies it to the element "el", if provided.
649
         * @method generateId
650
         * @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).
651
         * @param {String} prefix (optional) an optional prefix to use (defaults to "yui-gen").
652
         * @return {String | Array} The generated ID, or array of generated IDs (or original ID if already present on an element)
653
         */
654
        generateId: function(el, prefix) {
655
            prefix = prefix || 'yui-gen';
656
 
657
            var f = function(el) {
658
                if (el && el.id) { // do not override existing ID
659
                    return el.id;
660
                }
661
 
662
                var id = prefix + YAHOO.env._id_counter++;
663
 
664
                if (el) {
665
                    if (el[OWNER_DOCUMENT] && el[OWNER_DOCUMENT].getElementById(id)) { // in case one already exists
666
                        // use failed id plus prefix to help ensure uniqueness
667
                        return Y.Dom.generateId(el, id + prefix);
668
                    }
669
                    el.id = id;
670
                }
671
 
672
                return id;
673
            };
674
 
675
            // batch fails when no element, so just generate and return single ID
676
            return Y.Dom.batch(el, f, Y.Dom, true) || f.apply(Y.Dom, arguments);
677
        },
678
 
679
        /**
680
         * Determines whether an HTMLElement is an ancestor of another HTML element in the DOM hierarchy.
681
         * @method isAncestor
682
         * @param {String | HTMLElement} haystack The possible ancestor
683
         * @param {String | HTMLElement} needle The possible descendent
684
         * @return {Boolean} Whether or not the haystack is an ancestor of needle
685
         */
686
        isAncestor: function(haystack, needle) {
687
            haystack = Y.Dom.get(haystack);
688
            needle = Y.Dom.get(needle);
689
 
690
            var ret = false;
691
 
692
            if ( (haystack && needle) && (haystack[NODE_TYPE] && needle[NODE_TYPE]) ) {
693
                if (haystack.contains && haystack !== needle) { // contains returns true when equal
694
                    ret = haystack.contains(needle);
695
                }
696
                else if (haystack.compareDocumentPosition) { // gecko
697
                    ret = !!(haystack.compareDocumentPosition(needle) & 16);
698
                }
699
            } else {
700
            }
701
            return ret;
702
        },
703
 
704
        /**
705
         * Determines whether an HTMLElement is present in the current document.
706
         * @method inDocument
707
         * @param {String | HTMLElement} el The element to search for
708
         * @param {Object} doc An optional document to search, defaults to element's owner document
709
         * @return {Boolean} Whether or not the element is present in the current document
710
         */
711
        inDocument: function(el, doc) {
712
            return Y.Dom._inDoc(Y.Dom.get(el), doc);
713
        },
714
 
715
        _inDoc: function(el, doc) {
716
            var ret = false;
717
            if (el && el[TAG_NAME]) {
718
                doc = doc || el[OWNER_DOCUMENT];
719
                ret = Y.Dom.isAncestor(doc[DOCUMENT_ELEMENT], el);
720
            } else {
721
            }
722
            return ret;
723
        },
724
 
725
        /**
726
         * Returns an array of HTMLElements that pass the test applied by supplied boolean method.
727
         * For optimized performance, include a tag and/or root node when possible.
728
         * Note: This method operates against a live collection, so modifying the
729
         * collection in the callback (removing/appending nodes, etc.) will have
730
         * side effects.  Instead you should iterate the returned nodes array,
731
         * as you would with the native "getElementsByTagName" method.
732
         * @method getElementsBy
733
         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
734
         * @param {String} tag (optional) The tag name of the elements being collected
735
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point
736
         * @param {Function} apply (optional) A function to apply to each element when found
737
         * @param {Any} o (optional) An optional arg that is passed to the supplied method
738
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
739
         * @return {Array} Array of HTMLElements
740
         */
741
        getElementsBy: function(method, tag, root, apply, o, overrides, firstOnly) {
742
            tag = tag || '*';
743
            root = (root) ? Y.Dom.get(root) : null || document;
744
 
745
                var ret = (firstOnly) ? null : [],
746
                    elements;
747
 
748
            // in case Dom.get() returns null
749
            if (root) {
750
                elements = root.getElementsByTagName(tag);
751
                for (var i = 0, len = elements.length; i < len; ++i) {
752
                    if ( method(elements[i]) ) {
753
                        if (firstOnly) {
754
                            ret = elements[i];
755
                            break;
756
                        } else {
757
                            ret[ret.length] = elements[i];
758
                        }
759
                    }
760
                }
761
 
762
                if (apply) {
763
                    Y.Dom.batch(ret, apply, o, overrides);
764
                }
765
            }
766
 
767
 
768
            return ret;
769
        },
770
 
771
        /**
772
         * Returns the first HTMLElement that passes the test applied by the supplied boolean method.
773
         * @method getElementBy
774
         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
775
         * @param {String} tag (optional) The tag name of the elements being collected
776
         * @param {String | HTMLElement} root (optional) The HTMLElement or an ID to use as the starting point
777
         * @return {HTMLElement}
778
         */
779
        getElementBy: function(method, tag, root) {
780
            return Y.Dom.getElementsBy(method, tag, root, null, null, null, true);
781
        },
782
 
783
        /**
784
         * Runs the supplied method against each item in the Collection/Array.
785
         * The method is called with the element(s) as the first arg, and the optional param as the second ( method(el, o) ).
786
         * @method batch
787
         * @param {String | HTMLElement | Array} el (optional) An element or array of elements to apply the method to
788
         * @param {Function} method The method to apply to the element(s)
789
         * @param {Any} o (optional) An optional arg that is passed to the supplied method
790
         * @param {Boolean} overrides (optional) Whether or not to override the scope of "method" with "o"
791
         * @return {Any | Array} The return value(s) from the supplied method
792
         */
793
        batch: function(el, method, o, overrides) {
794
            var collection = [],
795
                scope = (overrides) ? o : null;
796
 
797
            el = (el && (el[TAG_NAME] || el.item)) ? el : Y.Dom.get(el); // skip get() when possible
798
            if (el && method) {
799
                if (el[TAG_NAME] || el.length === undefined) { // element or not array-like
800
                    return method.call(scope, el, o);
801
                }
802
 
803
                for (var i = 0; i < el.length; ++i) {
804
                    collection[collection.length] = method.call(scope || el[i], el[i], o);
805
                }
806
            } else {
807
                return false;
808
            }
809
            return collection;
810
        },
811
 
812
        /**
813
         * Returns the height of the document.
814
         * @method getDocumentHeight
815
         * @return {Int} The height of the actual document (which includes the body and its margin).
816
         */
817
        getDocumentHeight: function() {
818
            var scrollHeight = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollHeight : documentElement.scrollHeight,
819
                h = Math.max(scrollHeight, Y.Dom.getViewportHeight());
820
 
821
            return h;
822
        },
823
 
824
        /**
825
         * Returns the width of the document.
826
         * @method getDocumentWidth
827
         * @return {Int} The width of the actual document (which includes the body and its margin).
828
         */
829
        getDocumentWidth: function() {
830
            var scrollWidth = (document[COMPAT_MODE] != CSS1_COMPAT || isSafari) ? document.body.scrollWidth : documentElement.scrollWidth,
831
                w = Math.max(scrollWidth, Y.Dom.getViewportWidth());
832
            return w;
833
        },
834
 
835
        /**
836
         * Returns the current height of the viewport.
837
         * @method getViewportHeight
838
         * @return {Int} The height of the viewable area of the page (excludes scrollbars).
839
         */
840
        getViewportHeight: function() {
841
            var height = self.innerHeight, // Safari, Opera
842
                mode = document[COMPAT_MODE];
843
 
844
            if ( (mode || isIE) && !isOpera ) { // IE, Gecko
845
                height = (mode == CSS1_COMPAT) ?
846
                        documentElement.clientHeight : // Standards
847
                        document.body.clientHeight; // Quirks
848
            }
849
 
850
            return height;
851
        },
852
 
853
        /**
854
         * Returns the current width of the viewport.
855
         * @method getViewportWidth
856
         * @return {Int} The width of the viewable area of the page (excludes scrollbars).
857
         */
858
 
859
        getViewportWidth: function() {
860
            var width = self.innerWidth,  // Safari
861
                mode = document[COMPAT_MODE];
862
 
863
            if (mode || isIE) { // IE, Gecko, Opera
864
                width = (mode == CSS1_COMPAT) ?
865
                        documentElement.clientWidth : // Standards
866
                        document.body.clientWidth; // Quirks
867
            }
868
            return width;
869
        },
870
 
871
       /**
872
         * Returns the nearest ancestor that passes the test applied by supplied boolean method.
873
         * For performance reasons, IDs are not accepted and argument validation omitted.
874
         * @method getAncestorBy
875
         * @param {HTMLElement} node The HTMLElement to use as the starting point
876
         * @param {Function} method - A boolean method for testing elements which receives the element as its only argument.
877
         * @return {Object} HTMLElement or null if not found
878
         */
879
        getAncestorBy: function(node, method) {
880
            while ( (node = node[PARENT_NODE]) ) { // NOTE: assignment
881
                if ( Y.Dom._testElement(node, method) ) {
882
                    return node;
883
                }
884
            }
885
 
886
            return null;
887
        },
888
 
889
        /**
890
         * Returns the nearest ancestor with the given className.
891
         * @method getAncestorByClassName
892
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point
893
         * @param {String} className
894
         * @return {Object} HTMLElement
895
         */
896
        getAncestorByClassName: function(node, className) {
897
            node = Y.Dom.get(node);
898
            if (!node) {
899
                return null;
900
            }
901
            var method = function(el) { return Y.Dom.hasClass(el, className); };
902
            return Y.Dom.getAncestorBy(node, method);
903
        },
904
 
905
        /**
906
         * Returns the nearest ancestor with the given tagName.
907
         * @method getAncestorByTagName
908
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point
909
         * @param {String} tagName
910
         * @return {Object} HTMLElement
911
         */
912
        getAncestorByTagName: function(node, tagName) {
913
            node = Y.Dom.get(node);
914
            if (!node) {
915
                return null;
916
            }
917
            var method = function(el) {
918
                 return el[TAG_NAME] && el[TAG_NAME].toUpperCase() == tagName.toUpperCase();
919
            };
920
 
921
            return Y.Dom.getAncestorBy(node, method);
922
        },
923
 
924
        /**
925
         * Returns the previous sibling that is an HTMLElement.
926
         * For performance reasons, IDs are not accepted and argument validation omitted.
927
         * Returns the nearest HTMLElement sibling if no method provided.
928
         * @method getPreviousSiblingBy
929
         * @param {HTMLElement} node The HTMLElement to use as the starting point
930
         * @param {Function} method A boolean function used to test siblings
931
         * that receives the sibling node being tested as its only argument
932
         * @return {Object} HTMLElement or null if not found
933
         */
934
        getPreviousSiblingBy: function(node, method) {
935
            while (node) {
936
                node = node.previousSibling;
937
                if ( Y.Dom._testElement(node, method) ) {
938
                    return node;
939
                }
940
            }
941
            return null;
942
        },
943
 
944
        /**
945
         * Returns the previous sibling that is an HTMLElement
946
         * @method getPreviousSibling
947
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point
948
         * @return {Object} HTMLElement or null if not found
949
         */
950
        getPreviousSibling: function(node) {
951
            node = Y.Dom.get(node);
952
            if (!node) {
953
                return null;
954
            }
955
 
956
            return Y.Dom.getPreviousSiblingBy(node);
957
        },
958
 
959
        /**
960
         * Returns the next HTMLElement sibling that passes the boolean method.
961
         * For performance reasons, IDs are not accepted and argument validation omitted.
962
         * Returns the nearest HTMLElement sibling if no method provided.
963
         * @method getNextSiblingBy
964
         * @param {HTMLElement} node The HTMLElement to use as the starting point
965
         * @param {Function} method A boolean function used to test siblings
966
         * that receives the sibling node being tested as its only argument
967
         * @return {Object} HTMLElement or null if not found
968
         */
969
        getNextSiblingBy: function(node, method) {
970
            while (node) {
971
                node = node.nextSibling;
972
                if ( Y.Dom._testElement(node, method) ) {
973
                    return node;
974
                }
975
            }
976
            return null;
977
        },
978
 
979
        /**
980
         * Returns the next sibling that is an HTMLElement
981
         * @method getNextSibling
982
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point
983
         * @return {Object} HTMLElement or null if not found
984
         */
985
        getNextSibling: function(node) {
986
            node = Y.Dom.get(node);
987
            if (!node) {
988
                return null;
989
            }
990
 
991
            return Y.Dom.getNextSiblingBy(node);
992
        },
993
 
994
        /**
995
         * Returns the first HTMLElement child that passes the test method.
996
         * @method getFirstChildBy
997
         * @param {HTMLElement} node The HTMLElement to use as the starting point
998
         * @param {Function} method A boolean function used to test children
999
         * that receives the node being tested as its only argument
1000
         * @return {Object} HTMLElement or null if not found
1001
         */
1002
        getFirstChildBy: function(node, method) {
1003
            var child = ( Y.Dom._testElement(node.firstChild, method) ) ? node.firstChild : null;
1004
            return child || Y.Dom.getNextSiblingBy(node.firstChild, method);
1005
        },
1006
 
1007
        /**
1008
         * Returns the first HTMLElement child.
1009
         * @method getFirstChild
1010
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point
1011
         * @return {Object} HTMLElement or null if not found
1012
         */
1013
        getFirstChild: function(node, method) {
1014
            node = Y.Dom.get(node);
1015
            if (!node) {
1016
                return null;
1017
            }
1018
            return Y.Dom.getFirstChildBy(node);
1019
        },
1020
 
1021
        /**
1022
         * Returns the last HTMLElement child that passes the test method.
1023
         * @method getLastChildBy
1024
         * @param {HTMLElement} node The HTMLElement to use as the starting point
1025
         * @param {Function} method A boolean function used to test children
1026
         * that receives the node being tested as its only argument
1027
         * @return {Object} HTMLElement or null if not found
1028
         */
1029
        getLastChildBy: function(node, method) {
1030
            if (!node) {
1031
                return null;
1032
            }
1033
            var child = ( Y.Dom._testElement(node.lastChild, method) ) ? node.lastChild : null;
1034
            return child || Y.Dom.getPreviousSiblingBy(node.lastChild, method);
1035
        },
1036
 
1037
        /**
1038
         * Returns the last HTMLElement child.
1039
         * @method getLastChild
1040
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point
1041
         * @return {Object} HTMLElement or null if not found
1042
         */
1043
        getLastChild: function(node) {
1044
            node = Y.Dom.get(node);
1045
            return Y.Dom.getLastChildBy(node);
1046
        },
1047
 
1048
        /**
1049
         * Returns an array of HTMLElement childNodes that pass the test method.
1050
         * @method getChildrenBy
1051
         * @param {HTMLElement} node The HTMLElement to start from
1052
         * @param {Function} method A boolean function used to test children
1053
         * that receives the node being tested as its only argument
1054
         * @return {Array} A static array of HTMLElements
1055
         */
1056
        getChildrenBy: function(node, method) {
1057
            var child = Y.Dom.getFirstChildBy(node, method),
1058
                children = child ? [child] : [];
1059
 
1060
            Y.Dom.getNextSiblingBy(child, function(node) {
1061
                if ( !method || method(node) ) {
1062
                    children[children.length] = node;
1063
                }
1064
                return false; // fail test to collect all children
1065
            });
1066
 
1067
            return children;
1068
        },
1069
 
1070
        /**
1071
         * Returns an array of HTMLElement childNodes.
1072
         * @method getChildren
1073
         * @param {String | HTMLElement} node The HTMLElement or an ID to use as the starting point
1074
         * @return {Array} A static array of HTMLElements
1075
         */
1076
        getChildren: function(node) {
1077
            node = Y.Dom.get(node);
1078
            if (!node) {
1079
            }
1080
 
1081
            return Y.Dom.getChildrenBy(node);
1082
        },
1083
 
1084
        /**
1085
         * Returns the left scroll value of the document
1086
         * @method getDocumentScrollLeft
1087
         * @param {HTMLDocument} document (optional) The document to get the scroll value of
1088
         * @return {Int}  The amount that the document is scrolled to the left
1089
         */
1090
        getDocumentScrollLeft: function(doc) {
1091
            doc = doc || document;
1092
            return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
1093
        },
1094
 
1095
        /**
1096
         * Returns the top scroll value of the document
1097
         * @method getDocumentScrollTop
1098
         * @param {HTMLDocument} document (optional) The document to get the scroll value of
1099
         * @return {Int}  The amount that the document is scrolled to the top
1100
         */
1101
        getDocumentScrollTop: function(doc) {
1102
            doc = doc || document;
1103
            return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
1104
        },
1105
 
1106
        /**
1107
         * Inserts the new node as the previous sibling of the reference node
1108
         * @method insertBefore
1109
         * @param {String | HTMLElement} newNode The node to be inserted
1110
         * @param {String | HTMLElement} referenceNode The node to insert the new node before
1111
         * @return {HTMLElement} The node that was inserted (or null if insert fails)
1112
         */
1113
        insertBefore: function(newNode, referenceNode) {
1114
            newNode = Y.Dom.get(newNode);
1115
            referenceNode = Y.Dom.get(referenceNode);
1116
 
1117
            if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
1118
                return null;
1119
            }
1120
 
1121
            return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode);
1122
        },
1123
 
1124
        /**
1125
         * Inserts the new node as the next sibling of the reference node
1126
         * @method insertAfter
1127
         * @param {String | HTMLElement} newNode The node to be inserted
1128
         * @param {String | HTMLElement} referenceNode The node to insert the new node after
1129
         * @return {HTMLElement} The node that was inserted (or null if insert fails)
1130
         */
1131
        insertAfter: function(newNode, referenceNode) {
1132
            newNode = Y.Dom.get(newNode);
1133
            referenceNode = Y.Dom.get(referenceNode);
1134
 
1135
            if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
1136
                return null;
1137
            }
1138
 
1139
            if (referenceNode.nextSibling) {
1140
                return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode.nextSibling);
1141
            } else {
1142
                return referenceNode[PARENT_NODE].appendChild(newNode);
1143
            }
1144
        },
1145
 
1146
        /**
1147
         * Creates a Region based on the viewport relative to the document.
1148
         * @method getClientRegion
1149
         * @return {Region} A Region object representing the viewport which accounts for document scroll
1150
         */
1151
        getClientRegion: function() {
1152
            var t = Y.Dom.getDocumentScrollTop(),
1153
                l = Y.Dom.getDocumentScrollLeft(),
1154
                r = Y.Dom.getViewportWidth() + l,
1155
                b = Y.Dom.getViewportHeight() + t;
1156
 
1157
            return new Y.Region(t, r, b, l);
1158
        },
1159
 
1160
        /**
1161
         * Provides a normalized attribute interface.
1162
         * @method setAttribute
1163
         * @param {String | HTMLElement} el The target element for the attribute.
1164
         * @param {String} attr The attribute to set.
1165
         * @param {String} val The value of the attribute.
1166
         */
1167
        setAttribute: function(el, attr, val) {
1168
            Y.Dom.batch(el, Y.Dom._setAttribute, { attr: attr, val: val });
1169
        },
1170
 
1171
        _setAttribute: function(el, args) {
1172
            var attr = Y.Dom._toCamel(args.attr),
1173
                val = args.val;
1174
 
1175
            if (el && el.setAttribute) {
1176
                // set as DOM property, except for BUTTON, which errors on property setter
1177
                if (Y.Dom.DOT_ATTRIBUTES[attr] && el.tagName && el.tagName != 'BUTTON') {
1178
                    el[attr] = val;
1179
                } else {
1180
                    attr = Y.Dom.CUSTOM_ATTRIBUTES[attr] || attr;
1181
                    el.setAttribute(attr, val);
1182
                }
1183
            } else {
1184
            }
1185
        },
1186
 
1187
        /**
1188
         * Provides a normalized attribute interface.
1189
         * @method getAttribute
1190
         * @param {String | HTMLElement} el The target element for the attribute.
1191
         * @param {String} attr The attribute to get.
1192
         * @return {String} The current value of the attribute.
1193
         */
1194
        getAttribute: function(el, attr) {
1195
            return Y.Dom.batch(el, Y.Dom._getAttribute, attr);
1196
        },
1197
 
1198
 
1199
        _getAttribute: function(el, attr) {
1200
            var val;
1201
            attr = Y.Dom.CUSTOM_ATTRIBUTES[attr] || attr;
1202
 
1203
            if (Y.Dom.DOT_ATTRIBUTES[attr]) {
1204
                val = el[attr];
1205
            } else if (el && 'getAttribute' in el) {
1206
                if (/^(?:href|src)$/.test(attr)) { // use IE flag to return exact value
1207
                    val = el.getAttribute(attr, 2);
1208
                } else {
1209
                    val = el.getAttribute(attr);
1210
                }
1211
            } else {
1212
            }
1213
 
1214
            return val;
1215
        },
1216
 
1217
        _toCamel: function(property) {
1218
            var c = propertyCache;
1219
 
1220
            function tU(x,l) {
1221
                return l.toUpperCase();
1222
            }
1223
 
1224
            return c[property] || (c[property] = property.indexOf('-') === -1 ?
1225
                                    property :
1226
                                    property.replace( /-([a-z])/gi, tU ));
1227
        },
1228
 
1229
        _getClassRegex: function(className) {
1230
            var re;
1231
            if (className !== undefined) { // allow empty string to pass
1232
                if (className.exec) { // already a RegExp
1233
                    re = className;
1234
                } else {
1235
                    re = reCache[className];
1236
                    if (!re) {
1237
                        // escape special chars (".", "[", etc.)
1238
                        className = className.replace(Y.Dom._patterns.CLASS_RE_TOKENS, '\\$1');
1239
                        className = className.replace(/\s+/g, SPACE); // convert line breaks and other delims
1240
                        re = reCache[className] = new RegExp(C_START + className + C_END, G);
1241
                    }
1242
                }
1243
            }
1244
            return re;
1245
        },
1246
 
1247
        _patterns: {
1248
            ROOT_TAG: /^body|html$/i, // body for quirks mode, html for standards,
1249
            CLASS_RE_TOKENS: /([\.\(\)\^\$\*\+\?\|\[\]\{\}\\])/g
1250
        },
1251
 
1252
 
1253
        _testElement: function(node, method) {
1254
            return node && node[NODE_TYPE] == 1 && ( !method || method(node) );
1255
        },
1256
 
1257
        _calcBorders: function(node, xy2) {
1258
            var t = parseInt(Y.Dom[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
1259
                l = parseInt(Y.Dom[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
1260
            if (isGecko) {
1261
                if (RE_TABLE.test(node[TAG_NAME])) {
1262
                    t = 0;
1263
                    l = 0;
1264
                }
1265
            }
1266
            xy2[0] += l;
1267
            xy2[1] += t;
1268
            return xy2;
1269
        }
1270
    };
1271
 
1272
    var _getComputedStyle = Y.Dom[GET_COMPUTED_STYLE];
1273
    // fix opera computedStyle default color unit (convert to rgb)
1274
    if (UA.opera) {
1275
        Y.Dom[GET_COMPUTED_STYLE] = function(node, att) {
1276
            var val = _getComputedStyle(node, att);
1277
            if (RE_COLOR.test(att)) {
1278
                val = Y.Dom.Color.toRGB(val);
1279
            }
1280
 
1281
            return val;
1282
        };
1283
 
1284
    }
1285
 
1286
    // safari converts transparent to rgba(), others use "transparent"
1287
    if (UA.webkit) {
1288
        Y.Dom[GET_COMPUTED_STYLE] = function(node, att) {
1289
            var val = _getComputedStyle(node, att);
1290
 
1291
            if (val === 'rgba(0, 0, 0, 0)') {
1292
                val = 'transparent';
1293
            }
1294
 
1295
            return val;
1296
        };
1297
 
1298
    }
1299
 
1300
    if (UA.ie && UA.ie >= 8) {
1301
        Y.Dom.DOT_ATTRIBUTES.type = true; // IE 8 errors on input.setAttribute('type')
1302
    }
1303
})();
1304
/**
1305
 * A region is a representation of an object on a grid.  It is defined
1306
 * by the top, right, bottom, left extents, so is rectangular by default.  If
1307
 * other shapes are required, this class could be extended to support it.
1308
 * @namespace YAHOO.util
1309
 * @class Region
1310
 * @param {Int} t the top extent
1311
 * @param {Int} r the right extent
1312
 * @param {Int} b the bottom extent
1313
 * @param {Int} l the left extent
1314
 * @constructor
1315
 */
1316
YAHOO.util.Region = function(t, r, b, l) {
1317
 
1318
    /**
1319
     * The region's top extent
1320
     * @property top
1321
     * @type Int
1322
     */
1323
    this.top = t;
1324
 
1325
    /**
1326
     * The region's top extent
1327
     * @property y
1328
     * @type Int
1329
     */
1330
    this.y = t;
1331
 
1332
    /**
1333
     * The region's top extent as index, for symmetry with set/getXY
1334
     * @property 1
1335
     * @type Int
1336
     */
1337
    this[1] = t;
1338
 
1339
    /**
1340
     * The region's right extent
1341
     * @property right
1342
     * @type int
1343
     */
1344
    this.right = r;
1345
 
1346
    /**
1347
     * The region's bottom extent
1348
     * @property bottom
1349
     * @type Int
1350
     */
1351
    this.bottom = b;
1352
 
1353
    /**
1354
     * The region's left extent
1355
     * @property left
1356
     * @type Int
1357
     */
1358
    this.left = l;
1359
 
1360
    /**
1361
     * The region's left extent
1362
     * @property x
1363
     * @type Int
1364
     */
1365
    this.x = l;
1366
 
1367
    /**
1368
     * The region's left extent as index, for symmetry with set/getXY
1369
     * @property 0
1370
     * @type Int
1371
     */
1372
    this[0] = l;
1373
 
1374
    /**
1375
     * The region's total width
1376
     * @property width
1377
     * @type Int
1378
     */
1379
    this.width = this.right - this.left;
1380
 
1381
    /**
1382
     * The region's total height
1383
     * @property height
1384
     * @type Int
1385
     */
1386
    this.height = this.bottom - this.top;
1387
};
1388
 
1389
/**
1390
 * Returns true if this region contains the region passed in
1391
 * @method contains
1392
 * @param  {Region}  region The region to evaluate
1393
 * @return {Boolean}        True if the region is contained with this region,
1394
 *                          else false
1395
 */
1396
YAHOO.util.Region.prototype.contains = function(region) {
1397
    return ( region.left   >= this.left   &&
1398
             region.right  <= this.right  &&
1399
             region.top    >= this.top    &&
1400
             region.bottom <= this.bottom    );
1401
 
1402
};
1403
 
1404
/**
1405
 * Returns the area of the region
1406
 * @method getArea
1407
 * @return {Int} the region's area
1408
 */
1409
YAHOO.util.Region.prototype.getArea = function() {
1410
    return ( (this.bottom - this.top) * (this.right - this.left) );
1411
};
1412
 
1413
/**
1414
 * Returns the region where the passed in region overlaps with this one
1415
 * @method intersect
1416
 * @param  {Region} region The region that intersects
1417
 * @return {Region}        The overlap region, or null if there is no overlap
1418
 */
1419
YAHOO.util.Region.prototype.intersect = function(region) {
1420
    var t = Math.max( this.top,    region.top    ),
1421
        r = Math.min( this.right,  region.right  ),
1422
        b = Math.min( this.bottom, region.bottom ),
1423
        l = Math.max( this.left,   region.left   );
1424
 
1425
    if (b >= t && r >= l) {
1426
        return new YAHOO.util.Region(t, r, b, l);
1427
    } else {
1428
        return null;
1429
    }
1430
};
1431
 
1432
/**
1433
 * Returns the region representing the smallest region that can contain both
1434
 * the passed in region and this region.
1435
 * @method union
1436
 * @param  {Region} region The region that to create the union with
1437
 * @return {Region}        The union region
1438
 */
1439
YAHOO.util.Region.prototype.union = function(region) {
1440
    var t = Math.min( this.top,    region.top    ),
1441
        r = Math.max( this.right,  region.right  ),
1442
        b = Math.max( this.bottom, region.bottom ),
1443
        l = Math.min( this.left,   region.left   );
1444
 
1445
    return new YAHOO.util.Region(t, r, b, l);
1446
};
1447
 
1448
/**
1449
 * toString
1450
 * @method toString
1451
 * @return string the region properties
1452
 */
1453
YAHOO.util.Region.prototype.toString = function() {
1454
    return ( "Region {"    +
1455
             "top: "       + this.top    +
1456
             ", right: "   + this.right  +
1457
             ", bottom: "  + this.bottom +
1458
             ", left: "    + this.left   +
1459
             ", height: "  + this.height +
1460
             ", width: "    + this.width   +
1461
             "}" );
1462
};
1463
 
1464
/**
1465
 * Returns a region that is occupied by the DOM element
1466
 * @method getRegion
1467
 * @param  {HTMLElement} el The element
1468
 * @return {Region}         The region that the element occupies
1469
 * @static
1470
 */
1471
YAHOO.util.Region.getRegion = function(el) {
1472
    var p = YAHOO.util.Dom.getXY(el),
1473
        t = p[1],
1474
        r = p[0] + el.offsetWidth,
1475
        b = p[1] + el.offsetHeight,
1476
        l = p[0];
1477
 
1478
    return new YAHOO.util.Region(t, r, b, l);
1479
};
1480
 
1481
/////////////////////////////////////////////////////////////////////////////
1482
 
1483
 
1484
/**
1485
 * A point is a region that is special in that it represents a single point on
1486
 * the grid.
1487
 * @namespace YAHOO.util
1488
 * @class Point
1489
 * @param {Int} x The X position of the point
1490
 * @param {Int} y The Y position of the point
1491
 * @constructor
1492
 * @extends YAHOO.util.Region
1493
 */
1494
YAHOO.util.Point = function(x, y) {
1495
   if (YAHOO.lang.isArray(x)) { // accept input from Dom.getXY, Event.getXY, etc.
1496
      y = x[1]; // dont blow away x yet
1497
      x = x[0];
1498
   }
1499
 
1500
    YAHOO.util.Point.superclass.constructor.call(this, y, x, y, x);
1501
};
1502
 
1503
YAHOO.extend(YAHOO.util.Point, YAHOO.util.Region);
1504
 
1505
(function() {
1506
/**
1507
 * Internal methods used to add style management functionality to DOM.
1508
 * @module dom
1509
 * @class IEStyle
1510
 * @namespace YAHOO.util.Dom
1511
 */
1512
 
1513
var Y = YAHOO.util,
1514
    CLIENT_TOP = 'clientTop',
1515
    CLIENT_LEFT = 'clientLeft',
1516
    PARENT_NODE = 'parentNode',
1517
    RIGHT = 'right',
1518
    HAS_LAYOUT = 'hasLayout',
1519
    PX = 'px',
1520
    OPACITY = 'opacity',
1521
    AUTO = 'auto',
1522
    BORDER_LEFT_WIDTH = 'borderLeftWidth',
1523
    BORDER_TOP_WIDTH = 'borderTopWidth',
1524
    BORDER_RIGHT_WIDTH = 'borderRightWidth',
1525
    BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
1526
    VISIBLE = 'visible',
1527
    TRANSPARENT = 'transparent',
1528
    HEIGHT = 'height',
1529
    WIDTH = 'width',
1530
    STYLE = 'style',
1531
    CURRENT_STYLE = 'currentStyle',
1532
 
1533
// IE getComputedStyle
1534
// TODO: unit-less lineHeight (e.g. 1.22)
1535
    re_size = /^width|height$/,
1536
    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,
1537
 
1538
    ComputedStyle = {
1539
        /**
1540
        * @method get
1541
        * @description Method used by DOM to get style information for IE
1542
        * @param {HTMLElement} el The element to check
1543
        * @param {String} property The property to check
1544
        * @returns {String} The computed style
1545
        */
1546
        get: function(el, property) {
1547
            var value = '',
1548
                current = el[CURRENT_STYLE][property];
1549
 
1550
            if (property === OPACITY) {
1551
                value = Y.Dom.getStyle(el, OPACITY);
1552
            } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
1553
                value = current;
1554
            } else if (Y.Dom.IE_COMPUTED[property]) { // use compute function
1555
                value = Y.Dom.IE_COMPUTED[property](el, property);
1556
            } else if (re_unit.test(current)) { // convert to pixel
1557
                value = Y.Dom.IE.ComputedStyle.getPixel(el, property);
1558
            } else {
1559
                value = current;
1560
            }
1561
 
1562
            return value;
1563
        },
1564
        /**
1565
        * @method getOffset
1566
        * @description Determine the offset of an element
1567
        * @param {HTMLElement} el The element to check
1568
        * @param {String} prop The property to check.
1569
        * @return {String} The offset
1570
        */
1571
        getOffset: function(el, prop) {
1572
            var current = el[CURRENT_STYLE][prop],                        // value of "width", "top", etc.
1573
                capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
1574
                offset = 'offset' + capped,                             // "offsetWidth", "offsetTop", etc.
1575
                pixel = 'pixel' + capped,                               // "pixelWidth", "pixelTop", etc.
1576
                value = '',
1577
                actual;
1578
 
1579
            if (current == AUTO) {
1580
                actual = el[offset]; // offsetHeight/Top etc.
1581
                if (actual === undefined) { // likely "right" or "bottom"
1582
                    value = 0;
1583
                }
1584
 
1585
                value = actual;
1586
                if (re_size.test(prop)) { // account for box model diff
1587
                    el[STYLE][prop] = actual;
1588
                    if (el[offset] > actual) {
1589
                        // the difference is padding + border (works in Standards & Quirks modes)
1590
                        value = actual - (el[offset] - actual);
1591
                    }
1592
                    el[STYLE][prop] = AUTO; // revert to auto
1593
                }
1594
            } else { // convert units to px
1595
                if (!el[STYLE][pixel] && !el[STYLE][prop]) { // need to map style.width to currentStyle (no currentStyle.pixelWidth)
1596
                    el[STYLE][prop] = current;              // no style.pixelWidth if no style.width
1597
                }
1598
                value = el[STYLE][pixel];
1599
            }
1600
            return value + PX;
1601
        },
1602
        /**
1603
        * @method getBorderWidth
1604
        * @description Try to determine the width of an elements border
1605
        * @param {HTMLElement} el The element to check
1606
        * @param {String} property The property to check
1607
        * @return {String} The elements border width
1608
        */
1609
        getBorderWidth: function(el, property) {
1610
            // clientHeight/Width = paddingBox (e.g. offsetWidth - borderWidth)
1611
            // clientTop/Left = borderWidth
1612
            var value = null;
1613
            if (!el[CURRENT_STYLE][HAS_LAYOUT]) { // TODO: unset layout?
1614
                el[STYLE].zoom = 1; // need layout to measure client
1615
            }
1616
 
1617
            switch(property) {
1618
                case BORDER_TOP_WIDTH:
1619
                    value = el[CLIENT_TOP];
1620
                    break;
1621
                case BORDER_BOTTOM_WIDTH:
1622
                    value = el.offsetHeight - el.clientHeight - el[CLIENT_TOP];
1623
                    break;
1624
                case BORDER_LEFT_WIDTH:
1625
                    value = el[CLIENT_LEFT];
1626
                    break;
1627
                case BORDER_RIGHT_WIDTH:
1628
                    value = el.offsetWidth - el.clientWidth - el[CLIENT_LEFT];
1629
                    break;
1630
            }
1631
            return value + PX;
1632
        },
1633
        /**
1634
        * @method getPixel
1635
        * @description Get the pixel value from a style property
1636
        * @param {HTMLElement} node The element to check
1637
        * @param {String} att The attribute to check
1638
        * @return {String} The pixel value
1639
        */
1640
        getPixel: function(node, att) {
1641
            // use pixelRight to convert to px
1642
            var val = null,
1643
                styleRight = node[CURRENT_STYLE][RIGHT],
1644
                current = node[CURRENT_STYLE][att];
1645
 
1646
            node[STYLE][RIGHT] = current;
1647
            val = node[STYLE].pixelRight;
1648
            node[STYLE][RIGHT] = styleRight; // revert
1649
 
1650
            return val + PX;
1651
        },
1652
 
1653
        /**
1654
        * @method getMargin
1655
        * @description Get the margin value from a style property
1656
        * @param {HTMLElement} node The element to check
1657
        * @param {String} att The attribute to check
1658
        * @return {String} The margin value
1659
        */
1660
        getMargin: function(node, att) {
1661
            var val;
1662
            if (node[CURRENT_STYLE][att] == AUTO) {
1663
                val = 0 + PX;
1664
            } else {
1665
                val = Y.Dom.IE.ComputedStyle.getPixel(node, att);
1666
            }
1667
            return val;
1668
        },
1669
 
1670
        /**
1671
        * @method getVisibility
1672
        * @description Get the visibility of an element
1673
        * @param {HTMLElement} node The element to check
1674
        * @param {String} att The attribute to check
1675
        * @return {String} The value
1676
        */
1677
        getVisibility: function(node, att) {
1678
            var current;
1679
            while ( (current = node[CURRENT_STYLE]) && current[att] == 'inherit') { // NOTE: assignment in test
1680
                node = node[PARENT_NODE];
1681
            }
1682
            return (current) ? current[att] : VISIBLE;
1683
        },
1684
 
1685
        /**
1686
        * @method getColor
1687
        * @description Get the color of an element
1688
        * @param {HTMLElement} node The element to check
1689
        * @param {String} att The attribute to check
1690
        * @return {String} The value
1691
        */
1692
        getColor: function(node, att) {
1693
            return Y.Dom.Color.toRGB(node[CURRENT_STYLE][att]) || TRANSPARENT;
1694
        },
1695
 
1696
        /**
1697
        * @method getBorderColor
1698
        * @description Get the bordercolor of an element
1699
        * @param {HTMLElement} node The element to check
1700
        * @param {String} att The attribute to check
1701
        * @return {String} The value
1702
        */
1703
        getBorderColor: function(node, att) {
1704
            var current = node[CURRENT_STYLE],
1705
                val = current[att] || current.color;
1706
            return Y.Dom.Color.toRGB(Y.Dom.Color.toHex(val));
1707
        }
1708
 
1709
    },
1710
 
1711
//fontSize: getPixelFont,
1712
    IEComputed = {};
1713
 
1714
IEComputed.top = IEComputed.right = IEComputed.bottom = IEComputed.left =
1715
        IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
1716
 
1717
IEComputed.color = ComputedStyle.getColor;
1718
 
1719
IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
1720
        IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
1721
        ComputedStyle.getBorderWidth;
1722
 
1723
IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
1724
        IEComputed.marginLeft = ComputedStyle.getMargin;
1725
 
1726
IEComputed.visibility = ComputedStyle.getVisibility;
1727
IEComputed.borderColor = IEComputed.borderTopColor =
1728
        IEComputed.borderRightColor = IEComputed.borderBottomColor =
1729
        IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
1730
 
1731
Y.Dom.IE_COMPUTED = IEComputed;
1732
Y.Dom.IE_ComputedStyle = ComputedStyle;
1733
})();
1734
(function() {
1735
/**
1736
 * Add style management functionality to DOM.
1737
 * @module dom
1738
 * @class Color
1739
 * @namespace YAHOO.util.Dom
1740
 */
1741
 
1742
var TO_STRING = 'toString',
1743
    PARSE_INT = parseInt,
1744
    RE = RegExp,
1745
    Y = YAHOO.util;
1746
 
1747
Y.Dom.Color = {
1748
    /**
1749
    * @property KEYWORDS
1750
    * @type Object
1751
    * @description Color keywords used when converting to Hex
1752
    */
1753
    KEYWORDS: {
1754
        black: '000',
1755
        silver: 'c0c0c0',
1756
        gray: '808080',
1757
        white: 'fff',
1758
        maroon: '800000',
1759
        red: 'f00',
1760
        purple: '800080',
1761
        fuchsia: 'f0f',
1762
        green: '008000',
1763
        lime: '0f0',
1764
        olive: '808000',
1765
        yellow: 'ff0',
1766
        navy: '000080',
1767
        blue: '00f',
1768
        teal: '008080',
1769
        aqua: '0ff'
1770
    },
1771
    /**
1772
    * @property re_RGB
1773
    * @private
1774
    * @type Regex
1775
    * @description Regex to parse rgb(0,0,0) formatted strings
1776
    */
1777
    re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
1778
    /**
1779
    * @property re_hex
1780
    * @private
1781
    * @type Regex
1782
    * @description Regex to parse #123456 formatted strings
1783
    */
1784
    re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
1785
    /**
1786
    * @property re_hex3
1787
    * @private
1788
    * @type Regex
1789
    * @description Regex to parse #123 formatted strings
1790
    */
1791
    re_hex3: /([0-9A-F])/gi,
1792
    /**
1793
    * @method toRGB
1794
    * @description Converts a hex or color string to an rgb string: rgb(0,0,0)
1795
    * @param {String} val The string to convert to RGB notation.
1796
    * @returns {String} The converted string
1797
    */
1798
    toRGB: function(val) {
1799
        if (!Y.Dom.Color.re_RGB.test(val)) {
1800
            val = Y.Dom.Color.toHex(val);
1801
        }
1802
 
1803
        if(Y.Dom.Color.re_hex.exec(val)) {
1804
            val = 'rgb(' + [
1805
                PARSE_INT(RE.$1, 16),
1806
                PARSE_INT(RE.$2, 16),
1807
                PARSE_INT(RE.$3, 16)
1808
            ].join(', ') + ')';
1809
        }
1810
        return val;
1811
    },
1812
    /**
1813
    * @method toHex
1814
    * @description Converts an rgb or color string to a hex string: #123456
1815
    * @param {String} val The string to convert to hex notation.
1816
    * @returns {String} The converted string
1817
    */
1818
    toHex: function(val) {
1819
        val = Y.Dom.Color.KEYWORDS[val] || val;
1820
        if (Y.Dom.Color.re_RGB.exec(val)) {
1821
            val = [
1822
                Number(RE.$1).toString(16),
1823
                Number(RE.$2).toString(16),
1824
                Number(RE.$3).toString(16)
1825
            ];
1826
 
1827
            for (var i = 0; i < val.length; i++) {
1828
                if (val[i].length < 2) {
1829
                    val[i] = '0' + val[i];
1830
                }
1831
            }
1832
 
1833
            val = val.join('');
1834
        }
1835
 
1836
        if (val.length < 6) {
1837
            val = val.replace(Y.Dom.Color.re_hex3, '$1$1');
1838
        }
1839
 
1840
        if (val !== 'transparent' && val.indexOf('#') < 0) {
1841
            val = '#' + val;
1842
        }
1843
 
1844
        return val.toUpperCase();
1845
    }
1846
};
1847
}());
1848
YAHOO.register("dom", YAHOO.util.Dom, {version: "2.9.0", build: "2800"});
1849
 
1850
    if (YAHOO.env._id_counter < 1e+6) {
1851
        YAHOO.env._id_counter =  Y.Env._yidx * 1e+6;
1852
    }
1853
}, '2.9.0' ,{"requires": ["yui2-yahoo"]});