~mnordhoff/loggerhead/valid-html

« back to all changes in this revision

Viewing changes to loggerhead/static/javascript/yui/build/dom/dom.js

  • Committer: Matt Nordhoff
  • Date: 2008-10-25 21:09:19 UTC
  • mfrom: (229.2.4 trunk)
  • Revision ID: mnordhoff@mattnordhoff.com-20081025210919-cbnlvd2ow5qpnxvh
Merge trunk.

This undoes the whitespace adjustment I made in macros.pt.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
Copyright (c) 2008, Yahoo! Inc. All rights reserved.
 
3
Code licensed under the BSD License:
 
4
http://developer.yahoo.net/yui/license.txt
 
5
version: 3.0.0pr1
 
6
*/
 
7
YUI.add('dom', function(Y) {
 
8
 
 
9
/** 
 
10
 * The DOM utility provides a cross-browser abtraction layer
 
11
 * normalizing DOM tasks, and adds extra helper functionality
 
12
 * for other common tasks. 
 
13
 * @module dom
 
14
 * @submodule dom-base
 
15
 *
 
16
 */
 
17
 
 
18
/** 
 
19
 * Provides DOM helper methods.
 
20
 * @class DOM
 
21
 *
 
22
 */
 
23
var NODE_TYPE = 'nodeType',
 
24
    OWNER_DOCUMENT = 'ownerDocument',
 
25
    DOCUMENT_ELEMENT = 'documentElement',
 
26
    DEFAULT_VIEW = 'defaultView',
 
27
    PARENT_WINDOW = 'parentWindow',
 
28
    TAG_NAME = 'tagName',
 
29
    PARENT_NODE = 'parentNode',
 
30
    FIRST_CHILD = 'firstChild',
 
31
    LAST_CHILD = 'lastChild',
 
32
    PREVIOUS_SIBLING = 'previousSibling',
 
33
    NEXT_SIBLING = 'nextSibling',
 
34
    CONTAINS = 'contains',
 
35
    COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
 
36
    INNER_TEXT = 'innerText',
 
37
    TEXT_CONTENT = 'textContent',
 
38
    LENGTH = 'length',
 
39
 
 
40
    UNDEFINED = undefined;
 
41
 
 
42
var re_tag = /<([a-z]+)/i;
 
43
 
 
44
var templateCache = {};
 
45
 
 
46
Y.DOM = {
 
47
    /**
 
48
     * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
 
49
     * @method byId         
 
50
     * @param {String} id the id attribute 
 
51
     * @param {Object} doc optional The document to search. Defaults to current document 
 
52
     * @return {HTMLElement | null} The HTMLElement with the id, or null if none found. 
 
53
     */
 
54
    byId: function(id, doc) {
 
55
        return Y.DOM._getDoc(doc).getElementById(id);
 
56
    },
 
57
 
 
58
    /**
 
59
     * Returns the text content of the HTMLElement. 
 
60
     * @method getText         
 
61
     * @param {HTMLElement} element The html element. 
 
62
     * @return {String} The text content of the element (includes text of any descending elements).
 
63
     */
 
64
    getText: function(element) {
 
65
        var text = element ? element[TEXT_CONTENT] : '';
 
66
        if (text === UNDEFINED && INNER_TEXT in element) {
 
67
            text = element[INNER_TEXT];
 
68
        } 
 
69
        return text || ''; 
 
70
    },
 
71
 
 
72
    /**
 
73
     * Finds the firstChild of the given HTMLElement. 
 
74
     * @method firstChild
 
75
     * @param {HTMLElement} element The html element. 
 
76
     * @param {Function} fn optional An optional boolean test to apply.
 
77
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
78
     * If no function is given, the first found is returned.
 
79
     * @return {HTMLElement | null} The first matching child html element.
 
80
     */
 
81
    firstChild: function(element, fn) {
 
82
        return Y.DOM._childBy(element, null, fn); 
 
83
    },
 
84
 
 
85
    firstChildByTag: function(element, tag, fn) {
 
86
        return Y.DOM._childBy(element, tag, fn); 
 
87
    },
 
88
 
 
89
    /**
 
90
     * Finds the lastChild of the given HTMLElement.
 
91
     * @method lastChild
 
92
     * @param {HTMLElement} element The html element.
 
93
     * @param {Function} fn optional An optional boolean test to apply.
 
94
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
95
     * If no function is given, the first found is returned.
 
96
     * @return {HTMLElement | null} The first matching child html element.
 
97
     */
 
98
    lastChild: function(element, fn) {
 
99
        return Y.DOM._childBy(element, null, fn, true); 
 
100
    },
 
101
 
 
102
    lastChildByTag: function(element, tag, fn) {
 
103
        return Y.DOM._childBy(element, tag, fn, true); 
 
104
    },
 
105
 
 
106
    /**
 
107
     * Finds all HTMLElement childNodes matching the given tag.
 
108
     * @method childrenByTag
 
109
     * @param {HTMLElement} element The html element.
 
110
     * @param {String} tag The tag to search for.
 
111
     * @param {Function} fn optional An optional boolean test to apply.
 
112
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
113
     * If no function is given, all children with the given tag are collected.
 
114
     * @return {Array} The collection of child elements.
 
115
     */
 
116
    childrenByTag: function() {
 
117
        if (document[DOCUMENT_ELEMENT].children) {
 
118
            return function(element, tag, fn, toArray) { // TODO: keep toArray option?
 
119
                tag = (tag && tag !== '*') ? tag : null;
 
120
                var elements = [];
 
121
                if (element) {
 
122
                    elements = (tag) ? element.children.tags(tag) : element.children; 
 
123
 
 
124
                    if (fn || toArray) {
 
125
                        elements = Y.DOM.filterElementsBy(elements, fn);
 
126
                    }
 
127
                }
 
128
 
 
129
                return elements; 
 
130
            };
 
131
        } else {
 
132
            return function(element, tag, fn) {
 
133
                tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
 
134
                var elements = [],
 
135
                    wrapFn = fn;
 
136
 
 
137
                if (element) {
 
138
                    elements = element.childNodes; 
 
139
                    if (tag) { // wrap fn and add tag test TODO: allow tag in filterElementsBy?
 
140
                        wrapFn = function(el) {
 
141
                            return el[TAG_NAME].toUpperCase() === tag && (!fn || fn(el));
 
142
                        };
 
143
                    }
 
144
 
 
145
                    elements = Y.DOM.filterElementsBy(elements, wrapFn); 
 
146
                }
 
147
                return elements;
 
148
            };
 
149
        }
 
150
    }(),
 
151
 
 
152
    /**
 
153
     * Finds all HTMLElement childNodes.
 
154
     * @method children
 
155
     * @param {HTMLElement} element The html element.
 
156
     * @param {Function} fn optional An optional boolean test to apply.
 
157
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
158
     * If no function is given, all children are collected.
 
159
     * @return {Array} The collection of child elements.
 
160
     */
 
161
    children: function(element, fn) {
 
162
        return Y.DOM.childrenByTag(element, null, fn); 
 
163
    },
 
164
 
 
165
    /**
 
166
     * Finds the previous sibling of the element.
 
167
     * @method previous
 
168
     * @param {HTMLElement} element The html element.
 
169
     * @param {Function} fn optional An optional boolean test to apply.
 
170
     * The optional function is passed the current DOM node being tested as its only argument.
 
171
     * If no function is given, the first sibling is returned.
 
172
     * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
 
173
     * @return {HTMLElement | null} The matching DOM node or null if none found. 
 
174
     */
 
175
    previous: function(element, fn, all) {
 
176
        return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn);
 
177
    },
 
178
 
 
179
    /**
 
180
     * Finds the next sibling of the element.
 
181
     * @method next
 
182
     * @param {HTMLElement} element The html element.
 
183
     * @param {Function} fn optional An optional boolean test to apply.
 
184
     * The optional function is passed the current DOM node being tested as its only argument.
 
185
     * If no function is given, the first sibling is returned.
 
186
     * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
 
187
     * @return {HTMLElement | null} The matching DOM node or null if none found. 
 
188
     */
 
189
    next: function(element, fn) {
 
190
        return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn);
 
191
    },
 
192
 
 
193
    /**
 
194
     * Finds the ancestor of the element.
 
195
     * @method ancestor
 
196
     * @param {HTMLElement} element The html element.
 
197
     * @param {Function} fn optional An optional boolean test to apply.
 
198
     * The optional function is passed the current DOM node being tested as its only argument.
 
199
     * If no function is given, the parentNode is returned.
 
200
     * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
 
201
     * @return {HTMLElement | null} The matching DOM node or null if none found. 
 
202
     */
 
203
    ancestor: function(element, fn) {
 
204
        return Y.DOM.elementByAxis(element, PARENT_NODE, fn);
 
205
    },
 
206
 
 
207
    /**
 
208
     * Searches the element by the given axis for the first matching element.
 
209
     * @method elementByAxis
 
210
     * @param {HTMLElement} element The html element.
 
211
     * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
 
212
     * @param {Function} fn optional An optional boolean test to apply.
 
213
     * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
 
214
     * The optional function is passed the current DOM node being tested as its only argument.
 
215
     * If no function is given, the first element is returned.
 
216
     * @return {HTMLElement | null} The matching element or null if none found.
 
217
     */
 
218
    elementByAxis: function(element, axis, fn, all) {
 
219
        while (element && (element = element[axis])) { // NOTE: assignment
 
220
                if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
 
221
                    return element;
 
222
                }
 
223
        }
 
224
        return null;
 
225
    },
 
226
 
 
227
    /**
 
228
     * Finds all elements with the given tag.
 
229
     * @method byTag
 
230
     * @param {String} tag The tag being search for. 
 
231
     * @param {HTMLElement} root optional An optional root element to start from.
 
232
     * @param {Function} fn optional An optional boolean test to apply.
 
233
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
234
     * If no function is given, all elements with the given tag are returned.
 
235
     * @return {Array} The collection of matching elements.
 
236
     */
 
237
    byTag: function(tag, root, fn) {
 
238
        root = root || Y.config.doc;
 
239
 
 
240
        var elements = root.getElementsByTagName(tag),
 
241
            retNodes = [];
 
242
 
 
243
        for (var i = 0, len = elements[LENGTH]; i < len; ++i) {
 
244
            if ( !fn || fn(elements[i]) ) {
 
245
                retNodes[retNodes[LENGTH]] = elements[i];
 
246
            }
 
247
        }
 
248
        return retNodes;
 
249
    },
 
250
 
 
251
    /**
 
252
     * Finds the first element with the given tag.
 
253
     * @method firstByTag
 
254
     * @param {String} tag The tag being search for. 
 
255
     * @param {HTMLElement} root optional An optional root element to start from.
 
256
     * @param {Function} fn optional An optional boolean test to apply.
 
257
     * The optional function is passed the current HTMLElement being tested as its only argument.
 
258
     * If no function is given, the first match is returned.
 
259
     * @return {HTMLElement} The matching element.
 
260
     */
 
261
    firstByTag: function(tag, root, fn) {
 
262
        root = root || Y.config.doc;
 
263
 
 
264
        var elements = root.getElementsByTagName(tag),
 
265
            ret = null;
 
266
 
 
267
        for (var i = 0, len = elements[LENGTH]; i < len; ++i) {
 
268
            if ( !fn || fn(elements[i]) ) {
 
269
                ret = elements[i];
 
270
                break;
 
271
            }
 
272
        }
 
273
        return ret;
 
274
    },
 
275
 
 
276
    /**
 
277
     * Filters a collection of HTMLElements by the given attributes.
 
278
     * @method filterElementsBy
 
279
     * @param {Array} elements The collection of HTMLElements to filter.
 
280
     * @param {Function} fn A boolean test to apply.
 
281
     * The function is passed the current HTMLElement being tested as its only argument.
 
282
     * If no function is given, all HTMLElements are kept.
 
283
     * @return {Array} The filtered collection of HTMLElements.
 
284
     */
 
285
    filterElementsBy: function(elements, fn, firstOnly) {
 
286
        var ret = (firstOnly) ? null : [];
 
287
        for (var i = 0, el; el = elements[i++];) {
 
288
            if ( el[TAG_NAME] && (!fn || fn(el)) ) {
 
289
                if (firstOnly) {
 
290
                    ret = el;
 
291
                    break;
 
292
                } else {
 
293
                    ret[ret[LENGTH]] = el;
 
294
                }
 
295
            }
 
296
        }
 
297
 
 
298
        return ret;
 
299
    },
 
300
 
 
301
    /**
 
302
     * Determines whether or not one HTMLElement is or contains another HTMLElement.
 
303
     * @method contains
 
304
     * @param {HTMLElement} element The containing html element.
 
305
     * @param {HTMLElement} needle The html element that may be contained.
 
306
     * @return {Boolean} Whether or not the element is or contains the needle.
 
307
     */
 
308
    contains: function(element, needle) {
 
309
        var ret = false;
 
310
 
 
311
        if (!needle || !needle[NODE_TYPE] || !element || !element[NODE_TYPE]) {
 
312
            ret = false;
 
313
        } else if (element[CONTAINS])  {
 
314
            if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
 
315
                ret = element[CONTAINS](needle);
 
316
            } else {
 
317
                ret = Y.DOM._bruteContains(element, needle); 
 
318
            }
 
319
        } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
 
320
            if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) { 
 
321
                ret = true;
 
322
            }
 
323
        }
 
324
 
 
325
        return ret;
 
326
    },
 
327
 
 
328
    /**
 
329
     * Returns an HTMLElement for the given string of HTML.
 
330
     * @method create
 
331
     * @param {String} html The string of HTML to convert to a DOM element. 
 
332
     * @param {Document} document An optional document to create the node with.
 
333
     * @return {HTMLElement} The newly created element. 
 
334
     */
 
335
    create: function(html, doc) {
 
336
        doc = doc || Y.config.doc;
 
337
        var m = re_tag.exec(html);
 
338
        var create = Y.DOM._create,
 
339
            custom = Y.DOM.creators,
 
340
            tag, ret;
 
341
 
 
342
        if (m && custom[m[1]]) {
 
343
            if (typeof custom[m[1]] === 'function') {
 
344
                create = custom[m[1]];
 
345
            } else {
 
346
                tag = custom[m[1]];
 
347
            }
 
348
        }
 
349
        ret = create(html, doc, tag);
 
350
        return (ret.childNodes.length > 1) ? ret.childNodes : ret.childNodes[0]; // collection or item
 
351
        //return ret.firstChild;
 
352
    },
 
353
 
 
354
    _create: function(html, doc, tag) {
 
355
        tag = tag || 'div';
 
356
        var frag = templateCache[tag] || doc.createElement(tag);
 
357
        frag.innerHTML = Y.Lang.trim(html);
 
358
        return frag;
 
359
    },
 
360
 
 
361
    /**
 
362
     * Brute force version of contains.
 
363
     * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
 
364
     * @method _bruteContains
 
365
     * @private
 
366
     * @param {HTMLElement} element The containing html element.
 
367
     * @param {HTMLElement} needle The html element that may be contained.
 
368
     * @return {Boolean} Whether or not the element is or contains the needle.
 
369
     */
 
370
    _bruteContains: function(element, needle) {
 
371
        while (needle) {
 
372
            if (element === needle) {
 
373
                return true;
 
374
            }
 
375
            needle = needle.parentNode;
 
376
        }
 
377
        return false;
 
378
    },
 
379
 
 
380
    /**
 
381
     * Memoizes dynamic regular expressions to boost runtime performance. 
 
382
     * @method _getRegExp
 
383
     * @private
 
384
     * @param {String} str The string to convert to a regular expression.
 
385
     * @param {String} flags optional An optinal string of flags.
 
386
     * @return {RegExp} An instance of RegExp
 
387
     */
 
388
    _getRegExp: function(str, flags) {
 
389
        flags = flags || '';
 
390
        Y.DOM._regexCache = Y.DOM._regexCache || {};
 
391
        if (!Y.DOM._regexCache[str + flags]) {
 
392
            Y.DOM._regexCache[str + flags] = new RegExp(str, flags);
 
393
        }
 
394
        return Y.DOM._regexCache[str + flags];
 
395
    },
 
396
 
 
397
    /**
 
398
     * returns the appropriate document.
 
399
     * @method _getDoc
 
400
     * @private
 
401
     * @param {HTMLElement} element optional Target element.
 
402
     * @return {Object} The document for the given element or the default document. 
 
403
     */
 
404
    _getDoc: function(element) {
 
405
        element = element || {};
 
406
        return (element[NODE_TYPE] === 9) ? element : element[OWNER_DOCUMENT] ||
 
407
                                                Y.config.doc;
 
408
    },
 
409
 
 
410
    /**
 
411
     * returns the appropriate window.
 
412
     * @method _getWin
 
413
     * @private
 
414
     * @param {HTMLElement} element optional Target element.
 
415
     * @return {Object} The window for the given element or the default window. 
 
416
     */
 
417
    _getWin: function(element) {
 
418
        var doc = Y.DOM._getDoc(element);
 
419
        return (element.document) ? element : doc[DEFAULT_VIEW] ||
 
420
                                        doc[PARENT_WINDOW] || Y.config.win;
 
421
    },
 
422
 
 
423
    _childBy: function(element, tag, fn, rev) {
 
424
        var ret = null,
 
425
            root, axis;
 
426
 
 
427
        if (element) {
 
428
            if (rev) {
 
429
                root = element[LAST_CHILD];
 
430
                axis = PREVIOUS_SIBLING;
 
431
            } else {
 
432
                root = element[FIRST_CHILD];
 
433
                axis = NEXT_SIBLING;
 
434
            }
 
435
 
 
436
            if (Y.DOM._testElement(root, tag, fn)) { // is the matching element
 
437
                ret = root;
 
438
            } else { // need to scan nextSibling axis of firstChild to find matching element
 
439
                ret = Y.DOM.elementByAxis(root, axis, fn);
 
440
            }
 
441
        }
 
442
        return ret;
 
443
 
 
444
    },
 
445
 
 
446
    _testElement: function(element, tag, fn) {
 
447
        tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
 
448
        return (element && element[TAG_NAME] &&
 
449
                (!tag || element[TAG_NAME].toUpperCase() === tag) &&
 
450
                (!fn || fn(element)));
 
451
    },
 
452
 
 
453
    creators: {},
 
454
 
 
455
    _IESimpleCreate: function(html, doc) {
 
456
        doc = doc || Y.config.doc;
 
457
        return doc.createElement(html);
 
458
    }
 
459
};
 
460
 
 
461
 
 
462
(function() {
 
463
    var creators = Y.DOM.creators,
 
464
        create = Y.DOM.create,
 
465
        re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/;
 
466
 
 
467
    var TABLE_OPEN = '<table>',
 
468
        TABLE_CLOSE = '</table>';
 
469
 
 
470
    if (Y.UA.gecko || Y.UA.ie) { // require custom creation code for certain element types
 
471
        Y.mix(creators, {
 
472
            option: function(html, doc) {
 
473
                var frag = create('<select>' + html + '</select>');
 
474
                return frag;
 
475
            },
 
476
 
 
477
            tr: function(html, doc) {
 
478
                var frag = creators.tbody('<tbody>' + html + '</tbody>', doc);
 
479
                return frag.firstChild;
 
480
            },
 
481
 
 
482
            td: function(html, doc) {
 
483
                var frag = creators.tr('<tr>' + html + '</tr>', doc);
 
484
                return frag.firstChild;
 
485
            }, 
 
486
 
 
487
            tbody: function(html, doc) {
 
488
                var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc);
 
489
                return frag;
 
490
            },
 
491
 
 
492
            legend: 'fieldset'
 
493
        });
 
494
 
 
495
        creators.col = creators.tbody; // IE wraps in colgroup
 
496
    }
 
497
 
 
498
    if (Y.UA.ie) {
 
499
        // TODO: allow multiples ("<link><link>")
 
500
        creators.col = creators.script = creators.link = Y.DOM._IESimpleCreate; 
 
501
 
 
502
        // TODO: thead/tfoot with nested tbody
 
503
        creators.tbody = function(html, doc) {
 
504
            var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc);
 
505
            var tb = frag.children.tags('tbody')[0];
 
506
            if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
 
507
                tb.parentNode.removeChild(tb);
 
508
            }
 
509
            return frag;
 
510
        };
 
511
 
 
512
    }
 
513
 
 
514
    if (Y.UA.gecko || Y.UA.ie) { // require custom creation code for certain element types
 
515
        Y.mix(creators, {
 
516
                th: creators.td,
 
517
                thead: creators.tbody,
 
518
                tfoot: creators.tbody,
 
519
                caption: creators.tbody,
 
520
                colgroup: creators.tbody,
 
521
                col: creators.tbody,
 
522
                optgroup: creators.option
 
523
        });
 
524
    }
 
525
})();
 
526
/** 
 
527
 * The DOM utility provides a cross-browser abtraction layer
 
528
 * normalizing DOM tasks, and adds extra helper functionality
 
529
 * for other common tasks. 
 
530
 * @module dom
 
531
 * @submodule dom-base
 
532
 * @for DOM
 
533
 */
 
534
 
 
535
var CLASS_NAME = 'className';
 
536
 
 
537
Y.mix(Y.DOM, {
 
538
    /**
 
539
     * Determines whether a DOM element has the given className.
 
540
     * @method hasClass
 
541
     * @param {HTMLElement} element The DOM element. 
 
542
     * @param {String} className the class name to search for
 
543
     * @return {Boolean} Whether or not the element has the given class. 
 
544
     */
 
545
    hasClass: function(node, className) {
 
546
        var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
 
547
        return re.test(node[CLASS_NAME]);
 
548
    },
 
549
 
 
550
    /**
 
551
     * Adds a class name to a given DOM element.
 
552
     * @method addClass         
 
553
     * @param {HTMLElement} element The DOM element. 
 
554
     * @param {String} className the class name to add to the class attribute
 
555
     */
 
556
    addClass: function(node, className) {
 
557
        if (!Y.DOM.hasClass(node, className)) { // skip if already present 
 
558
            node[CLASS_NAME] = Y.Lang.trim([node[CLASS_NAME], className].join(' '));
 
559
        }
 
560
    },
 
561
 
 
562
    /**
 
563
     * Removes a class name from a given element.
 
564
     * @method removeClass         
 
565
     * @param {HTMLElement} element The DOM element. 
 
566
     * @param {String} className the class name to remove from the class attribute
 
567
     */
 
568
    removeClass: function(node, className) {
 
569
        if (className && Y.DOM.hasClass(node, className)) {
 
570
            node[CLASS_NAME] = Y.Lang.trim(node[CLASS_NAME].replace(Y.DOM._getRegExp('(?:^|\\s+)' +
 
571
                            className + '(?:\\s+|$)'), ' '));
 
572
 
 
573
            if ( Y.DOM.hasClass(node, className) ) { // in case of multiple adjacent
 
574
                Y.DOM.removeClass(node, className);
 
575
            }
 
576
        }                 
 
577
    },
 
578
 
 
579
    /**
 
580
     * Replace a class with another class for a given element.
 
581
     * If no oldClassName is present, the newClassName is simply added.
 
582
     * @method replaceClass  
 
583
     * @param {HTMLElement} element The DOM element. 
 
584
     * @param {String} oldClassName the class name to be replaced
 
585
     * @param {String} newClassName the class name that will be replacing the old class name
 
586
     */
 
587
    replaceClass: function(node, oldC, newC) {
 
588
        Y.DOM.addClass(node, newC);
 
589
        Y.DOM.removeClass(node, oldC);
 
590
    },
 
591
 
 
592
    /**
 
593
     * If the className exists on the node it is removed, if it doesn't exist it is added.
 
594
     * @method toggleClass  
 
595
     * @param {HTMLElement} element The DOM element. 
 
596
     * @param {String} className the class name to be toggled
 
597
     */
 
598
    toggleClass: function(node, className) {
 
599
        if (Y.DOM.hasClass(node, className)) {
 
600
            Y.DOM.removeClass(node, className);
 
601
        } else {
 
602
            Y.DOM.addClass(node, className);
 
603
        }
 
604
    }
 
605
});
 
606
 
 
607
/** 
 
608
 * Add style management functionality to DOM.
 
609
 * @module dom
 
610
 * @submodule dom-style
 
611
 * @for DOM
 
612
 */
 
613
 
 
614
var DOCUMENT_ELEMENT = 'documentElement',
 
615
    DEFAULT_VIEW = 'defaultView',
 
616
    OWNER_DOCUMENT = 'ownerDocument',
 
617
    STYLE = 'style',
 
618
    FLOAT = 'float',
 
619
    CSS_FLOAT = 'cssFloat',
 
620
    STYLE_FLOAT = 'styleFloat',
 
621
    TRANSPARENT = 'transparent',
 
622
    VISIBLE = 'visible',
 
623
    WIDTH = 'width',
 
624
    HEIGHT = 'height',
 
625
    BORDER_TOP_WIDTH = 'borderTopWidth',
 
626
    BORDER_RIGHT_WIDTH = 'borderRightWidth',
 
627
    BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
 
628
    BORDER_LEFT_WIDTH = 'borderLeftWidth',
 
629
    GET_COMPUTED_STYLE = 'getComputedStyle',
 
630
 
 
631
    DOCUMENT = Y.config.doc,
 
632
    UNDEFINED = undefined,
 
633
 
 
634
    re_color = /color$/i;
 
635
 
 
636
 
 
637
Y.mix(Y.DOM, {
 
638
    CUSTOM_STYLES: {},
 
639
 
 
640
 
 
641
    /**
 
642
     * Sets a style property for a given element.
 
643
     * @method setStyle
 
644
     * @param {HTMLElement} An HTMLElement to apply the style to.
 
645
     * @param {String} att The style property to set. 
 
646
     * @param {String|Number} val The value. 
 
647
     */
 
648
    setStyle: function(node, att, val) {
 
649
        var style = node[STYLE],
 
650
            CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES;
 
651
 
 
652
        if (style) {
 
653
            if (att in CUSTOM_STYLES) {
 
654
                if (CUSTOM_STYLES[att].set) {
 
655
                    CUSTOM_STYLES[att].set(node, val, style);
 
656
                    return; // NOTE: return
 
657
                } else if (typeof CUSTOM_STYLES[att] === 'string') {
 
658
                    att = CUSTOM_STYLES[att];
 
659
                }
 
660
            }
 
661
            style[att] = val; 
 
662
        }
 
663
    },
 
664
 
 
665
    /**
 
666
     * Returns the current style value for the given property.
 
667
     * @method getStyle
 
668
     * @param {HTMLElement} An HTMLElement to get the style from.
 
669
     * @param {String} att The style property to get. 
 
670
     */
 
671
    getStyle: function(node, att) {
 
672
        var style = node[STYLE],
 
673
            CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES,
 
674
            val = '';
 
675
 
 
676
        if (style) {
 
677
            if (att in CUSTOM_STYLES) {
 
678
                if (CUSTOM_STYLES[att].get) {
 
679
                    return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return
 
680
                } else if (typeof CUSTOM_STYLES[att] === 'string') {
 
681
                    att = CUSTOM_STYLES[att];
 
682
                }
 
683
            }
 
684
            val = style[att];
 
685
            if (val === '') { // TODO: is empty string sufficient?
 
686
                val = Y.DOM[GET_COMPUTED_STYLE](node, att);
 
687
            }
 
688
        }
 
689
 
 
690
        return val;
 
691
    },
 
692
 
 
693
    /**
 
694
     * Sets multiple style properties.
 
695
     * @method setStyles
 
696
     * @param {HTMLElement} node An HTMLElement to apply the styles to. 
 
697
     * @param {Object} hash An object literal of property:value pairs. 
 
698
     */
 
699
    'setStyles': function(node, hash) {
 
700
        Y.each(hash, function(v, n) {
 
701
            Y.DOM.setStyle(node, n, v);
 
702
        }, Y.DOM);
 
703
    },
 
704
 
 
705
    /**
 
706
     * Returns the computed style for the given node.
 
707
     * @method getComputedStyle
 
708
     * @param {HTMLElement} An HTMLElement to get the style from.
 
709
     * @param {String} att The style property to get. 
 
710
     * @return {String} The computed value of the style property. 
 
711
     */
 
712
    getComputedStyle: function(node, att) {
 
713
        var val = '',
 
714
            doc = node[OWNER_DOCUMENT];
 
715
 
 
716
        if (node[STYLE]) {
 
717
            val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, '')[att];
 
718
        }
 
719
        return val;
 
720
    }
 
721
});
 
722
 
 
723
if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) {
 
724
    Y.DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT;
 
725
} else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) {
 
726
    Y.DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT;
 
727
}
 
728
 
 
729
if (Y.UA.opera) { // opera defaults to hex instead of RGB
 
730
    Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
 
731
        var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
 
732
            val = view[GET_COMPUTED_STYLE](node, '')[att];
 
733
 
 
734
        if (re_color.test(att)) {
 
735
            val = Y.Color.toRGB(val);
 
736
        }
 
737
 
 
738
        return val;
 
739
    };
 
740
 
 
741
}
 
742
 
 
743
if (Y.UA.webkit) { // safari converts transparent to rgba()
 
744
    Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
 
745
        var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
 
746
            val = view[GET_COMPUTED_STYLE](node, '')[att];
 
747
 
 
748
        if (val === 'rgba(0, 0, 0, 0)') {
 
749
            val = TRANSPARENT; 
 
750
        }
 
751
 
 
752
        return val;
 
753
    };
 
754
 
 
755
}
 
756
 
 
757
/**
 
758
 * Adds position and region management functionality to DOM.
 
759
 * @module dom
 
760
 * @submodule dom-screen
 
761
 * @for DOM
 
762
 */
 
763
 
 
764
var OFFSET_TOP = 'offsetTop',
 
765
    DOCUMENT_ELEMENT = 'documentElement',
 
766
    COMPAT_MODE = 'compatMode',
 
767
    OFFSET_LEFT = 'offsetLeft',
 
768
    OFFSET_PARENT = 'offsetParent',
 
769
    POSITION = 'position',
 
770
    FIXED = 'fixed',
 
771
    RELATIVE = 'relative',
 
772
    LEFT = 'left',
 
773
    TOP = 'top',
 
774
    SCROLL_LEFT = 'scrollLeft',
 
775
    SCROLL_TOP = 'scrollTop',
 
776
    _BACK_COMPAT = 'BackCompat',
 
777
    MEDIUM = 'medium',
 
778
    HEIGHT = 'height',
 
779
    WIDTH = 'width',
 
780
    BORDER_LEFT_WIDTH = 'borderLeftWidth',
 
781
    BORDER_TOP_WIDTH = 'borderTopWidth',
 
782
    GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
 
783
    GET_COMPUTED_STYLE = 'getComputedStyle',
 
784
    RE_TABLE = /^t(?:able|d|h)$/i;
 
785
 
 
786
Y.mix(Y.DOM, {
 
787
    /**
 
788
     * Returns the inner height of the viewport (exludes scrollbar). 
 
789
     * @method winHeight
 
790
     * @return {Int} The pixel height of the viewport.
 
791
     */
 
792
    winHeight: function(node) {
 
793
        var h = Y.DOM._getWinSize(node)[HEIGHT];
 
794
        return h;
 
795
    },
 
796
 
 
797
    /**
 
798
     * Returns the inner width of the viewport (exludes scrollbar). 
 
799
     * @method winWidth
 
800
     * @return {Int} The pixel width of the viewport.
 
801
     */
 
802
    winWidth: function(node) {
 
803
        var w = Y.DOM._getWinSize(node)[WIDTH];
 
804
        return w;
 
805
    },
 
806
 
 
807
    /**
 
808
     * Document height 
 
809
     * @method docHeight
 
810
     * @return {Int} The pixel height of the document.
 
811
     */
 
812
    docHeight:  function(node) {
 
813
        var h = Y.DOM._getDocSize(node)[HEIGHT];
 
814
        return Math.max(h, Y.DOM._getWinSize(node)[HEIGHT]);
 
815
    },
 
816
 
 
817
    /**
 
818
     * Document width 
 
819
     * @method docWidth
 
820
     * @return {Int} The pixel width of the document.
 
821
     */
 
822
    docWidth:  function(node) {
 
823
        var w = Y.DOM._getDocSize(node)[WIDTH];
 
824
        return Math.max(w, Y.DOM._getWinSize(node)[WIDTH]);
 
825
    },
 
826
 
 
827
    /**
 
828
     * Amount page has been scroll vertically 
 
829
     * @method docScrollX
 
830
     * @return {Int} The scroll amount in pixels.
 
831
     */
 
832
    docScrollX: function(node) {
 
833
        var doc = Y.DOM._getDoc();
 
834
        return Math.max(doc[DOCUMENT_ELEMENT][SCROLL_LEFT], doc.body[SCROLL_LEFT]);
 
835
    },
 
836
 
 
837
    /**
 
838
     * Amount page has been scroll horizontally 
 
839
     * @method docScrollY
 
840
     * @return {Int} The scroll amount in pixels.
 
841
     */
 
842
    docScrollY:  function(node) {
 
843
        var doc = Y.DOM._getDoc();
 
844
        return Math.max(doc[DOCUMENT_ELEMENT][SCROLL_TOP], doc.body[SCROLL_TOP]);
 
845
    },
 
846
 
 
847
    /**
 
848
     * Gets the current position of an element based on page coordinates. 
 
849
     * Element must be part of the DOM tree to have page coordinates
 
850
     * (display:none or elements not appended return false).
 
851
     * @method getXY
 
852
     * @param element The target element
 
853
     * @return {Array} The XY position of the element
 
854
 
 
855
     TODO: test inDocument/display
 
856
     */
 
857
    getXY: function() {
 
858
        if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
 
859
            return function(node) {
 
860
                if (!node) {
 
861
                    return false;
 
862
                }
 
863
                var scrollLeft = Y.DOM.docScrollX(node),
 
864
                    scrollTop = Y.DOM.docScrollY(node),
 
865
                    box = node[GET_BOUNDING_CLIENT_RECT](),
 
866
                    doc = Y.DOM._getDoc(node),
 
867
                    //Round the numbers so we get sane data back
 
868
                    xy = [Math.floor(box[LEFT]), Math.floor(box[TOP])];
 
869
 
 
870
                    if (Y.UA.ie) {
 
871
                        var off1 = 2, off2 = 2,
 
872
                        mode = doc[COMPAT_MODE],
 
873
                        bLeft = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH),
 
874
                        bTop = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
 
875
 
 
876
                        if (Y.UA.ie === 6) {
 
877
                            if (mode !== _BACK_COMPAT) {
 
878
                                off1 = 0;
 
879
                                off2 = 0;
 
880
                            }
 
881
                        }
 
882
                        
 
883
                        if ((mode == _BACK_COMPAT)) {
 
884
                            if (bLeft !== MEDIUM) {
 
885
                                off1 = parseInt(bLeft, 10);
 
886
                            }
 
887
                            if (bTop !== MEDIUM) {
 
888
                                off2 = parseInt(bTop, 10);
 
889
                            }
 
890
                        }
 
891
                        
 
892
                        xy[0] -= off1;
 
893
                        xy[1] -= off2;
 
894
                    }
 
895
 
 
896
                if ((scrollTop || scrollLeft)) {
 
897
                    xy[0] += scrollLeft;
 
898
                    xy[1] += scrollTop;
 
899
                }
 
900
 
 
901
                // gecko may return sub-pixel (non-int) values
 
902
                xy[0] = Math.floor(xy[0]);
 
903
                xy[1] = Math.floor(xy[1]);
 
904
 
 
905
                return xy;                   
 
906
            };
 
907
        } else {
 
908
            return function(node) { // manually calculate by crawling up offsetParents
 
909
                //Calculate the Top and Left border sizes (assumes pixels)
 
910
                var xy = [node[OFFSET_LEFT], node[OFFSET_TOP]],
 
911
                    parentNode = node,
 
912
                    bCheck = ((Y.UA.gecko || (Y.UA.webkit > 519)) ? true : false);
 
913
 
 
914
                while ((parentNode = parentNode[OFFSET_PARENT])) {
 
915
                    xy[0] += parentNode[OFFSET_LEFT];
 
916
                    xy[1] += parentNode[OFFSET_TOP];
 
917
                    if (bCheck) {
 
918
                        xy = Y.DOM._calcBorders(parentNode, xy);
 
919
                    }
 
920
                }
 
921
 
 
922
                // account for any scrolled ancestors
 
923
                if (Y.DOM.getStyle(node, POSITION) != FIXED) {
 
924
                    parentNode = node;
 
925
                    var scrollTop, scrollLeft;
 
926
 
 
927
                    while ((parentNode = parentNode.parentNode)) {
 
928
                        scrollTop = parentNode[SCROLL_TOP];
 
929
                        scrollLeft = parentNode[SCROLL_LEFT];
 
930
 
 
931
                        //Firefox does something funky with borders when overflow is not visible.
 
932
                        if (Y.UA.gecko && (Y.DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
 
933
                                xy = Y.DOM._calcBorders(parentNode, xy);
 
934
                        }
 
935
                        
 
936
 
 
937
                        if (scrollTop || scrollLeft) {
 
938
                            xy[0] -= scrollLeft;
 
939
                            xy[1] -= scrollTop;
 
940
                        }
 
941
                    }
 
942
                    xy[0] += Y.DOM.docScrollX(node);
 
943
                    xy[1] += Y.DOM.docScrollY(node);
 
944
 
 
945
                } else {
 
946
                    //Fix FIXED position -- add scrollbars
 
947
                    if (Y.UA.opera) {
 
948
                        xy[0] -= Y.DOM.docScrollX(node);
 
949
                        xy[1] -= Y.DOM.docScrollY(node);
 
950
                    } else if (Y.UA.webkit || Y.UA.gecko) {
 
951
                        xy[0] += Y.DOM.docScrollX(node);
 
952
                        xy[1] += Y.DOM.docScrollY(node);
 
953
                    }
 
954
                }
 
955
                //Round the numbers so we get sane data back
 
956
                xy[0] = Math.floor(xy[0]);
 
957
                xy[1] = Math.floor(xy[1]);
 
958
 
 
959
                return xy;                
 
960
            };
 
961
        }
 
962
    }(),// NOTE: Executing for loadtime branching
 
963
 
 
964
    /**
 
965
     * Gets the current X position of an element based on page coordinates. 
 
966
     * Element must be part of the DOM tree to have page coordinates
 
967
     * (display:none or elements not appended return false).
 
968
     * @method getX
 
969
     * @param element The target element
 
970
     * @return {Int} The X position of the element
 
971
     */
 
972
 
 
973
    getX: function(node) {
 
974
        return Y.DOM.getXY(node)[0];
 
975
    },
 
976
 
 
977
    /**
 
978
     * Gets the current Y position of an element based on page coordinates. 
 
979
     * Element must be part of the DOM tree to have page coordinates
 
980
     * (display:none or elements not appended return false).
 
981
     * @method getY
 
982
     * @param element The target element
 
983
     * @return {Int} The Y position of the element
 
984
     */
 
985
 
 
986
    getY: function(node) {
 
987
        return Y.DOM.getXY(node)[1];
 
988
    },
 
989
 
 
990
    /**
 
991
     * Set the position of an html element in page coordinates.
 
992
     * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 
993
     * @method setXY
 
994
     * @param element The target element
 
995
     * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
 
996
     * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
 
997
     */
 
998
    setXY: function(node, xy, noRetry) {
 
999
        var pos = Y.DOM.getStyle(node, POSITION),
 
1000
            setStyle = Y.DOM.setStyle,
 
1001
            delta = [ // assuming pixels; if not we will have to retry
 
1002
                parseInt( Y.DOM[GET_COMPUTED_STYLE](node, LEFT), 10 ),
 
1003
                parseInt( Y.DOM[GET_COMPUTED_STYLE](node, TOP), 10 )
 
1004
            ];
 
1005
    
 
1006
        if (pos == 'static') { // default to relative
 
1007
            pos = RELATIVE;
 
1008
            setStyle(node, POSITION, pos);
 
1009
        }
 
1010
 
 
1011
        var currentXY = Y.DOM.getXY(node);
 
1012
 
 
1013
        if (currentXY === false) { // has to be part of doc to have xy
 
1014
            return false; 
 
1015
        }
 
1016
        
 
1017
        if ( isNaN(delta[0]) ) {// in case of 'auto'
 
1018
            delta[0] = (pos == RELATIVE) ? 0 : node[OFFSET_LEFT];
 
1019
        } 
 
1020
        if ( isNaN(delta[1]) ) { // in case of 'auto'
 
1021
            delta[1] = (pos == RELATIVE) ? 0 : node[OFFSET_TOP];
 
1022
        } 
 
1023
 
 
1024
        if (xy[0] !== null) {
 
1025
            setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
 
1026
        }
 
1027
 
 
1028
        if (xy[1] !== null) {
 
1029
            setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
 
1030
        }
 
1031
      
 
1032
        if (!noRetry) {
 
1033
            var newXY = Y.DOM.getXY(node);
 
1034
 
 
1035
            // if retry is true, try one more time if we miss 
 
1036
           if ( (xy[0] !== null && newXY[0] != xy[0]) || 
 
1037
                (xy[1] !== null && newXY[1] != xy[1]) ) {
 
1038
               Y.DOM.setXY(node, xy, true);
 
1039
           }
 
1040
        }        
 
1041
 
 
1042
    },
 
1043
 
 
1044
    /**
 
1045
     * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
 
1046
     * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 
1047
     * @method setX
 
1048
     * @param element The target element
 
1049
     * @param {Int} x The X values for new position (coordinates are page-based)
 
1050
     */
 
1051
    setX: function(node, x) {
 
1052
        return Y.DOM.setXY(node, [x, null]);
 
1053
    },
 
1054
 
 
1055
    /**
 
1056
     * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
 
1057
     * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
 
1058
     * @method setY
 
1059
     * @param element The target element
 
1060
     * @param {Int} y The Y values for new position (coordinates are page-based)
 
1061
     */
 
1062
    setY: function(node, y) {
 
1063
        return Y.DOM.setXY(node, [null, y]);
 
1064
    },
 
1065
 
 
1066
    _calcBorders: function(node, xy2) {
 
1067
        var t = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
 
1068
            l = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
 
1069
        if (Y.UA.gecko) {
 
1070
            if (RE_TABLE.test(node.tagName)) {
 
1071
                t = 0;
 
1072
                l = 0;
 
1073
            }
 
1074
        }
 
1075
        xy2[0] += l;
 
1076
        xy2[1] += t;
 
1077
        return xy2;
 
1078
    },
 
1079
 
 
1080
    _getWinSize: function(node) {
 
1081
        var doc = Y.DOM._getDoc(),
 
1082
            win = doc.defaultView || doc.parentWindow,
 
1083
            mode = doc[COMPAT_MODE],
 
1084
            h = win.innerHeight,
 
1085
            w = win.innerWidth,
 
1086
            root = doc[DOCUMENT_ELEMENT];
 
1087
 
 
1088
        if ( mode && !Y.UA.opera ) { // IE, Gecko
 
1089
            if (mode != 'CSS1Compat') { // Quirks
 
1090
                root = doc.body; 
 
1091
            }
 
1092
            h = root.clientHeight;
 
1093
            w = root.clientWidth;
 
1094
        }
 
1095
        return { height: h, width: w }; 
 
1096
    },
 
1097
 
 
1098
    _getDocSize: function(node) {
 
1099
        var doc = Y.DOM._getDoc(),
 
1100
            root = doc[DOCUMENT_ELEMENT];
 
1101
 
 
1102
        if (doc[COMPAT_MODE] != 'CSS1Compat') {
 
1103
            root = doc.body;
 
1104
        }
 
1105
 
 
1106
        return { height: root.scrollHeight, width: root.scrollWidth };
 
1107
    }
 
1108
});
 
1109
 
 
1110
/**
 
1111
 * Adds position and region management functionality to DOM.
 
1112
 * @module dom
 
1113
 * @submodule dom-screen
 
1114
 * @for DOM
 
1115
 */
 
1116
 
 
1117
var OFFSET_WIDTH = 'offsetWidth',
 
1118
    OFFSET_HEIGHT = 'offsetHeight',
 
1119
    TAG_NAME = 'tagName';
 
1120
 
 
1121
var getOffsets = function(r1, r2) {
 
1122
 
 
1123
    var t = Math.max(r1.top,    r2.top   ),
 
1124
        r = Math.min(r1.right,  r2.right ),
 
1125
        b = Math.min(r1.bottom, r2.bottom),
 
1126
        l = Math.max(r1.left,   r2.left  );
 
1127
    
 
1128
    return {
 
1129
        top: t,
 
1130
        bottom: b,
 
1131
        left: l,
 
1132
        right: r
 
1133
    };
 
1134
};
 
1135
 
 
1136
Y.mix(Y.DOM, {
 
1137
    /**
 
1138
     * Returns an Object literal containing the following about this element: (top, right, bottom, left)
 
1139
     * @method region
 
1140
     * @param {HTMLElement} element The DOM element. 
 
1141
     @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
 
1142
     */
 
1143
    region: function(node) {
 
1144
        var x = Y.DOM.getXY(node),
 
1145
            ret = false;
 
1146
        
 
1147
        if (x) {
 
1148
            ret = {
 
1149
                '0': x[0],
 
1150
                '1': x[1],
 
1151
                top: x[1],
 
1152
                right: x[0] + node[OFFSET_WIDTH],
 
1153
                bottom: x[1] + node[OFFSET_HEIGHT],
 
1154
                left: x[0],
 
1155
                height: node[OFFSET_HEIGHT],
 
1156
                width: node[OFFSET_WIDTH]
 
1157
            };
 
1158
        }
 
1159
 
 
1160
        return ret;
 
1161
    },
 
1162
 
 
1163
    /**
 
1164
     * Find the intersect information for the passes nodes.
 
1165
     * @method intersect
 
1166
     * @param {HTMLElement} element The first element 
 
1167
     * @param {HTMLElement | Object} element2 The element or region to check the interect with
 
1168
     * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
 
1169
     @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
 
1170
     */
 
1171
    intersect: function(node, node2, altRegion) {
 
1172
        var r = altRegion || Y.DOM.region(node), region = {};
 
1173
 
 
1174
        var n = node2;
 
1175
        if (n[TAG_NAME]) {
 
1176
            region = Y.DOM.region(n);
 
1177
        } else if (Y.Lang.isObject(node2)) {
 
1178
            region = node2;
 
1179
        } else {
 
1180
            return false;
 
1181
        }
 
1182
        
 
1183
        var off = getOffsets(region, r);
 
1184
        return {
 
1185
            top: off.top,
 
1186
            right: off.right,
 
1187
            bottom: off.bottom,
 
1188
            left: off.left,
 
1189
            area: ((off.bottom - off.top) * (off.right - off.left)),
 
1190
            yoff: ((off.bottom - off.top)),
 
1191
            xoff: (off.right - off.left),
 
1192
            inRegion: Y.DOM.inRegion(node, node2, false, altRegion)
 
1193
        };
 
1194
        
 
1195
    },
 
1196
    /**
 
1197
     * Check if any part of this node is in the passed region
 
1198
     * @method inRegion
 
1199
     * @param {Object} node2 The node to get the region from or an Object literal of the region
 
1200
     * $param {Boolean} all Should all of the node be inside the region
 
1201
     * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
 
1202
     * @return {Boolean} True if in region, false if not.
 
1203
     */
 
1204
    inRegion: function(node, node2, all, altRegion) {
 
1205
        var region = {},
 
1206
            r = altRegion || Y.DOM.region(node);
 
1207
 
 
1208
        var n = node2;
 
1209
        if (n[TAG_NAME]) {
 
1210
            region = Y.DOM.region(n);
 
1211
        } else if (Y.Lang.isObject(node2)) {
 
1212
            region = node2;
 
1213
        } else {
 
1214
            return false;
 
1215
        }
 
1216
            
 
1217
        if (all) {
 
1218
            return ( r.left   >= region.left   &&
 
1219
                r.right  <= region.right  && 
 
1220
                r.top    >= region.top    && 
 
1221
                r.bottom <= region.bottom    );
 
1222
        } else {
 
1223
            var off = getOffsets(region, r);
 
1224
            if (off.bottom >= off.top && off.right >= off.left) {
 
1225
                return true;
 
1226
            } else {
 
1227
                return false;
 
1228
            }
 
1229
            
 
1230
        }
 
1231
    },
 
1232
 
 
1233
    /**
 
1234
     * Check if any part of this element is in the viewport
 
1235
     * @method inViewportRegion
 
1236
     * @param {HTMLElement} element The DOM element. 
 
1237
     * @param {Boolean} all Should all of the node be inside the region
 
1238
     * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
 
1239
     * @return {Boolean} True if in region, false if not.
 
1240
     */
 
1241
    inViewportRegion: function(node, all, altRegion) {
 
1242
        return Y.DOM.inRegion(node, Y.DOM.viewportRegion(node), all, altRegion);
 
1243
            
 
1244
    },
 
1245
 
 
1246
    /**
 
1247
     * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
 
1248
     * @method viewportRegion
 
1249
     @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
 
1250
     */
 
1251
    viewportRegion: function(node) {
 
1252
        node = node || Y.config.doc.documentElement;
 
1253
        var r = {
 
1254
            top: Y.DOM.docScrollY(node),
 
1255
            right: Y.DOM.winWidth(node) + Y.DOM.docScrollX(node),
 
1256
            bottom: (Y.DOM.docScrollY(node) + Y.DOM.winHeight(node)),
 
1257
            left: Y.DOM.docScrollX(node)
 
1258
        };
 
1259
 
 
1260
        return r;
 
1261
    }
 
1262
});
 
1263
/**
 
1264
 * Add style management functionality to DOM.
 
1265
 * @module dom
 
1266
 * @submodule dom-style
 
1267
 * @for DOM
 
1268
 */
 
1269
 
 
1270
var CLIENT_TOP = 'clientTop',
 
1271
    CLIENT_LEFT = 'clientLeft',
 
1272
    PARENT_NODE = 'parentNode',
 
1273
    RIGHT = 'right',
 
1274
    HAS_LAYOUT = 'hasLayout',
 
1275
    PX = 'px',
 
1276
    FILTER = 'filter',
 
1277
    FILTERS = 'filters',
 
1278
    OPACITY = 'opacity',
 
1279
    AUTO = 'auto',
 
1280
    CURRENT_STYLE = 'currentStyle';
 
1281
 
 
1282
// use alpha filter for IE opacity
 
1283
if (document[DOCUMENT_ELEMENT][STYLE][OPACITY] === UNDEFINED &&
 
1284
        document[DOCUMENT_ELEMENT][FILTERS]) {
 
1285
    Y.DOM.CUSTOM_STYLES[OPACITY] = {
 
1286
        get: function(node) {
 
1287
            var val = 100;
 
1288
            try { // will error if no DXImageTransform
 
1289
                val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
 
1290
 
 
1291
            } catch(e) {
 
1292
                try { // make sure its in the document
 
1293
                    val = node[FILTERS]('alpha')[OPACITY];
 
1294
                } catch(err) {
 
1295
                }
 
1296
            }
 
1297
            return val / 100;
 
1298
        },
 
1299
 
 
1300
        set: function(node, val, style) {
 
1301
            if (typeof style[FILTER] == 'string') { // in case not appended
 
1302
                style[FILTER] = 'alpha(' + OPACITY + '=' + val * 100 + ')';
 
1303
                
 
1304
                if (!node[CURRENT_STYLE] || !node[CURRENT_STYLE][HAS_LAYOUT]) {
 
1305
                    style.zoom = 1; // needs layout 
 
1306
                }
 
1307
            }
 
1308
        }
 
1309
    };
 
1310
}
 
1311
 
 
1312
// IE getComputedStyle
 
1313
// TODO: unit-less lineHeight (e.g. 1.22)
 
1314
var re_size = /^width|height$/,
 
1315
    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;
 
1316
 
 
1317
var ComputedStyle = {
 
1318
    CUSTOM_STYLES: {},
 
1319
 
 
1320
    get: function(el, property) {
 
1321
        var value = '',
 
1322
            current = el[CURRENT_STYLE][property];
 
1323
 
 
1324
        if (property === OPACITY) {
 
1325
            value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);        
 
1326
        } else if (!current || current.indexOf(PX) > -1) { // no need to convert
 
1327
            value = current;
 
1328
        } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
 
1329
            value = Y.DOM.IE.COMPUTED[property](el, property);
 
1330
        } else if (re_unit.test(current)) { // convert to pixel
 
1331
            value = Y.DOM.IE.ComputedStyle.getPixel(el, property);
 
1332
        } else {
 
1333
            value = current;
 
1334
        }
 
1335
 
 
1336
        return value;
 
1337
    },
 
1338
 
 
1339
    getOffset: function(el, prop) {
 
1340
        var current = el[CURRENT_STYLE][prop],                        // value of "width", "top", etc.
 
1341
            capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
 
1342
            offset = 'offset' + capped,                             // "offsetWidth", "offsetTop", etc.
 
1343
            pixel = 'pixel' + capped,                               // "pixelWidth", "pixelTop", etc.
 
1344
            value = '';
 
1345
 
 
1346
        if (current == AUTO) {
 
1347
            var actual = el[offset]; // offsetHeight/Top etc.
 
1348
            if (actual === UNDEFINED) { // likely "right" or "bottom"
 
1349
                value = 0;
 
1350
            }
 
1351
 
 
1352
            value = actual;
 
1353
            if (re_size.test(prop)) { // account for box model diff 
 
1354
                el[STYLE][prop] = actual; 
 
1355
                if (el[offset] > actual) {
 
1356
                    // the difference is padding + border (works in Standards & Quirks modes)
 
1357
                    value = actual - (el[offset] - actual);
 
1358
                }
 
1359
                el[STYLE][prop] = AUTO; // revert to auto
 
1360
            }
 
1361
        } else { // convert units to px
 
1362
            if (!el[STYLE][pixel] && !el[STYLE][prop]) { // need to map style.width to currentStyle (no currentStyle.pixelWidth)
 
1363
                el[STYLE][prop] = current;              // no style.pixelWidth if no style.width
 
1364
            }
 
1365
            value = el[STYLE][pixel];
 
1366
        }
 
1367
        return value + PX;
 
1368
    },
 
1369
 
 
1370
    getBorderWidth: function(el, property) {
 
1371
        // clientHeight/Width = paddingBox (e.g. offsetWidth - borderWidth)
 
1372
        // clientTop/Left = borderWidth
 
1373
        var value = null;
 
1374
        if (!el[CURRENT_STYLE][HAS_LAYOUT]) { // TODO: unset layout?
 
1375
            el[STYLE].zoom = 1; // need layout to measure client
 
1376
        }
 
1377
 
 
1378
        switch(property) {
 
1379
            case BORDER_TOP_WIDTH:
 
1380
                value = el[CLIENT_TOP];
 
1381
                break;
 
1382
            case BORDER_BOTTOM_WIDTH:
 
1383
                value = el.offsetHeight - el.clientHeight - el[CLIENT_TOP];
 
1384
                break;
 
1385
            case BORDER_LEFT_WIDTH:
 
1386
                value = el[CLIENT_LEFT];
 
1387
                break;
 
1388
            case BORDER_RIGHT_WIDTH:
 
1389
                value = el.offsetWidth - el.clientWidth - el[CLIENT_LEFT];
 
1390
                break;
 
1391
        }
 
1392
        return value + PX;
 
1393
    },
 
1394
 
 
1395
    getPixel: function(node, att) {
 
1396
        // use pixelRight to convert to px
 
1397
        var val = null,
 
1398
            styleRight = node[CURRENT_STYLE][RIGHT],
 
1399
            current = node[CURRENT_STYLE][att];
 
1400
 
 
1401
        node[STYLE][RIGHT] = current;
 
1402
        val = node[STYLE].pixelRight;
 
1403
        node[STYLE][RIGHT] = styleRight; // revert
 
1404
 
 
1405
        return val + PX;
 
1406
    },
 
1407
 
 
1408
    getMargin: function(node, att) {
 
1409
        var val;
 
1410
        if (node[CURRENT_STYLE][att] == AUTO) {
 
1411
            val = 0 + PX;
 
1412
        } else {
 
1413
            val = Y.DOM.IE.ComputedStyle.getPixel(node, att);
 
1414
        }
 
1415
        return val;
 
1416
    },
 
1417
 
 
1418
    getVisibility: function(node, att) {
 
1419
        var current;
 
1420
        while ( (current = node[CURRENT_STYLE]) && current[att] == 'inherit') { // NOTE: assignment in test
 
1421
            node = node[PARENT_NODE];
 
1422
        }
 
1423
        return (current) ? current[att] : VISIBLE;
 
1424
    },
 
1425
 
 
1426
    getColor: function(node, att) {
 
1427
        var current = node[CURRENT_STYLE][att];
 
1428
 
 
1429
        if (!current || current === TRANSPARENT) {
 
1430
            Y.DOM.elementByAxis(node, PARENT_NODE, null, function(parent) {
 
1431
                current = parent[CURRENT_STYLE][att];
 
1432
                if (current && current !== TRANSPARENT) {
 
1433
                    node = parent;
 
1434
                    return true;
 
1435
                }
 
1436
            });
 
1437
        }
 
1438
 
 
1439
        return Y.Color.toRGB(current);
 
1440
    },
 
1441
 
 
1442
    getBorderColor: function(node, att) {
 
1443
        var current = node[CURRENT_STYLE];
 
1444
        var val = current[att] || current.color;
 
1445
        return Y.Color.toRGB(Y.Color.toHex(val));
 
1446
    }
 
1447
 
 
1448
};
 
1449
 
 
1450
//fontSize: getPixelFont,
 
1451
var IEComputed = {};
 
1452
 
 
1453
IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
 
1454
 
 
1455
IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
 
1456
 
 
1457
IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
 
1458
        IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
 
1459
        ComputedStyle.getBorderWidth;
 
1460
 
 
1461
IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
 
1462
        IEComputed.marginLeft = ComputedStyle.getMargin;
 
1463
 
 
1464
IEComputed.visibility = ComputedStyle.getVisibility;
 
1465
IEComputed.borderColor = IEComputed.borderTopColor =
 
1466
        IEComputed.borderRightColor = IEComputed.borderBottomColor =
 
1467
        IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
 
1468
 
 
1469
if (!Y.config.win[GET_COMPUTED_STYLE]) {
 
1470
    Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get; 
 
1471
}
 
1472
 
 
1473
Y.namespace('DOM.IE');
 
1474
Y.DOM.IE.COMPUTED = IEComputed;
 
1475
Y.DOM.IE.ComputedStyle = ComputedStyle;
 
1476
 
 
1477
/**
 
1478
 * Provides helper methods for collecting and filtering DOM elements.
 
1479
 * @module dom
 
1480
 * @submodule selector
 
1481
 */
 
1482
 
 
1483
/**
 
1484
 * Provides helper methods for collecting and filtering DOM elements.
 
1485
 * @class Selector
 
1486
 * @static
 
1487
 */
 
1488
 
 
1489
var TAG = 'tag',
 
1490
    PARENT_NODE = 'parentNode',
 
1491
    PREVIOUS_SIBLING = 'previousSibling',
 
1492
    LENGTH = 'length',
 
1493
    NODE_TYPE = 'nodeType',
 
1494
    TAG_NAME = 'tagName',
 
1495
    ATTRIBUTES = 'attributes',
 
1496
    PSEUDOS = 'pseudos',
 
1497
    COMBINATOR = 'combinator';
 
1498
 
 
1499
var reNth = /^(?:([\-]?\d*)(n){1}|(odd|even)$)*([\-+]?\d*)$/;
 
1500
 
 
1501
var patterns = {
 
1502
    tag: /^((?:-?[_a-z]+[\w\-]*)|\*)/i,
 
1503
    attributes: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
 
1504
    pseudos: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
 
1505
    combinator: /^\s*([>+~]|\s)\s*/
 
1506
};
 
1507
 
 
1508
var Selector = {
 
1509
    /**
 
1510
     * Default document for use queries 
 
1511
     * @property document
 
1512
     * @type object
 
1513
     * @default window.document
 
1514
     */
 
1515
    document: Y.config.doc,
 
1516
    /**
 
1517
     * Mapping of attributes to aliases, normally to work around HTMLAttributes
 
1518
     * that conflict with JS reserved words.
 
1519
     * @property attrAliases
 
1520
     * @type object
 
1521
     */
 
1522
    attrAliases: {},
 
1523
 
 
1524
    /**
 
1525
     * Mapping of shorthand tokens to corresponding attribute selector 
 
1526
     * @property shorthand
 
1527
     * @type object
 
1528
     */
 
1529
    shorthand: {
 
1530
        '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
 
1531
        '\\.(-?[_a-z]+[-\\w]*)': '[class~=$1]'
 
1532
    },
 
1533
 
 
1534
    /**
 
1535
     * List of operators and corresponding boolean functions. 
 
1536
     * These functions are passed the attribute and the current node's value of the attribute.
 
1537
     * @property operators
 
1538
     * @type object
 
1539
     */
 
1540
    operators: {
 
1541
        '=': function(attr, val) { return attr === val; }, // Equality
 
1542
        '!=': function(attr, val) { return attr !== val; }, // Inequality
 
1543
        '~=': function(attr, val) { // Match one of space seperated words 
 
1544
            var s = ' ';
 
1545
            return (s + attr + s).indexOf((s + val + s)) > -1;
 
1546
        },
 
1547
        '|=': function(attr, val) { return Y.DOM._getRegExp('^' + val + '[-]?').test(attr); }, // Match start with value followed by optional hyphen
 
1548
        '^=': function(attr, val) { return attr.indexOf(val) === 0; }, // Match starts with value
 
1549
        '$=': function(attr, val) { return attr.lastIndexOf(val) === attr[LENGTH] - val[LENGTH]; }, // Match ends with value
 
1550
        '*=': function(attr, val) { return attr.indexOf(val) > -1; }, // Match contains value as substring 
 
1551
        '': function(attr, val) { return attr; } // Just test for existence of attribute
 
1552
    },
 
1553
 
 
1554
    /**
 
1555
     * List of pseudo-classes and corresponding boolean functions. 
 
1556
     * These functions are called with the current node, and any value that was parsed with the pseudo regex.
 
1557
     * @property pseudos
 
1558
     * @type object
 
1559
     */
 
1560
    pseudos: {
 
1561
        'root': function(node) {
 
1562
            return node === node.ownerDocument.documentElement;
 
1563
        },
 
1564
 
 
1565
        'nth-child': function(node, val) {
 
1566
            return Selector.getNth(node, val);
 
1567
        },
 
1568
 
 
1569
        'nth-last-child': function(node, val) {
 
1570
            return Selector.getNth(node, val, null, true);
 
1571
        },
 
1572
 
 
1573
        'nth-of-type': function(node, val) {
 
1574
            return Selector.getNth(node, val, node[TAG_NAME]);
 
1575
        },
 
1576
         
 
1577
        'nth-last-of-type': function(node, val) {
 
1578
            return Selector.getNth(node, val, node[TAG_NAME], true);
 
1579
        },
 
1580
         
 
1581
        'first-child': function(node) {
 
1582
            return Y.DOM.firstChild(node[PARENT_NODE]) === node;
 
1583
        },
 
1584
 
 
1585
        'last-child': function(node) {
 
1586
            return Y.DOM.lastChild(node[PARENT_NODE]) === node;
 
1587
        },
 
1588
 
 
1589
        'first-of-type': function(node, val) {
 
1590
            return Y.DOM.firstChildByTag(node[PARENT_NODE], node[TAG_NAME]) === node;
 
1591
        },
 
1592
         
 
1593
        'last-of-type': function(node, val) {
 
1594
            return Y.DOM.lastChildByTag(node[PARENT_NODE], node[TAG_NAME]) === node;
 
1595
        },
 
1596
         
 
1597
        'only-child': function(node) {
 
1598
            var children = Y.DOM.children(node[PARENT_NODE]);
 
1599
            return children[LENGTH] === 1 && children[0] === node;
 
1600
        },
 
1601
 
 
1602
        'only-of-type': function(node) {
 
1603
            return Y.DOM.childrenByTag(node[PARENT_NODE], node[TAG_NAME])[LENGTH] === 1;
 
1604
        },
 
1605
 
 
1606
        'empty': function(node) {
 
1607
            return node.childNodes[LENGTH] === 0;
 
1608
        },
 
1609
 
 
1610
        'not': function(node, simple) {
 
1611
            return !Selector.test(node, simple);
 
1612
        },
 
1613
 
 
1614
        'contains': function(node, str) {
 
1615
            var text = node.innerText || node.textContent || '';
 
1616
            return text.indexOf(str) > -1;
 
1617
        },
 
1618
        'checked': function(node) {
 
1619
            return node.checked === true;
 
1620
        }
 
1621
    },
 
1622
 
 
1623
    /**
 
1624
     * Test if the supplied node matches the supplied selector.
 
1625
     * @method test
 
1626
     *
 
1627
     * @param {HTMLElement | String} node An id or node reference to the HTMLElement being tested.
 
1628
     * @param {string} selector The CSS Selector to test the node against.
 
1629
     * @return{boolean} Whether or not the node matches the selector.
 
1630
     * @static
 
1631
    
 
1632
     */
 
1633
    test: function(node, selector) {
 
1634
        if (!node) {
 
1635
            return false;
 
1636
        }
 
1637
 
 
1638
        var groups = selector ? selector.split(',') : [];
 
1639
        if (groups[LENGTH]) {
 
1640
            for (var i = 0, len = groups[LENGTH]; i < len; ++i) {
 
1641
                if ( Selector._testNode(node, groups[i]) ) { // passes if ANY group matches
 
1642
                    return true;
 
1643
                }
 
1644
            }
 
1645
            return false;
 
1646
        }
 
1647
        return Selector._testNode(node, selector);
 
1648
    },
 
1649
 
 
1650
    /**
 
1651
     * Filters a set of nodes based on a given CSS selector. 
 
1652
     * @method filter
 
1653
     *
 
1654
     * @param {array} nodes A set of nodes/ids to filter. 
 
1655
     * @param {string} selector The selector used to test each node.
 
1656
     * @return{array} An array of nodes from the supplied array that match the given selector.
 
1657
     * @static
 
1658
     */
 
1659
    filter: function(nodes, selector) {
 
1660
        nodes = nodes || [];
 
1661
 
 
1662
        var result = Selector._filter(nodes, Selector._tokenize(selector)[0]);
 
1663
        return result;
 
1664
    },
 
1665
 
 
1666
    /**
 
1667
     * Retrieves a set of nodes based on a given CSS selector. 
 
1668
     * @method query
 
1669
     *
 
1670
     * @param {string} selector The CSS Selector to test the node against.
 
1671
     * @param {HTMLElement | String} root optional An id or HTMLElement to start the query from. Defaults to Selector.document.
 
1672
     * @param {Boolean} firstOnly optional Whether or not to return only the first match.
 
1673
     * @return {Array} An array of nodes that match the given selector.
 
1674
     * @static
 
1675
     */
 
1676
    query: function(selector, root, firstOnly) {
 
1677
        var result = Selector._query(selector, root, firstOnly);
 
1678
        return result;
 
1679
    },
 
1680
 
 
1681
    _query: function(selector, root, firstOnly, deDupe) {
 
1682
        var result =  (firstOnly) ? null : [];
 
1683
        if (!selector) {
 
1684
            return result;
 
1685
        }
 
1686
 
 
1687
        root = root || Selector.document;
 
1688
        var groups = selector.split(','); // TODO: handle comma in attribute/pseudo
 
1689
 
 
1690
        if (groups[LENGTH] > 1) {
 
1691
            var found;
 
1692
            for (var i = 0, len = groups[LENGTH]; i < len; ++i) {
 
1693
                found = arguments.callee(groups[i], root, firstOnly, true);
 
1694
                result = firstOnly ? found : result.concat(found); 
 
1695
            }
 
1696
            Selector._clearFoundCache();
 
1697
            return result;
 
1698
        }
 
1699
 
 
1700
        var tokens = Selector._tokenize(selector);
 
1701
        var idToken = tokens[Selector._getIdTokenIndex(tokens)],
 
1702
            nodes = [],
 
1703
            node,
 
1704
            id,
 
1705
            token = tokens.pop() || {};
 
1706
            
 
1707
        if (idToken) {
 
1708
            id = Selector._getId(idToken[ATTRIBUTES]);
 
1709
        }
 
1710
 
 
1711
        // use id shortcut when possible
 
1712
        if (id) {
 
1713
            node = Selector.document.getElementById(id);
 
1714
 
 
1715
            if (node && (root[NODE_TYPE] === 9 || Y.DOM.contains(root, node))) {
 
1716
                if ( Selector._testNode(node, null, idToken) ) {
 
1717
                    if (idToken === token) {
 
1718
                        nodes = [node]; // simple selector
 
1719
                    } else {
 
1720
                        root = node; // start from here
 
1721
                    }
 
1722
                }
 
1723
            } else {
 
1724
                return result;
 
1725
            }
 
1726
        }
 
1727
 
 
1728
        if (root && !nodes[LENGTH]) {
 
1729
            nodes = root.getElementsByTagName(token[TAG]);
 
1730
        }
 
1731
 
 
1732
        if (nodes[LENGTH]) {
 
1733
            result = Selector._filter(nodes, token, firstOnly, deDupe); 
 
1734
        }
 
1735
        return result;
 
1736
    },
 
1737
 
 
1738
    _filter: function(nodes, token, firstOnly, deDupe) {
 
1739
        var result = firstOnly ? null : [];
 
1740
 
 
1741
        result = Y.DOM.filterElementsBy(nodes, function(node) {
 
1742
            if (! Selector._testNode(node, '', token, deDupe)) {
 
1743
                return false;
 
1744
            }
 
1745
 
 
1746
            if (deDupe) {
 
1747
                if (node._found) {
 
1748
                    return false;
 
1749
                }
 
1750
                node._found = true;
 
1751
                Selector._foundCache[Selector._foundCache[LENGTH]] = node;
 
1752
            }
 
1753
            return true;
 
1754
        }, firstOnly);
 
1755
 
 
1756
        return result;
 
1757
    },
 
1758
 
 
1759
    _testNode: function(node, selector, token, deDupe) {
 
1760
        token = token || Selector._tokenize(selector).pop() || {};
 
1761
        var ops = Selector.operators,
 
1762
            pseudos = Selector.pseudos,
 
1763
            prev = token.previous,
 
1764
            i, len;
 
1765
 
 
1766
        if (!node[TAG_NAME] ||
 
1767
            (token[TAG] !== '*' && node[TAG_NAME].toUpperCase() !== token[TAG]) ||
 
1768
            (deDupe && node._found) ) {
 
1769
            return false;
 
1770
        }
 
1771
 
 
1772
        if (token[ATTRIBUTES][LENGTH]) {
 
1773
            var attribute;
 
1774
            for (i = 0, len = token[ATTRIBUTES][LENGTH]; i < len; ++i) {
 
1775
                attribute = node.getAttribute(token[ATTRIBUTES][i][0], 2);
 
1776
                if (attribute === undefined) {
 
1777
                    attribute = node[token[ATTRIBUTES][i][0]];
 
1778
                    if (attribute === undefined) {
 
1779
                        return false;
 
1780
                    }
 
1781
                }
 
1782
                if ( ops[token[ATTRIBUTES][i][1]] &&
 
1783
                        !ops[token[ATTRIBUTES][i][1]](attribute, token[ATTRIBUTES][i][2])) {
 
1784
                    return false;
 
1785
                }
 
1786
            }
 
1787
        }
 
1788
 
 
1789
        if (token[PSEUDOS][LENGTH]) {
 
1790
            for (i = 0, len = token[PSEUDOS][LENGTH]; i < len; ++i) {
 
1791
                if (pseudos[token[PSEUDOS][i][0]] &&
 
1792
                        !pseudos[token[PSEUDOS][i][0]](node, token[PSEUDOS][i][1])) {
 
1793
                    return false;
 
1794
                }
 
1795
            }
 
1796
        }
 
1797
        return (prev && prev[COMBINATOR] !== ',') ?
 
1798
                Selector.combinators[prev[COMBINATOR]](node, token) :
 
1799
                true;
 
1800
    },
 
1801
 
 
1802
 
 
1803
    _foundCache: [],
 
1804
    _regexCache: {},
 
1805
 
 
1806
    _clearFoundCache: function() {
 
1807
        for (var i = 0, len = Selector._foundCache[LENGTH]; i < len; ++i) {
 
1808
            try { // IE no like delete
 
1809
                delete Selector._foundCache[i]._found;
 
1810
            } catch(e) {
 
1811
                Selector._foundCache[i].removeAttribute('_found');
 
1812
            }
 
1813
        }
 
1814
        Selector._foundCache = [];
 
1815
    },
 
1816
 
 
1817
    combinators: {
 
1818
        ' ': function(node, token) {
 
1819
            while ((node = node[PARENT_NODE])) {
 
1820
                if (Selector._testNode(node, '', token.previous)) {
 
1821
                    return true;
 
1822
                }
 
1823
            }  
 
1824
            return false;
 
1825
        },
 
1826
 
 
1827
        '>': function(node, token) {
 
1828
            return Selector._testNode(node[PARENT_NODE], null, token.previous);
 
1829
        },
 
1830
        '+': function(node, token) {
 
1831
            var sib = node[PREVIOUS_SIBLING];
 
1832
            while (sib && sib[NODE_TYPE] !== 1) {
 
1833
                sib = sib[PREVIOUS_SIBLING];
 
1834
            }
 
1835
 
 
1836
            if (sib && Selector._testNode(sib, null, token.previous)) {
 
1837
                return true; 
 
1838
            }
 
1839
            return false;
 
1840
        },
 
1841
 
 
1842
        '~': function(node, token) {
 
1843
            var sib = node[PREVIOUS_SIBLING];
 
1844
            while (sib) {
 
1845
                if (sib[NODE_TYPE] === 1 && Selector._testNode(sib, null, token.previous)) {
 
1846
                    return true;
 
1847
                }
 
1848
                sib = sib[PREVIOUS_SIBLING];
 
1849
            }
 
1850
 
 
1851
            return false;
 
1852
        }
 
1853
    },
 
1854
 
 
1855
 
 
1856
    /*
 
1857
        an+b = get every _a_th node starting at the _b_th
 
1858
        0n+b = no repeat ("0" and "n" may both be omitted (together) , e.g. "0n+1" or "1", not "0+1"), return only the _b_th element
 
1859
        1n+b =  get every element starting from b ("1" may may be omitted, e.g. "1n+0" or "n+0" or "n")
 
1860
        an+0 = get every _a_th element, "0" may be omitted 
 
1861
    */
 
1862
    getNth: function(node, expr, tag, reverse) {
 
1863
        reNth.test(expr);
 
1864
 
 
1865
        var a = parseInt(RegExp.$1, 10), // include every _a_ elements (zero means no repeat, just first _a_)
 
1866
            n = RegExp.$2, // "n"
 
1867
            oddeven = RegExp.$3, // "odd" or "even"
 
1868
            b = parseInt(RegExp.$4, 10) || 0, // start scan from element _b_
 
1869
            op, i, len, siblings;
 
1870
 
 
1871
        if (tag) {
 
1872
            siblings = Y.DOM.childrenByTag(node[PARENT_NODE], tag);
 
1873
        } else {
 
1874
            siblings = Y.DOM.children(node[PARENT_NODE]);
 
1875
        }
 
1876
 
 
1877
        if (oddeven) {
 
1878
            a = 2; // always every other
 
1879
            op = '+';
 
1880
            n = 'n';
 
1881
            b = (oddeven === 'odd') ? 1 : 0;
 
1882
        } else if ( isNaN(a) ) {
 
1883
            a = (n) ? 1 : 0; // start from the first or no repeat
 
1884
        }
 
1885
 
 
1886
        if (a === 0) { // just the first
 
1887
            if (reverse) {
 
1888
                b = siblings[LENGTH] - b + 1; 
 
1889
            }
 
1890
 
 
1891
            if (siblings[b - 1] === node) {
 
1892
                return true;
 
1893
            } else {
 
1894
                return false;
 
1895
            }
 
1896
 
 
1897
        } else if (a < 0) {
 
1898
            reverse = !!reverse;
 
1899
            a = Math.abs(a);
 
1900
        }
 
1901
 
 
1902
        if (!reverse) {
 
1903
            for (i = b - 1, len = siblings[LENGTH]; i < len; i += a) {
 
1904
                if ( i >= 0 && siblings[i] === node ) {
 
1905
                    return true;
 
1906
                }
 
1907
            }
 
1908
        } else {
 
1909
            for (i = siblings[LENGTH] - b, len = siblings[LENGTH]; i >= 0; i -= a) {
 
1910
                if ( i < len && siblings[i] === node ) {
 
1911
                    return true;
 
1912
                }
 
1913
            }
 
1914
        }
 
1915
        return false;
 
1916
    },
 
1917
 
 
1918
    _getId: function(attr) {
 
1919
        for (var i = 0, len = attr[LENGTH]; i < len; ++i) {
 
1920
            if (attr[i][0] == 'id' && attr[i][1] === '=') {
 
1921
                return attr[i][2];
 
1922
            }
 
1923
        }
 
1924
    },
 
1925
 
 
1926
    _getIdTokenIndex: function(tokens) {
 
1927
        for (var i = 0, len = tokens[LENGTH]; i < len; ++i) {
 
1928
            if (Selector._getId(tokens[i][ATTRIBUTES])) {
 
1929
                return i;
 
1930
            }
 
1931
        }
 
1932
        return -1;
 
1933
    },
 
1934
 
 
1935
    /**
 
1936
        Break selector into token units per simple selector.
 
1937
        Combinator is attached to left-hand selector.
 
1938
     */
 
1939
    _tokenize: function(selector) {
 
1940
        var token = {},     // one token per simple selector (left selector holds combinator)
 
1941
            tokens = [],    // array of tokens
 
1942
            found = false,  // whether or not any matches were found this pass
 
1943
            match;          // the regex match
 
1944
 
 
1945
        selector = Selector._replaceShorthand(selector); // convert ID and CLASS shortcuts to attributes
 
1946
 
 
1947
        /*
 
1948
            Search for selector patterns, store, and strip them from the selector string
 
1949
            until no patterns match (invalid selector) or we run out of chars.
 
1950
 
 
1951
            Multiple attributes and pseudos are allowed, in any order.
 
1952
            for example:
 
1953
                'form:first-child[type=button]:not(button)[lang|=en]'
 
1954
        */
 
1955
        do {
 
1956
            found = false; // reset after full pass
 
1957
            for (var re in patterns) {
 
1958
                if (patterns.hasOwnProperty(re)) {
 
1959
                    if (re != TAG && re != COMBINATOR) { // only one allowed
 
1960
                        token[re] = token[re] || [];
 
1961
                    }
 
1962
                    if ((match = patterns[re].exec(selector))) { // note assignment
 
1963
                        found = true;
 
1964
                        if (re != TAG && re != COMBINATOR) { // only one allowed
 
1965
                            //token[re] = token[re] || [];
 
1966
 
 
1967
                            // capture ID for fast path to element
 
1968
                            if (re === ATTRIBUTES && match[1] === 'id') {
 
1969
                                token.id = match[3];
 
1970
                            }
 
1971
 
 
1972
                            token[re].push(match.slice(1));
 
1973
                        } else { // single selector (tag, combinator)
 
1974
                            token[re] = match[1];
 
1975
                        }
 
1976
                        selector = selector.replace(match[0], ''); // strip current match from selector
 
1977
                        if (re === COMBINATOR || !selector[LENGTH]) { // next token or done
 
1978
                            token[ATTRIBUTES] = Selector._fixAttributes(token[ATTRIBUTES]);
 
1979
                            token[PSEUDOS] = token[PSEUDOS] || [];
 
1980
                            token[TAG] = token[TAG] ? token[TAG].toUpperCase() : '*';
 
1981
                            tokens.push(token);
 
1982
 
 
1983
                            token = { // prep next token
 
1984
                                previous: token
 
1985
                            };
 
1986
                        }
 
1987
                    }
 
1988
                }
 
1989
            }
 
1990
        } while (found);
 
1991
 
 
1992
        return tokens;
 
1993
    },
 
1994
 
 
1995
    _fixAttributes: function(attr) {
 
1996
        var aliases = Selector.attrAliases;
 
1997
        attr = attr || [];
 
1998
        for (var i = 0, len = attr[LENGTH]; i < len; ++i) {
 
1999
            if (aliases[attr[i][0]]) { // convert reserved words, etc
 
2000
                attr[i][0] = aliases[attr[i][0]];
 
2001
            }
 
2002
            if (!attr[i][1]) { // use exists operator
 
2003
                attr[i][1] = '';
 
2004
            }
 
2005
        }
 
2006
        return attr;
 
2007
    },
 
2008
 
 
2009
    _replaceShorthand: function(selector) {
 
2010
        var shorthand = Selector.shorthand;
 
2011
        var attrs = selector.match(patterns[ATTRIBUTES]); // pull attributes to avoid false pos on "." and "#"
 
2012
        if (attrs) {
 
2013
            selector = selector.replace(patterns[ATTRIBUTES], 'REPLACED_ATTRIBUTE');
 
2014
        }
 
2015
        for (var re in shorthand) {
 
2016
            if (shorthand.hasOwnProperty(re)) {
 
2017
                selector = selector.replace(Y.DOM._getRegExp(re, 'gi'), shorthand[re]);
 
2018
            }
 
2019
        }
 
2020
 
 
2021
        if (attrs) {
 
2022
            for (var i = 0, len = attrs[LENGTH]; i < len; ++i) {
 
2023
                selector = selector.replace('REPLACED_ATTRIBUTE', attrs[i]);
 
2024
            }
 
2025
        }
 
2026
        return selector;
 
2027
    }
 
2028
 
 
2029
};
 
2030
 
 
2031
if (Y.UA.ie) { // rewrite class for IE (others use getAttribute('class')
 
2032
    Selector.attrAliases['class'] = 'className';
 
2033
    Selector.attrAliases['for'] = 'htmlFor';
 
2034
}
 
2035
 
 
2036
Y.Selector = Selector;
 
2037
Y.Selector.patterns = patterns;
 
2038
 
 
2039
/**
 
2040
 * Add style management functionality to DOM.
 
2041
 * @module dom
 
2042
 * @submodule dom-style
 
2043
 * @for DOM
 
2044
 */
 
2045
 
 
2046
var TO_STRING = 'toString',
 
2047
    PARSE_INT = parseInt,
 
2048
    RE = RegExp;
 
2049
 
 
2050
Y.Color = {
 
2051
    KEYWORDS: {
 
2052
        black: '000',
 
2053
        silver: 'c0c0c0',
 
2054
        gray: '808080',
 
2055
        white: 'fff',
 
2056
        maroon: '800000',
 
2057
        red: 'f00',
 
2058
        purple: '800080',
 
2059
        fuchsia: 'f0f',
 
2060
        green: '008000',
 
2061
        lime: '0f0',
 
2062
        olive: '808000',
 
2063
        yellow: 'ff0',
 
2064
        navy: '000080',
 
2065
        blue: '00f',
 
2066
        teal: '008080',
 
2067
        aqua: '0ff'
 
2068
    },
 
2069
 
 
2070
    re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
 
2071
    re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
 
2072
    re_hex3: /([0-9A-F])/gi,
 
2073
 
 
2074
    toRGB: function(val) {
 
2075
        if (!Y.Color.re_RGB.test(val)) {
 
2076
            val = Y.Color.toHex(val);
 
2077
        }
 
2078
 
 
2079
        if(Y.Color.re_hex.exec(val)) {
 
2080
            val = 'rgb(' + [
 
2081
                PARSE_INT(RE.$1, 16),
 
2082
                PARSE_INT(RE.$2, 16),
 
2083
                PARSE_INT(RE.$3, 16)
 
2084
            ].join(', ') + ')';
 
2085
        }
 
2086
        return val;
 
2087
    },
 
2088
 
 
2089
    toHex: function(val) {
 
2090
        val = Y.Color.KEYWORDS[val] || val;
 
2091
        if (Y.Color.re_RGB.exec(val)) {
 
2092
            var r = (RE.$1.length === 1) ? '0' + RE.$1 : Number(RE.$1),
 
2093
                g = (RE.$2.length === 1) ? '0' + RE.$2 : Number(RE.$2),
 
2094
                b = (RE.$3.length === 1) ? '0' + RE.$3 : Number(RE.$3);
 
2095
 
 
2096
            val = [
 
2097
                r[TO_STRING](16),
 
2098
                g[TO_STRING](16),
 
2099
                b[TO_STRING](16)
 
2100
            ].join('');
 
2101
        }
 
2102
 
 
2103
        if (val.length < 6) {
 
2104
            val = val.replace(Y.Color.re_hex3, '$1$1');
 
2105
        }
 
2106
 
 
2107
        if (val !== 'transparent' && val.indexOf('#') < 0) {
 
2108
            val = '#' + val;
 
2109
        }
 
2110
 
 
2111
        return val.toLowerCase();
 
2112
    }
 
2113
};
 
2114
 
 
2115
 
 
2116
 
 
2117
}, '3.0.0pr1' ,{skinnable:false});