]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - jssource/src_files/include/javascript/yui3/build/dom/dom.js
Release 6.2.0beta4
[Github/sugarcrm.git] / jssource / src_files / include / javascript / yui3 / build / dom / dom.js
1 /*
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 3.0.0
6 build: 1549
7 */
8 YUI.add('dom-base', function(Y) {
9
10 (function(Y) {
11 /** 
12  * The DOM utility provides a cross-browser abtraction layer
13  * normalizing DOM tasks, and adds extra helper functionality
14  * for other common tasks. 
15  * @module dom
16  * @submodule dom-base
17  *
18  */
19
20 /**
21  * Provides DOM helper methods.
22  * @class DOM
23  *
24  */
25 var NODE_TYPE = 'nodeType',
26     OWNER_DOCUMENT = 'ownerDocument',
27     DEFAULT_VIEW = 'defaultView',
28     PARENT_WINDOW = 'parentWindow',
29     TAG_NAME = 'tagName',
30     PARENT_NODE = 'parentNode',
31     FIRST_CHILD = 'firstChild',
32     PREVIOUS_SIBLING = 'previousSibling',
33     NEXT_SIBLING = 'nextSibling',
34     CONTAINS = 'contains',
35     COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
36
37     documentElement = document.documentElement,
38
39     re_tag = /<([a-z]+)/i;
40
41 Y.DOM = {
42     /**
43      * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
44      * @method byId         
45      * @param {String} id the id attribute 
46      * @param {Object} doc optional The document to search. Defaults to current document 
47      * @return {HTMLElement | null} The HTMLElement with the id, or null if none found. 
48      */
49     byId: function(id, doc) {
50         doc = doc || Y.config.doc;
51         // TODO: IE Name
52         return doc.getElementById(id);
53     },
54
55     // @deprecated
56     children: function(node, tag) {
57         var ret = [];
58         if (node) {
59             tag = tag || '*';
60             ret = Y.Selector.query('> ' + tag, node); 
61         }
62         return ret;
63     },
64
65     // @deprecated
66     firstByTag: function(tag, root) {
67         var ret;
68         root = root || Y.config.doc;
69
70         if (tag && root.getElementsByTagName) {
71             ret = root.getElementsByTagName(tag)[0];
72         }
73
74         return ret || null;
75     },
76
77     /**
78      * Returns the text content of the HTMLElement. 
79      * @method getText         
80      * @param {HTMLElement} element The html element. 
81      * @return {String} The text content of the element (includes text of any descending elements).
82      */
83     getText: (documentElement.textContent !== undefined) ?
84         function(element) {
85             var ret = '';
86             if (element) {
87                 ret = element.textContent;
88             }
89             return ret || '';
90         } : function(element) {
91             var ret = '';
92             if (element) {
93                 ret = element.innerText;
94             }
95             return ret || '';
96         },
97
98     /**
99      * Sets the text content of the HTMLElement. 
100      * @method setText         
101      * @param {HTMLElement} element The html element. 
102      * @param {String} content The content to add. 
103      */
104     setText: (documentElement.textContent !== undefined) ?
105         function(element, content) {
106             if (element) {
107                 element.textContent = content;
108             }
109         } : function(element, content) {
110             if (element) {
111                 element.innerText = content;
112             }
113         },
114
115     /*
116      * Finds the previous sibling of the element.
117      * @method previous
118      * @deprecated Use elementByAxis
119      * @param {HTMLElement} element The html element.
120      * @param {Function} fn optional An optional boolean test to apply.
121      * The optional function is passed the current DOM node being tested as its only argument.
122      * If no function is given, the first sibling is returned.
123      * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
124      * @return {HTMLElement | null} The matching DOM node or null if none found. 
125      */
126     previous: function(element, fn, all) {
127         return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn, all);
128     },
129
130     /*
131      * Finds the next sibling of the element.
132      * @method next
133      * @deprecated Use elementByAxis
134      * @param {HTMLElement} element The html element.
135      * @param {Function} fn optional An optional boolean test to apply.
136      * The optional function is passed the current DOM node being tested as its only argument.
137      * If no function is given, the first sibling is returned.
138      * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
139      * @return {HTMLElement | null} The matching DOM node or null if none found. 
140      */
141     next: function(element, fn, all) {
142         return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn, all);
143     },
144
145     /*
146      * Finds the ancestor of the element.
147      * @method ancestor
148      * @deprecated Use elementByAxis
149      * @param {HTMLElement} element The html element.
150      * @param {Function} fn optional An optional boolean test to apply.
151      * The optional function is passed the current DOM node being tested as its only argument.
152      * If no function is given, the parentNode is returned.
153      * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
154      * @return {HTMLElement | null} The matching DOM node or null if none found. 
155      */
156      // TODO: optional stopAt node?
157     ancestor: function(element, fn, all) {
158         return Y.DOM.elementByAxis(element, PARENT_NODE, fn, all);
159     },
160
161     /**
162      * Searches the element by the given axis for the first matching element.
163      * @method elementByAxis
164      * @param {HTMLElement} element The html element.
165      * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
166      * @param {Function} fn optional An optional boolean test to apply.
167      * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
168      * The optional function is passed the current HTMLElement being tested as its only argument.
169      * If no function is given, the first element is returned.
170      * @return {HTMLElement | null} The matching element or null if none found.
171      */
172     elementByAxis: function(element, axis, fn, all) {
173         while (element && (element = element[axis])) { // NOTE: assignment
174                 if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
175                     return element;
176                 }
177         }
178         return null;
179     },
180
181     /**
182      * Determines whether or not one HTMLElement is or contains another HTMLElement.
183      * @method contains
184      * @param {HTMLElement} element The containing html element.
185      * @param {HTMLElement} needle The html element that may be contained.
186      * @return {Boolean} Whether or not the element is or contains the needle.
187      */
188     contains: function(element, needle) {
189         var ret = false;
190
191         if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
192             ret = false;
193         } else if (element[CONTAINS])  {
194             if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
195                 ret = element[CONTAINS](needle);
196             } else {
197                 ret = Y.DOM._bruteContains(element, needle); 
198             }
199         } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
200             if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) { 
201                 ret = true;
202             }
203         }
204
205         return ret;
206     },
207
208     /**
209      * Determines whether or not the HTMLElement is part of the document.
210      * @method inDoc
211      * @param {HTMLElement} element The containing html element.
212      * @param {HTMLElement} doc optional The document to check.
213      * @return {Boolean} Whether or not the element is attached to the document. 
214      */
215     inDoc: function(element, doc) {
216         doc = doc || element[OWNER_DOCUMENT];
217         var id = element.id;
218         if (!id) { // TODO: remove when done?
219             id = element.id = Y.guid();
220         }
221
222         return !! (doc.getElementById(id));
223     },
224
225     /**
226      * Creates a new dom node using the provided markup string. 
227      * @method create
228      * @param {String} html The markup used to create the element
229      * @param {HTMLDocument} doc An optional document context 
230      */
231     create: function(html, doc) {
232         if (typeof html === 'string') {
233             html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
234         }
235
236         if (!doc && Y.DOM._cloneCache[html]) {
237             return Y.DOM._cloneCache[html].cloneNode(true); // NOTE: return
238         }
239
240         doc = doc || Y.config.doc;
241         var m = re_tag.exec(html),
242             create = Y.DOM._create,
243             custom = Y.DOM.creators,
244             ret = null,
245             tag, nodes;
246
247         if (m && custom[m[1]]) {
248             if (typeof custom[m[1]] === 'function') {
249                 create = custom[m[1]];
250             } else {
251                 tag = custom[m[1]];
252             }
253         }
254
255         nodes = create(html, doc, tag).childNodes;
256
257         if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
258             ret = nodes[0].parentNode.removeChild(nodes[0]);
259         } else { // return multiple nodes as a fragment
260              ret = Y.DOM._nl2frag(nodes, doc);
261         }
262
263         if (ret) {
264             Y.DOM._cloneCache[html] = ret.cloneNode(true);
265         }
266         return ret;
267     },
268
269     _nl2frag: function(nodes, doc) {
270         var ret = null,
271             i, len;
272
273         if (nodes && (nodes.push || nodes.item) && nodes[0]) {
274             doc = doc || nodes[0].ownerDocument; 
275             ret = doc.createDocumentFragment();
276
277             if (nodes.item) { // convert live list to static array
278                 nodes = Y.Array(nodes, 0, true);
279             }
280
281             for (i = 0, len = nodes.length; i < len; i++) {
282                 ret.appendChild(nodes[i]); 
283             }
284         } // else inline with log for minification
285         return ret;
286     },
287
288
289     CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
290         'for': 'htmlFor',
291         'class': 'className'
292     } : { // w3c
293         'htmlFor': 'for',
294         'className': 'class'
295     },
296
297     /**
298      * Provides a normalized attribute interface. 
299      * @method setAttibute
300      * @param {String | HTMLElement} el The target element for the attribute.
301      * @param {String} attr The attribute to set.
302      * @param {String} val The value of the attribute.
303      */
304     setAttribute: function(el, attr, val, ieAttr) {
305         if (el && el.setAttribute) {
306             attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
307             el.setAttribute(attr, val, ieAttr);
308         }
309     },
310
311
312     /**
313      * Provides a normalized attribute interface. 
314      * @method getAttibute
315      * @param {String | HTMLElement} el The target element for the attribute.
316      * @param {String} attr The attribute to get.
317      * @return {String} The current value of the attribute. 
318      */
319     getAttribute: function(el, attr, ieAttr) {
320         ieAttr = (ieAttr !== undefined) ? ieAttr : 2;
321         var ret = '';
322         if (el && el.getAttribute) {
323             attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
324             ret = el.getAttribute(attr, ieAttr);
325
326             if (ret === null) {
327                 ret = ''; // per DOM spec
328             }
329         }
330         return ret;
331     },
332
333     isWindow: function(obj) {
334         return obj.alert && obj.document;
335     },
336
337     _fragClones: {
338         div: document.createElement('div')
339     },
340
341     _create: function(html, doc, tag) {
342         tag = tag || 'div';
343
344         var frag = Y.DOM._fragClones[tag];
345         if (frag) {
346             frag = frag.cloneNode(false);
347         } else {
348             frag = Y.DOM._fragClones[tag] = doc.createElement(tag);
349         }
350         frag.innerHTML = html;
351         return frag;
352     },
353
354     _removeChildNodes: function(node) {
355         while (node.firstChild) {
356             node.removeChild(node.firstChild);
357         }
358     },
359
360     _cloneCache: {},
361
362     /**
363      * Inserts content in a node at the given location 
364      * @method addHTML
365      * @param {HTMLElement} node The node to insert into
366      * @param {String} content The content to be inserted 
367      * @param {String} where Where to insert the content; default is after lastChild 
368      */
369     addHTML: function(node, content, where) {
370         if (typeof content === 'string') {
371             content = Y.Lang.trim(content); // match IE which trims whitespace from innerHTML
372         }
373
374         var newNode = Y.DOM._cloneCache[content],
375             nodeParent = node.parentNode;
376             
377         if (newNode) {
378             newNode = newNode.cloneNode(true);
379         } else {
380             if (content.nodeType) { // domNode
381                 newNode = content;
382             } else { // create from string and cache
383                 newNode = Y.DOM.create(content);
384             }
385         }
386
387         if (where) {
388             if (where.nodeType) { // insert regardless of relationship to node
389                 // TODO: check if node.contains(where)?
390                 where.parentNode.insertBefore(newNode, where);
391             } else {
392                 switch (where) {
393                     case 'replace':
394                         while (node.firstChild) {
395                             node.removeChild(node.firstChild);
396                         }
397                         node.appendChild(newNode);
398                         break;
399                     case 'before':
400                         nodeParent.insertBefore(newNode, node);
401                         break;
402                     case 'after':
403                         if (node.nextSibling) { // IE errors if refNode is null
404                             nodeParent.insertBefore(newNode, node.nextSibling);
405                         } else {
406                             nodeParent.appendChild(newNode);
407                         }
408                         break;
409                     default:
410                         node.appendChild(newNode);
411                 }
412             }
413         } else {
414             node.appendChild(newNode);
415         }
416
417         return newNode;
418     },
419
420     VALUE_SETTERS: {},
421
422     VALUE_GETTERS: {},
423
424     getValue: function(node) {
425         var ret = '', // TODO: return null?
426             getter;
427
428         if (node && node[TAG_NAME]) {
429             getter = Y.DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
430
431             if (getter) {
432                 ret = getter(node);
433             } else {
434                 ret = node.value;
435             }
436         }
437
438         return (typeof ret === 'string') ? ret : '';
439     },
440
441     setValue: function(node, val) {
442         var setter;
443
444         if (node && node[TAG_NAME]) {
445             setter = Y.DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
446
447             if (setter) {
448                 setter(node, val);
449             } else {
450                 node.value = val;
451             }
452         }
453     },
454
455     /**
456      * Brute force version of contains.
457      * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
458      * @method _bruteContains
459      * @private
460      * @param {HTMLElement} element The containing html element.
461      * @param {HTMLElement} needle The html element that may be contained.
462      * @return {Boolean} Whether or not the element is or contains the needle.
463      */
464     _bruteContains: function(element, needle) {
465         while (needle) {
466             if (element === needle) {
467                 return true;
468             }
469             needle = needle.parentNode;
470         }
471         return false;
472     },
473
474 // TODO: move to Lang?
475     /**
476      * Memoizes dynamic regular expressions to boost runtime performance. 
477      * @method _getRegExp
478      * @private
479      * @param {String} str The string to convert to a regular expression.
480      * @param {String} flags optional An optinal string of flags.
481      * @return {RegExp} An instance of RegExp
482      */
483     _getRegExp: function(str, flags) {
484         flags = flags || '';
485         Y.DOM._regexCache = Y.DOM._regexCache || {};
486         if (!Y.DOM._regexCache[str + flags]) {
487             Y.DOM._regexCache[str + flags] = new RegExp(str, flags);
488         }
489         return Y.DOM._regexCache[str + flags];
490     },
491
492 // TODO: make getDoc/Win true privates?
493     /**
494      * returns the appropriate document.
495      * @method _getDoc
496      * @private
497      * @param {HTMLElement} element optional Target element.
498      * @return {Object} The document for the given element or the default document. 
499      */
500     _getDoc: function(element) {
501         element = element || {};
502
503         return (element[NODE_TYPE] === 9) ? element : // element === document
504                 element[OWNER_DOCUMENT] || // element === DOM node
505                 element.document || // element === window
506                 Y.config.doc; // default
507     },
508
509     /**
510      * returns the appropriate window.
511      * @method _getWin
512      * @private
513      * @param {HTMLElement} element optional Target element.
514      * @return {Object} The window for the given element or the default window. 
515      */
516     _getWin: function(element) {
517         var doc = Y.DOM._getDoc(element);
518         return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win;
519     },
520
521     _batch: function(nodes, fn, arg1, arg2, arg3, etc) {
522         fn = (typeof name === 'string') ? Y.DOM[fn] : fn;
523         var result,
524             ret = [];
525
526         if (fn && nodes) {
527             Y.each(nodes, function(node) {
528                 if ((result = fn.call(Y.DOM, node, arg1, arg2, arg3, etc)) !== undefined) {
529                     ret[ret.length] = result;
530                 }
531             });
532         }
533
534         return ret.length ? ret : nodes;
535     },
536
537     _testElement: function(element, tag, fn) {
538         tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
539         return (element && element[TAG_NAME] &&
540                 (!tag || element[TAG_NAME].toUpperCase() === tag) &&
541                 (!fn || fn(element)));
542     },
543
544     creators: {},
545
546     _IESimpleCreate: function(html, doc) {
547         doc = doc || Y.config.doc;
548         return doc.createElement(html);
549     }
550 };
551
552
553 (function(Y) {
554     var creators = Y.DOM.creators,
555         create = Y.DOM.create,
556         re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
557
558         TABLE_OPEN = '<table>',
559         TABLE_CLOSE = '</table>';
560
561     if (Y.UA.ie) {
562         Y.mix(creators, {
563         // TODO: thead/tfoot with nested tbody
564             // IE adds TBODY when creating TABLE elements (which may share this impl)
565             tbody: function(html, doc) {
566                 var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
567                     tb = frag.children.tags('tbody')[0];
568
569                 if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
570                     tb[PARENT_NODE].removeChild(tb); // strip extraneous tbody
571                 }
572                 return frag;
573             },
574
575             script: function(html, doc) {
576                 var frag = doc.createElement('div');
577
578                 frag.innerHTML = '-' + html;
579                 frag.removeChild(frag[FIRST_CHILD]);
580                 return frag;
581             }
582
583         }, true);
584
585         Y.mix(Y.DOM.VALUE_GETTERS, {
586             button: function(node) {
587                 return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
588             }
589         });
590
591         Y.mix(Y.DOM.VALUE_SETTERS, {
592             // IE: node.value changes the button text, which should be handled via innerHTML
593             button: function(node, val) {
594                 var attr = node.attributes.value;
595                 if (!attr) {
596                     attr = node[OWNER_DOCUMENT].createAttribute('value');
597                     node.setAttributeNode(attr);
598                 }
599
600                 attr.value = val;
601             }
602         });
603     }
604
605     if (Y.UA.gecko || Y.UA.ie) {
606         Y.mix(creators, {
607             option: function(html, doc) {
608                 return create('<select>' + html + '</select>', doc);
609             },
610
611             tr: function(html, doc) {
612                 return create('<tbody>' + html + '</tbody>', doc);
613             },
614
615             td: function(html, doc) {
616                 return create('<tr>' + html + '</tr>', doc);
617             }, 
618
619             tbody: function(html, doc) {
620                 return create(TABLE_OPEN + html + TABLE_CLOSE, doc);
621             }
622         });
623
624         Y.mix(creators, {
625             legend: 'fieldset',
626             th: creators.td,
627             thead: creators.tbody,
628             tfoot: creators.tbody,
629             caption: creators.tbody,
630             colgroup: creators.tbody,
631             col: creators.tbody,
632             optgroup: creators.option
633         });
634     }
635
636     Y.mix(Y.DOM.VALUE_GETTERS, {
637         option: function(node) {
638             var attrs = node.attributes;
639             return (attrs.value && attrs.value.specified) ? node.value : node.text;
640         },
641
642         select: function(node) {
643             var val = node.value,
644                 options = node.options;
645
646             if (options && val === '') {
647                 if (node.multiple) {
648                 } else {
649                     val = Y.DOM.getValue(options[node.selectedIndex], 'value');
650                 }
651             }
652
653             return val;
654         }
655     });
656 })(Y);
657
658 })(Y);
659 var addClass, hasClass, removeClass;
660
661 Y.mix(Y.DOM, {
662     /**
663      * Determines whether a DOM element has the given className.
664      * @method hasClass
665      * @param {HTMLElement} element The DOM element. 
666      * @param {String} className the class name to search for
667      * @return {Boolean} Whether or not the element has the given class. 
668      */
669     hasClass: function(node, className) {
670         var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
671         return re.test(node.className);
672     },
673
674     /**
675      * Adds a class name to a given DOM element.
676      * @method addClass         
677      * @param {HTMLElement} element The DOM element. 
678      * @param {String} className the class name to add to the class attribute
679      */
680     addClass: function(node, className) {
681         if (!Y.DOM.hasClass(node, className)) { // skip if already present 
682             node.className = Y.Lang.trim([node.className, className].join(' '));
683         }
684     },
685
686     /**
687      * Removes a class name from a given element.
688      * @method removeClass         
689      * @param {HTMLElement} element The DOM element. 
690      * @param {String} className the class name to remove from the class attribute
691      */
692     removeClass: function(node, className) {
693         if (className && hasClass(node, className)) {
694             node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
695                             className + '(?:\\s+|$)'), ' '));
696
697             if ( hasClass(node, className) ) { // in case of multiple adjacent
698                 removeClass(node, className);
699             }
700         }                 
701     },
702
703     /**
704      * Replace a class with another class for a given element.
705      * If no oldClassName is present, the newClassName is simply added.
706      * @method replaceClass  
707      * @param {HTMLElement} element The DOM element. 
708      * @param {String} oldClassName the class name to be replaced
709      * @param {String} newClassName the class name that will be replacing the old class name
710      */
711     replaceClass: function(node, oldC, newC) {
712         addClass(node, newC);
713         removeClass(node, oldC);
714     },
715
716     /**
717      * If the className exists on the node it is removed, if it doesn't exist it is added.
718      * @method toggleClass  
719      * @param {HTMLElement} element The DOM element. 
720      * @param {String} className the class name to be toggled
721      */
722     toggleClass: function(node, className) {
723         if (hasClass(node, className)) {
724             removeClass(node, className);
725         } else {
726             addClass(node, className);
727         }
728     }
729 });
730
731 hasClass = Y.DOM.hasClass;
732 removeClass = Y.DOM.removeClass;
733 addClass = Y.DOM.addClass;
734
735
736
737 }, '3.0.0' ,{requires:['oop']});
738 YUI.add('dom-style', function(Y) {
739
740 (function(Y) {
741 /** 
742  * Add style management functionality to DOM.
743  * @module dom
744  * @submodule dom-style
745  * @for DOM
746  */
747
748 var DOCUMENT_ELEMENT = 'documentElement',
749     DEFAULT_VIEW = 'defaultView',
750     OWNER_DOCUMENT = 'ownerDocument',
751     STYLE = 'style',
752     FLOAT = 'float',
753     CSS_FLOAT = 'cssFloat',
754     STYLE_FLOAT = 'styleFloat',
755     TRANSPARENT = 'transparent',
756     GET_COMPUTED_STYLE = 'getComputedStyle',
757
758     DOCUMENT = Y.config.doc,
759     UNDEFINED = undefined,
760
761     re_color = /color$/i;
762
763
764 Y.mix(Y.DOM, {
765     CUSTOM_STYLES: {
766     },
767
768
769     /**
770      * Sets a style property for a given element.
771      * @method setStyle
772      * @param {HTMLElement} An HTMLElement to apply the style to.
773      * @param {String} att The style property to set. 
774      * @param {String|Number} val The value. 
775      */
776     setStyle: function(node, att, val, style) {
777         style = style || node.style;
778         var CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES;
779
780         if (style) {
781             if (val === null) {
782                 val = ''; // normalize for unsetting
783             }
784             if (att in CUSTOM_STYLES) {
785                 if (CUSTOM_STYLES[att].set) {
786                     CUSTOM_STYLES[att].set(node, val, style);
787                     return; // NOTE: return
788                 } else if (typeof CUSTOM_STYLES[att] === 'string') {
789                     att = CUSTOM_STYLES[att];
790                 }
791             }
792             style[att] = val; 
793         }
794     },
795
796     /**
797      * Returns the current style value for the given property.
798      * @method getStyle
799      * @param {HTMLElement} An HTMLElement to get the style from.
800      * @param {String} att The style property to get. 
801      */
802     getStyle: function(node, att) {
803         var style = node[STYLE],
804             CUSTOM_STYLES = Y.DOM.CUSTOM_STYLES,
805             val = '';
806
807         if (style) {
808             if (att in CUSTOM_STYLES) {
809                 if (CUSTOM_STYLES[att].get) {
810                     return CUSTOM_STYLES[att].get(node, att, style); // NOTE: return
811                 } else if (typeof CUSTOM_STYLES[att] === 'string') {
812                     att = CUSTOM_STYLES[att];
813                 }
814             }
815             val = style[att];
816             if (val === '') { // TODO: is empty string sufficient?
817                 val = Y.DOM[GET_COMPUTED_STYLE](node, att);
818             }
819         }
820
821         return val;
822     },
823
824     /**
825      * Sets multiple style properties.
826      * @method setStyles
827      * @param {HTMLElement} node An HTMLElement to apply the styles to. 
828      * @param {Object} hash An object literal of property:value pairs. 
829      */
830     setStyles: function(node, hash) {
831         var style = node.style;
832         Y.each(hash, function(v, n) {
833             Y.DOM.setStyle(node, n, v, style);
834         }, Y.DOM);
835     },
836
837     /**
838      * Returns the computed style for the given node.
839      * @method getComputedStyle
840      * @param {HTMLElement} An HTMLElement to get the style from.
841      * @param {String} att The style property to get. 
842      * @return {String} The computed value of the style property. 
843      */
844     getComputedStyle: function(node, att) {
845         var val = '',
846             doc = node[OWNER_DOCUMENT];
847
848         if (node[STYLE]) {
849             val = doc[DEFAULT_VIEW][GET_COMPUTED_STYLE](node, null)[att];
850         }
851         return val;
852     }
853 });
854
855 // normalize reserved word float alternatives ("cssFloat" or "styleFloat")
856 if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][CSS_FLOAT] !== UNDEFINED) {
857     Y.DOM.CUSTOM_STYLES[FLOAT] = CSS_FLOAT;
858 } else if (DOCUMENT[DOCUMENT_ELEMENT][STYLE][STYLE_FLOAT] !== UNDEFINED) {
859     Y.DOM.CUSTOM_STYLES[FLOAT] = STYLE_FLOAT;
860 }
861
862 // fix opera computedStyle default color unit (convert to rgb)
863 if (Y.UA.opera) {
864     Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
865         var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
866             val = view[GET_COMPUTED_STYLE](node, '')[att];
867
868         if (re_color.test(att)) {
869             val = Y.Color.toRGB(val);
870         }
871
872         return val;
873     };
874
875 }
876
877 // safari converts transparent to rgba(), others use "transparent"
878 if (Y.UA.webkit) {
879     Y.DOM[GET_COMPUTED_STYLE] = function(node, att) {
880         var view = node[OWNER_DOCUMENT][DEFAULT_VIEW],
881             val = view[GET_COMPUTED_STYLE](node, '')[att];
882
883         if (val === 'rgba(0, 0, 0, 0)') {
884             val = TRANSPARENT; 
885         }
886
887         return val;
888     };
889
890 }
891 })(Y);
892 (function(Y) {
893 var PARSE_INT = parseInt,
894     RE = RegExp;
895
896 Y.Color = {
897     KEYWORDS: {
898         black: '000',
899         silver: 'c0c0c0',
900         gray: '808080',
901         white: 'fff',
902         maroon: '800000',
903         red: 'f00',
904         purple: '800080',
905         fuchsia: 'f0f',
906         green: '008000',
907         lime: '0f0',
908         olive: '808000',
909         yellow: 'ff0',
910         navy: '000080',
911         blue: '00f',
912         teal: '008080',
913         aqua: '0ff'
914     },
915
916     re_RGB: /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i,
917     re_hex: /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i,
918     re_hex3: /([0-9A-F])/gi,
919
920     toRGB: function(val) {
921         if (!Y.Color.re_RGB.test(val)) {
922             val = Y.Color.toHex(val);
923         }
924
925         if(Y.Color.re_hex.exec(val)) {
926             val = 'rgb(' + [
927                 PARSE_INT(RE.$1, 16),
928                 PARSE_INT(RE.$2, 16),
929                 PARSE_INT(RE.$3, 16)
930             ].join(', ') + ')';
931         }
932         return val;
933     },
934
935     toHex: function(val) {
936         val = Y.Color.KEYWORDS[val] || val;
937         if (Y.Color.re_RGB.exec(val)) {
938             val = [
939                 Number(RE.$1).toString(16),
940                 Number(RE.$2).toString(16),
941                 Number(RE.$3).toString(16)
942             ];
943
944             for (var i = 0; i < val.length; i++) {
945                 if (val[i].length < 2) {
946                     val[i] = val[i].replace(Y.Color.re_hex3, '$1$1');
947                 }
948             }
949
950             val = '#' + val.join('');
951         }
952
953         if (val.length < 6) {
954             val = val.replace(Y.Color.re_hex3, '$1$1');
955         }
956
957         if (val !== 'transparent' && val.indexOf('#') < 0) {
958             val = '#' + val;
959         }
960
961         return val.toLowerCase();
962     }
963 };
964 })(Y);
965
966 (function(Y) {
967 var HAS_LAYOUT = 'hasLayout',
968     PX = 'px',
969     FILTER = 'filter',
970     FILTERS = 'filters',
971     OPACITY = 'opacity',
972     AUTO = 'auto',
973
974     BORDER_WIDTH = 'borderWidth',
975     BORDER_TOP_WIDTH = 'borderTopWidth',
976     BORDER_RIGHT_WIDTH = 'borderRightWidth',
977     BORDER_BOTTOM_WIDTH = 'borderBottomWidth',
978     BORDER_LEFT_WIDTH = 'borderLeftWidth',
979     WIDTH = 'width',
980     HEIGHT = 'height',
981     TRANSPARENT = 'transparent',
982     VISIBLE = 'visible',
983     GET_COMPUTED_STYLE = 'getComputedStyle',
984     UNDEFINED = undefined,
985     documentElement = document.documentElement,
986
987     // TODO: unit-less lineHeight (e.g. 1.22)
988     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,
989
990     _getStyleObj = function(node) {
991         return node.currentStyle || node.style;
992     },
993
994     ComputedStyle = {
995         CUSTOM_STYLES: {},
996
997         get: function(el, property) {
998             var value = '',
999                 current;
1000
1001             if (el) {
1002                     current = _getStyleObj(el)[property];
1003
1004                 if (property === OPACITY && Y.DOM.CUSTOM_STYLES[OPACITY]) {
1005                     value = Y.DOM.CUSTOM_STYLES[OPACITY].get(el);        
1006                 } else if (!current || (current.indexOf && current.indexOf(PX) > -1)) { // no need to convert
1007                     value = current;
1008                 } else if (Y.DOM.IE.COMPUTED[property]) { // use compute function
1009                     value = Y.DOM.IE.COMPUTED[property](el, property);
1010                 } else if (re_unit.test(current)) { // convert to pixel
1011                     value = ComputedStyle.getPixel(el, property) + PX;
1012                 } else {
1013                     value = current;
1014                 }
1015             }
1016
1017             return value;
1018         },
1019
1020         sizeOffsets: {
1021             width: ['Left', 'Right'],
1022             height: ['Top', 'Bottom'],
1023             top: ['Top'],
1024             bottom: ['Bottom']
1025         },
1026
1027         getOffset: function(el, prop) {
1028             var current = _getStyleObj(el)[prop],                     // value of "width", "top", etc.
1029                 capped = prop.charAt(0).toUpperCase() + prop.substr(1), // "Width", "Top", etc.
1030                 offset = 'offset' + capped,                             // "offsetWidth", "offsetTop", etc.
1031                 pixel = 'pixel' + capped,                               // "pixelWidth", "pixelTop", etc.
1032                 sizeOffsets = ComputedStyle.sizeOffsets[prop], 
1033                 value = '';
1034
1035             // IE pixelWidth incorrect for percent
1036             // manually compute by subtracting padding and border from offset size
1037             // NOTE: clientWidth/Height (size minus border) is 0 when current === AUTO so offsetHeight is used
1038             // reverting to auto from auto causes position stacking issues (old impl)
1039             if (current === AUTO || current.indexOf('%') > -1) {
1040                 value = el['offset' + capped];
1041
1042                 if (sizeOffsets[0]) {
1043                     value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[0]);
1044                     value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[0] + 'Width', 1);
1045                 }
1046
1047                 if (sizeOffsets[1]) {
1048                     value -= ComputedStyle.getPixel(el, 'padding' + sizeOffsets[1]);
1049                     value -= ComputedStyle.getBorderWidth(el, 'border' + sizeOffsets[1] + 'Width', 1);
1050                 }
1051
1052             } else { // use style.pixelWidth, etc. to convert to pixels
1053                 // need to map style.width to currentStyle (no currentStyle.pixelWidth)
1054                 if (!el.style[pixel] && !el.style[prop]) {
1055                     el.style[prop] = current;
1056                 }
1057                 value = el.style[pixel];
1058                 
1059             }
1060             return value + PX;
1061         },
1062
1063         borderMap: {
1064             thin: '2px', 
1065             medium: '4px', 
1066             thick: '6px'
1067         },
1068
1069         getBorderWidth: function(el, property, omitUnit) {
1070             var unit = omitUnit ? '' : PX,
1071                 current = el.currentStyle[property];
1072
1073             if (current.indexOf(PX) < 0) { // look up keywords
1074                 if (ComputedStyle.borderMap[current]) {
1075                     current = ComputedStyle.borderMap[current];
1076                 } else {
1077                 }
1078             }
1079             return (omitUnit) ? parseFloat(current) : current;
1080         },
1081
1082         getPixel: function(node, att) {
1083             // use pixelRight to convert to px
1084             var val = null,
1085                 style = _getStyleObj(node),
1086                 styleRight = style.right,
1087                 current = style[att];
1088
1089             node.style.right = current;
1090             val = node.style.pixelRight;
1091             node.style.right = styleRight; // revert
1092
1093             return val;
1094         },
1095
1096         getMargin: function(node, att) {
1097             var val,
1098                 style = _getStyleObj(node);
1099
1100             if (style[att] == AUTO) {
1101                 val = 0;
1102             } else {
1103                 val = ComputedStyle.getPixel(node, att);
1104             }
1105             return val + PX;
1106         },
1107
1108         getVisibility: function(node, att) {
1109             var current;
1110             while ( (current = node.currentStyle) && current[att] == 'inherit') { // NOTE: assignment in test
1111                 node = node.parentNode;
1112             }
1113             return (current) ? current[att] : VISIBLE;
1114         },
1115
1116         getColor: function(node, att) {
1117             var current = _getStyleObj(node)[att];
1118
1119             if (!current || current === TRANSPARENT) {
1120                 Y.DOM.elementByAxis(node, 'parentNode', null, function(parent) {
1121                     current = _getStyleObj(parent)[att];
1122                     if (current && current !== TRANSPARENT) {
1123                         node = parent;
1124                         return true;
1125                     }
1126                 });
1127             }
1128
1129             return Y.Color.toRGB(current);
1130         },
1131
1132         getBorderColor: function(node, att) {
1133             var current = _getStyleObj(node),
1134                 val = current[att] || current.color;
1135             return Y.Color.toRGB(Y.Color.toHex(val));
1136         }
1137     },
1138
1139     //fontSize: getPixelFont,
1140     IEComputed = {};
1141
1142 // use alpha filter for IE opacity
1143 try {
1144     if (documentElement.style[OPACITY] === UNDEFINED &&
1145             documentElement[FILTERS]) {
1146         Y.DOM.CUSTOM_STYLES[OPACITY] = {
1147             get: function(node) {
1148                 var val = 100;
1149                 try { // will error if no DXImageTransform
1150                     val = node[FILTERS]['DXImageTransform.Microsoft.Alpha'][OPACITY];
1151
1152                 } catch(e) {
1153                     try { // make sure its in the document
1154                         val = node[FILTERS]('alpha')[OPACITY];
1155                     } catch(err) {
1156                     }
1157                 }
1158                 return val / 100;
1159             },
1160
1161             set: function(node, val, style) {
1162                 var current,
1163                     styleObj;
1164
1165                 if (val === '') { // normalize inline style behavior
1166                     styleObj = _getStyleObj(node);
1167                     current = (OPACITY in styleObj) ? styleObj[OPACITY] : 1; // revert to original opacity
1168                     val = current;
1169                 }
1170
1171                 if (typeof style[FILTER] == 'string') { // in case not appended
1172                     style[FILTER] = 'alpha(' + OPACITY + '=' + val * 100 + ')';
1173                     
1174                     if (!node.currentStyle || !node.currentStyle[HAS_LAYOUT]) {
1175                         style.zoom = 1; // needs layout 
1176                     }
1177                 }
1178             }
1179         };
1180     }
1181 } catch(e) {
1182 }
1183
1184 try {
1185     document.createElement('div').style.height = '-1px';
1186 } catch(e) { // IE throws error on invalid style set; trap common cases
1187     Y.DOM.CUSTOM_STYLES.height = {
1188         set: function(node, val, style) {
1189             var floatVal = parseFloat(val);
1190             if (isNaN(floatVal) || floatVal >= 0) {
1191                 style.height = val;
1192             } else {
1193             }
1194         }
1195     };
1196
1197     Y.DOM.CUSTOM_STYLES.width = {
1198         set: function(node, val, style) {
1199             var floatVal = parseFloat(val);
1200             if (isNaN(floatVal) || floatVal >= 0) {
1201                 style.width = val;
1202             } else {
1203             }
1204         }
1205     };
1206 }
1207
1208 // TODO: top, right, bottom, left
1209 IEComputed[WIDTH] = IEComputed[HEIGHT] = ComputedStyle.getOffset;
1210
1211 IEComputed.color = IEComputed.backgroundColor = ComputedStyle.getColor;
1212
1213 IEComputed[BORDER_WIDTH] = IEComputed[BORDER_TOP_WIDTH] = IEComputed[BORDER_RIGHT_WIDTH] =
1214         IEComputed[BORDER_BOTTOM_WIDTH] = IEComputed[BORDER_LEFT_WIDTH] =
1215         ComputedStyle.getBorderWidth;
1216
1217 IEComputed.marginTop = IEComputed.marginRight = IEComputed.marginBottom =
1218         IEComputed.marginLeft = ComputedStyle.getMargin;
1219
1220 IEComputed.visibility = ComputedStyle.getVisibility;
1221 IEComputed.borderColor = IEComputed.borderTopColor =
1222         IEComputed.borderRightColor = IEComputed.borderBottomColor =
1223         IEComputed.borderLeftColor = ComputedStyle.getBorderColor;
1224
1225 if (!Y.config.win[GET_COMPUTED_STYLE]) {
1226     Y.DOM[GET_COMPUTED_STYLE] = ComputedStyle.get; 
1227 }
1228
1229 Y.namespace('DOM.IE');
1230 Y.DOM.IE.COMPUTED = IEComputed;
1231 Y.DOM.IE.ComputedStyle = ComputedStyle;
1232
1233 })(Y);
1234
1235
1236 }, '3.0.0' ,{requires:['dom-base']});
1237 YUI.add('dom-screen', function(Y) {
1238
1239 (function(Y) {
1240
1241 /**
1242  * Adds position and region management functionality to DOM.
1243  * @module dom
1244  * @submodule dom-screen
1245  * @for DOM
1246  */
1247
1248 var DOCUMENT_ELEMENT = 'documentElement',
1249     COMPAT_MODE = 'compatMode',
1250     POSITION = 'position',
1251     FIXED = 'fixed',
1252     RELATIVE = 'relative',
1253     LEFT = 'left',
1254     TOP = 'top',
1255     _BACK_COMPAT = 'BackCompat',
1256     MEDIUM = 'medium',
1257     BORDER_LEFT_WIDTH = 'borderLeftWidth',
1258     BORDER_TOP_WIDTH = 'borderTopWidth',
1259     GET_BOUNDING_CLIENT_RECT = 'getBoundingClientRect',
1260     GET_COMPUTED_STYLE = 'getComputedStyle',
1261
1262     // TODO: how about thead/tbody/tfoot/tr?
1263     // TODO: does caption matter?
1264     RE_TABLE = /^t(?:able|d|h)$/i;
1265
1266 Y.mix(Y.DOM, {
1267     /**
1268      * Returns the inner height of the viewport (exludes scrollbar). 
1269      * @method winHeight
1270      * @return {Number} The current height of the viewport.
1271      */
1272     winHeight: function(node) {
1273         var h = Y.DOM._getWinSize(node).height;
1274         return h;
1275     },
1276
1277     /**
1278      * Returns the inner width of the viewport (exludes scrollbar). 
1279      * @method winWidth
1280      * @return {Number} The current width of the viewport.
1281      */
1282     winWidth: function(node) {
1283         var w = Y.DOM._getWinSize(node).width;
1284         return w;
1285     },
1286
1287     /**
1288      * Document height 
1289      * @method docHeight
1290      * @return {Number} The current height of the document.
1291      */
1292     docHeight:  function(node) {
1293         var h = Y.DOM._getDocSize(node).height;
1294         return Math.max(h, Y.DOM._getWinSize(node).height);
1295     },
1296
1297     /**
1298      * Document width 
1299      * @method docWidth
1300      * @return {Number} The current width of the document.
1301      */
1302     docWidth:  function(node) {
1303         var w = Y.DOM._getDocSize(node).width;
1304         return Math.max(w, Y.DOM._getWinSize(node).width);
1305     },
1306
1307     /**
1308      * Amount page has been scroll horizontally 
1309      * @method docScrollX
1310      * @return {Number} The current amount the screen is scrolled horizontally.
1311      */
1312     docScrollX: function(node) {
1313         var doc = Y.DOM._getDoc(node);
1314         return Math.max(doc[DOCUMENT_ELEMENT].scrollLeft, doc.body.scrollLeft);
1315     },
1316
1317     /**
1318      * Amount page has been scroll vertically 
1319      * @method docScrollY
1320      * @return {Number} The current amount the screen is scrolled vertically.
1321      */
1322     docScrollY:  function(node) {
1323         var doc = Y.DOM._getDoc(node);
1324         return Math.max(doc[DOCUMENT_ELEMENT].scrollTop, doc.body.scrollTop);
1325     },
1326
1327     /**
1328      * Gets the current position of an element based on page coordinates. 
1329      * Element must be part of the DOM tree to have page coordinates
1330      * (display:none or elements not appended return false).
1331      * @method getXY
1332      * @param element The target element
1333      * @return {Array} The XY position of the element
1334
1335      TODO: test inDocument/display?
1336      */
1337     getXY: function() {
1338         if (document[DOCUMENT_ELEMENT][GET_BOUNDING_CLIENT_RECT]) {
1339             return function(node) {
1340                 var xy = null,
1341                     scrollLeft,
1342                     scrollTop,
1343                     box,
1344                     off1, off2,
1345                     bLeft, bTop,
1346                     mode,
1347                     doc;
1348
1349                 if (node) {
1350                     if (Y.DOM.inDoc(node)) {
1351                         scrollLeft = Y.DOM.docScrollX(node);
1352                         scrollTop = Y.DOM.docScrollY(node);
1353                         box = node[GET_BOUNDING_CLIENT_RECT]();
1354                         doc = Y.DOM._getDoc(node);
1355                         xy = [box.left, box.top];
1356
1357                             if (Y.UA.ie) {
1358                                 off1 = 2;
1359                                 off2 = 2;
1360                                 mode = doc[COMPAT_MODE];
1361                                 bLeft = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_LEFT_WIDTH);
1362                                 bTop = Y.DOM[GET_COMPUTED_STYLE](doc[DOCUMENT_ELEMENT], BORDER_TOP_WIDTH);
1363
1364                                 if (Y.UA.ie === 6) {
1365                                     if (mode !== _BACK_COMPAT) {
1366                                         off1 = 0;
1367                                         off2 = 0;
1368                                     }
1369                                 }
1370                                 
1371                                 if ((mode == _BACK_COMPAT)) {
1372                                     if (bLeft !== MEDIUM) {
1373                                         off1 = parseInt(bLeft, 10);
1374                                     }
1375                                     if (bTop !== MEDIUM) {
1376                                         off2 = parseInt(bTop, 10);
1377                                     }
1378                                 }
1379                                 
1380                                 xy[0] -= off1;
1381                                 xy[1] -= off2;
1382
1383                             }
1384
1385                         if ((scrollTop || scrollLeft)) {
1386                             xy[0] += scrollLeft;
1387                             xy[1] += scrollTop;
1388                         }
1389                     } else { // default to current offsets
1390                         xy = Y.DOM._getOffset(node);
1391                     }
1392                 }
1393                 return xy;                   
1394             };
1395         } else {
1396             return function(node) { // manually calculate by crawling up offsetParents
1397                 //Calculate the Top and Left border sizes (assumes pixels)
1398                 var xy = null,
1399                     parentNode,
1400                     bCheck,
1401                     scrollTop,
1402                     scrollLeft;
1403
1404                 if (node) {
1405                     if (Y.DOM.inDoc(node)) {
1406                         xy = [node.offsetLeft, node.offsetTop];
1407                         parentNode = node;
1408                         // TODO: refactor with !! or just falsey
1409                         bCheck = ((Y.UA.gecko || Y.UA.webkit > 519) ? true : false);
1410
1411                         // TODO: worth refactoring for TOP/LEFT only?
1412                         while ((parentNode = parentNode.offsetParent)) {
1413                             xy[0] += parentNode.offsetLeft;
1414                             xy[1] += parentNode.offsetTop;
1415                             if (bCheck) {
1416                                 xy = Y.DOM._calcBorders(parentNode, xy);
1417                             }
1418                         }
1419
1420                         // account for any scrolled ancestors
1421                         if (Y.DOM.getStyle(node, POSITION) != FIXED) {
1422                             parentNode = node;
1423
1424                             while ((parentNode = parentNode.parentNode)) {
1425                                 scrollTop = parentNode.scrollTop;
1426                                 scrollLeft = parentNode.scrollLeft;
1427
1428                                 //Firefox does something funky with borders when overflow is not visible.
1429                                 if (Y.UA.gecko && (Y.DOM.getStyle(parentNode, 'overflow') !== 'visible')) {
1430                                         xy = Y.DOM._calcBorders(parentNode, xy);
1431                                 }
1432                                 
1433
1434                                 if (scrollTop || scrollLeft) {
1435                                     xy[0] -= scrollLeft;
1436                                     xy[1] -= scrollTop;
1437                                 }
1438                             }
1439                             xy[0] += Y.DOM.docScrollX(node);
1440                             xy[1] += Y.DOM.docScrollY(node);
1441
1442                         } else {
1443                             //Fix FIXED position -- add scrollbars
1444                             xy[0] += Y.DOM.docScrollX(node);
1445                             xy[1] += Y.DOM.docScrollY(node);
1446                         }
1447                     } else {
1448                         xy = Y.DOM._getOffset(node);
1449                     }
1450                 }
1451
1452                 return xy;                
1453             };
1454         }
1455     }(),// NOTE: Executing for loadtime branching
1456
1457     _getOffset: function(node) {
1458         var pos,
1459             xy = null;
1460
1461         if (node) {
1462             pos = Y.DOM.getStyle(node, POSITION);
1463             xy = [
1464                 parseInt(Y.DOM[GET_COMPUTED_STYLE](node, LEFT), 10),
1465                 parseInt(Y.DOM[GET_COMPUTED_STYLE](node, TOP), 10)
1466             ];
1467
1468             if ( isNaN(xy[0]) ) { // in case of 'auto'
1469                 xy[0] = parseInt(Y.DOM.getStyle(node, LEFT), 10); // try inline
1470                 if ( isNaN(xy[0]) ) { // default to offset value
1471                     xy[0] = (pos === RELATIVE) ? 0 : node.offsetLeft || 0;
1472                 }
1473             } 
1474
1475             if ( isNaN(xy[1]) ) { // in case of 'auto'
1476                 xy[1] = parseInt(Y.DOM.getStyle(node, TOP), 10); // try inline
1477                 if ( isNaN(xy[1]) ) { // default to offset value
1478                     xy[1] = (pos === RELATIVE) ? 0 : node.offsetTop || 0;
1479                 }
1480             } 
1481         }
1482
1483         return xy;
1484
1485     },
1486
1487     /**
1488      * Gets the current X position of an element based on page coordinates. 
1489      * Element must be part of the DOM tree to have page coordinates
1490      * (display:none or elements not appended return false).
1491      * @method getX
1492      * @param element The target element
1493      * @return {Int} The X position of the element
1494      */
1495
1496     getX: function(node) {
1497         return Y.DOM.getXY(node)[0];
1498     },
1499
1500     /**
1501      * Gets the current Y position of an element based on page coordinates. 
1502      * Element must be part of the DOM tree to have page coordinates
1503      * (display:none or elements not appended return false).
1504      * @method getY
1505      * @param element The target element
1506      * @return {Int} The Y position of the element
1507      */
1508
1509     getY: function(node) {
1510         return Y.DOM.getXY(node)[1];
1511     },
1512
1513     /**
1514      * Set the position of an html element in page coordinates.
1515      * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
1516      * @method setXY
1517      * @param element The target element
1518      * @param {Array} xy Contains X & Y values for new position (coordinates are page-based)
1519      * @param {Boolean} noRetry By default we try and set the position a second time if the first fails
1520      */
1521     setXY: function(node, xy, noRetry) {
1522         var setStyle = Y.DOM.setStyle,
1523             pos,
1524             delta,
1525             newXY,
1526             currentXY;
1527
1528         if (node && xy) {
1529             pos = Y.DOM.getStyle(node, POSITION);
1530
1531             delta = Y.DOM._getOffset(node);       
1532
1533             if (pos == 'static') { // default to relative
1534                 pos = RELATIVE;
1535                 setStyle(node, POSITION, pos);
1536             }
1537
1538             currentXY = Y.DOM.getXY(node);
1539
1540             if (xy[0] !== null) {
1541                 setStyle(node, LEFT, xy[0] - currentXY[0] + delta[0] + 'px');
1542             }
1543
1544             if (xy[1] !== null) {
1545                 setStyle(node, TOP, xy[1] - currentXY[1] + delta[1] + 'px');
1546             }
1547
1548             if (!noRetry) {
1549                 newXY = Y.DOM.getXY(node);
1550                 if (newXY[0] !== xy[0] || newXY[1] !== xy[1]) {
1551                     Y.DOM.setXY(node, xy, true); 
1552                 }
1553             }
1554           
1555         } else {
1556         }
1557     },
1558
1559     /**
1560      * Set the X position of an html element in page coordinates, regardless of how the element is positioned.
1561      * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
1562      * @method setX
1563      * @param element The target element
1564      * @param {Int} x The X values for new position (coordinates are page-based)
1565      */
1566     setX: function(node, x) {
1567         return Y.DOM.setXY(node, [x, null]);
1568     },
1569
1570     /**
1571      * Set the Y position of an html element in page coordinates, regardless of how the element is positioned.
1572      * The element(s) must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
1573      * @method setY
1574      * @param element The target element
1575      * @param {Int} y The Y values for new position (coordinates are page-based)
1576      */
1577     setY: function(node, y) {
1578         return Y.DOM.setXY(node, [null, y]);
1579     },
1580
1581     _calcBorders: function(node, xy2) {
1582         var t = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_TOP_WIDTH), 10) || 0,
1583             l = parseInt(Y.DOM[GET_COMPUTED_STYLE](node, BORDER_LEFT_WIDTH), 10) || 0;
1584         if (Y.UA.gecko) {
1585             if (RE_TABLE.test(node.tagName)) {
1586                 t = 0;
1587                 l = 0;
1588             }
1589         }
1590         xy2[0] += l;
1591         xy2[1] += t;
1592         return xy2;
1593     },
1594
1595     _getWinSize: function(node) {
1596         var doc = Y.DOM._getDoc(),
1597             win = doc.defaultView || doc.parentWindow,
1598             mode = doc[COMPAT_MODE],
1599             h = win.innerHeight,
1600             w = win.innerWidth,
1601             root = doc[DOCUMENT_ELEMENT];
1602
1603         if ( mode && !Y.UA.opera ) { // IE, Gecko
1604             if (mode != 'CSS1Compat') { // Quirks
1605                 root = doc.body; 
1606             }
1607             h = root.clientHeight;
1608             w = root.clientWidth;
1609         }
1610         return { height: h, width: w }; 
1611     },
1612
1613     _getDocSize: function(node) {
1614         var doc = Y.DOM._getDoc(),
1615             root = doc[DOCUMENT_ELEMENT];
1616
1617         if (doc[COMPAT_MODE] != 'CSS1Compat') {
1618             root = doc.body;
1619         }
1620
1621         return { height: root.scrollHeight, width: root.scrollWidth };
1622     }
1623 });
1624 })(Y);
1625 (function(Y) {
1626 var TOP = 'top',
1627     RIGHT = 'right',
1628     BOTTOM = 'bottom',
1629     LEFT = 'left',
1630
1631     getOffsets = function(r1, r2) {
1632         var t = Math.max(r1[TOP], r2[TOP]),
1633             r = Math.min(r1[RIGHT], r2[RIGHT]),
1634             b = Math.min(r1[BOTTOM], r2[BOTTOM]),
1635             l = Math.max(r1[LEFT], r2[LEFT]),
1636             ret = {};
1637         
1638         ret[TOP] = t;
1639         ret[RIGHT] = r;
1640         ret[BOTTOM] = b;
1641         ret[LEFT] = l;
1642         return ret;
1643     },
1644
1645     DOM = Y.DOM;
1646
1647 Y.mix(DOM, {
1648     /**
1649      * Returns an Object literal containing the following about this element: (top, right, bottom, left)
1650      * @method region
1651      * @param {HTMLElement} element The DOM element. 
1652      @return {Object} Object literal containing the following about this element: (top, right, bottom, left)
1653      */
1654     region: function(node) {
1655         var xy = DOM.getXY(node),
1656             ret = false;
1657         
1658         if (node && xy) {
1659             ret = DOM._getRegion(
1660                 xy[1], // top
1661                 xy[0] + node.offsetWidth, // right
1662                 xy[1] + node.offsetHeight, // bottom
1663                 xy[0] // left
1664             );
1665         }
1666
1667         return ret;
1668     },
1669
1670     /**
1671      * Find the intersect information for the passes nodes.
1672      * @method intersect
1673      * @param {HTMLElement} element The first element 
1674      * @param {HTMLElement | Object} element2 The element or region to check the interect with
1675      * @param {Object} altRegion An object literal containing the region for the first element if we already have the data (for performance i.e. DragDrop)
1676      @return {Object} Object literal containing the following intersection data: (top, right, bottom, left, area, yoff, xoff, inRegion)
1677      */
1678     intersect: function(node, node2, altRegion) {
1679         var r = altRegion || DOM.region(node), region = {},
1680             n = node2,
1681             off;
1682
1683         if (n.tagName) {
1684             region = DOM.region(n);
1685         } else if (Y.Lang.isObject(node2)) {
1686             region = node2;
1687         } else {
1688             return false;
1689         }
1690         
1691         off = getOffsets(region, r);
1692         return {
1693             top: off[TOP],
1694             right: off[RIGHT],
1695             bottom: off[BOTTOM],
1696             left: off[LEFT],
1697             area: ((off[BOTTOM] - off[TOP]) * (off[RIGHT] - off[LEFT])),
1698             yoff: ((off[BOTTOM] - off[TOP])),
1699             xoff: (off[RIGHT] - off[LEFT]),
1700             inRegion: DOM.inRegion(node, node2, false, altRegion)
1701         };
1702         
1703     },
1704     /**
1705      * Check if any part of this node is in the passed region
1706      * @method inRegion
1707      * @param {Object} node2 The node to get the region from or an Object literal of the region
1708      * $param {Boolean} all Should all of the node be inside the region
1709      * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
1710      * @return {Boolean} True if in region, false if not.
1711      */
1712     inRegion: function(node, node2, all, altRegion) {
1713         var region = {},
1714             r = altRegion || DOM.region(node),
1715             n = node2,
1716             off;
1717
1718         if (n.tagName) {
1719             region = DOM.region(n);
1720         } else if (Y.Lang.isObject(node2)) {
1721             region = node2;
1722         } else {
1723             return false;
1724         }
1725             
1726         if (all) {
1727             return (
1728                 r[LEFT]   >= region[LEFT]   &&
1729                 r[RIGHT]  <= region[RIGHT]  && 
1730                 r[TOP]    >= region[TOP]    && 
1731                 r[BOTTOM] <= region[BOTTOM]  );
1732         } else {
1733             off = getOffsets(region, r);
1734             if (off[BOTTOM] >= off[TOP] && off[RIGHT] >= off[LEFT]) {
1735                 return true;
1736             } else {
1737                 return false;
1738             }
1739             
1740         }
1741     },
1742
1743     /**
1744      * Check if any part of this element is in the viewport
1745      * @method inViewportRegion
1746      * @param {HTMLElement} element The DOM element. 
1747      * @param {Boolean} all Should all of the node be inside the region
1748      * @param {Object} altRegion An object literal containing the region for this node if we already have the data (for performance i.e. DragDrop)
1749      * @return {Boolean} True if in region, false if not.
1750      */
1751     inViewportRegion: function(node, all, altRegion) {
1752         return DOM.inRegion(node, DOM.viewportRegion(node), all, altRegion);
1753             
1754     },
1755
1756     _getRegion: function(t, r, b, l) {
1757         var region = {};
1758
1759         region[TOP] = region[1] = t;
1760         region[LEFT] = region[0] = l;
1761         region[BOTTOM] = b;
1762         region[RIGHT] = r;
1763         region.width = region[RIGHT] - region[LEFT];
1764         region.height = region[BOTTOM] - region[TOP];
1765
1766         return region;
1767     },
1768
1769     /**
1770      * Returns an Object literal containing the following about the visible region of viewport: (top, right, bottom, left)
1771      * @method viewportRegion
1772      @return {Object} Object literal containing the following about the visible region of the viewport: (top, right, bottom, left)
1773      */
1774     viewportRegion: function(node) {
1775         node = node || Y.config.doc.documentElement;
1776         var ret = false,
1777             scrollX,
1778             scrollY;
1779
1780         if (node) {
1781             scrollX = DOM.docScrollX(node);
1782             scrollY = DOM.docScrollY(node);
1783
1784             ret = DOM._getRegion(scrollY, // top
1785                 DOM.winWidth(node) + scrollX, // right
1786                 scrollY + DOM.winHeight(node), // bottom
1787                 scrollX); // left
1788         }
1789
1790         return ret;
1791     }
1792 });
1793 })(Y);
1794
1795
1796 }, '3.0.0' ,{requires:['dom-base', 'dom-style']});
1797 YUI.add('selector-native', function(Y) {
1798
1799 (function(Y) {
1800 /**
1801  * The selector-native module provides support for native querySelector
1802  * @module dom
1803  * @submodule selector-native
1804  * @for Selector
1805  */
1806
1807 /**
1808  * Provides support for using CSS selectors to query the DOM 
1809  * @class Selector 
1810  * @static
1811  * @for Selector
1812  */
1813
1814 Y.namespace('Selector'); // allow native module to standalone
1815
1816 var COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
1817     OWNER_DOCUMENT = 'ownerDocument',
1818     TMP_PREFIX = 'yui-tmp-',
1819     g_counter = 0;
1820
1821 var Selector = {
1822     _foundCache: [],
1823
1824     useNative: true,
1825
1826     _compare: ('sourceIndex' in document.documentElement) ?
1827         function(nodeA, nodeB) {
1828             var a = nodeA.sourceIndex,
1829                 b = nodeB.sourceIndex;
1830
1831             if (a === b) {
1832                 return 0;
1833             } else if (a > b) {
1834                 return 1;
1835             }
1836
1837             return -1;
1838
1839         } : (document.documentElement[COMPARE_DOCUMENT_POSITION] ?
1840         function(nodeA, nodeB) {
1841             if (nodeA[COMPARE_DOCUMENT_POSITION](nodeB) & 4) {
1842                 return -1;
1843             } else {
1844                 return 1;
1845             }
1846         } :
1847         function(nodeA, nodeB) {
1848             var rangeA, rangeB, compare;
1849             if (nodeA && nodeB) {
1850                 rangeA = nodeA[OWNER_DOCUMENT].createRange();
1851                 rangeA.setStart(nodeA, 0);
1852                 rangeB = nodeB[OWNER_DOCUMENT].createRange();
1853                 rangeB.setStart(nodeB, 0);
1854                 compare = rangeA.compareBoundaryPoints(1, rangeB); // 1 === Range.START_TO_END
1855             }
1856
1857             return compare;
1858         
1859     }),
1860
1861     _sort: function(nodes) {
1862         if (nodes) {
1863             nodes = Y.Array(nodes, 0, true);
1864             if (nodes.sort) {
1865                 nodes.sort(Selector._compare);
1866             }
1867         }
1868
1869         return nodes;
1870     },
1871
1872     _deDupe: function(nodes) {
1873         var ret = [],
1874             i, node;
1875
1876         for (i = 0; (node = nodes[i++]);) {
1877             if (!node._found) {
1878                 ret[ret.length] = node;
1879                 node._found = true;
1880             }
1881         }
1882
1883         for (i = 0; (node = ret[i++]);) {
1884             node._found = null;
1885             node.removeAttribute('_found');
1886         }
1887
1888         return ret;
1889     },
1890
1891     /**
1892      * Retrieves a set of nodes based on a given CSS selector. 
1893      * @method query
1894      *
1895      * @param {string} selector The CSS Selector to test the node against.
1896      * @param {HTMLElement} root optional An HTMLElement to start the query from. Defaults to Y.config.doc
1897      * @param {Boolean} firstOnly optional Whether or not to return only the first match.
1898      * @return {Array} An array of nodes that match the given selector.
1899      * @static
1900      */
1901     query: function(selector, root, firstOnly, skipNative) {
1902         root = root || Y.config.doc;
1903         var ret = [],
1904             useNative = (Y.Selector.useNative && document.querySelector && !skipNative),
1905             queries = [[selector, root]],
1906             query,
1907             result,
1908             i,
1909             fn = (useNative) ? Y.Selector._nativeQuery : Y.Selector._bruteQuery;
1910
1911         if (selector && fn) {
1912             // split group into seperate queries
1913             if (!skipNative && // already done if skipping
1914                     (!useNative || root.tagName)) { // split native when element scoping is needed
1915                 queries = Selector._splitQueries(selector, root);
1916             }
1917
1918             for (i = 0; (query = queries[i++]);) {
1919                 result = fn(query[0], query[1], firstOnly);
1920                 if (!firstOnly) { // coerce DOM Collection to Array
1921                     result = Y.Array(result, 0, true);
1922                 }
1923                 if (result) {
1924                     ret = ret.concat(result);
1925                 }
1926             }
1927
1928             if (queries.length > 1) { // remove dupes and sort by doc order 
1929                 ret = Selector._sort(Selector._deDupe(ret));
1930             }
1931         }
1932
1933         return (firstOnly) ? (ret[0] || null) : ret;
1934
1935     },
1936
1937     // allows element scoped queries to begin with combinator
1938     // e.g. query('> p', document.body) === query('body > p')
1939     _splitQueries: function(selector, node) {
1940         var groups = selector.split(','),
1941             queries = [],
1942             prefix = '',
1943             i, len;
1944
1945         if (node) {
1946             // enforce for element scoping
1947             if (node.tagName) {
1948                 node.id = node.id || Y.guid();
1949                 prefix = '#' + node.id + ' ';
1950             }
1951
1952             for (i = 0, len = groups.length; i < len; ++i) {
1953                 selector =  prefix + groups[i];
1954                 queries.push([selector, node]);
1955             }
1956         }
1957
1958         return queries;
1959     },
1960
1961     _nativeQuery: function(selector, root, one) {
1962         try {
1963             return root['querySelector' + (one ? '' : 'All')](selector);
1964         } catch(e) { // fallback to brute if available
1965             return Y.Selector.query(selector, root, one, true); // redo with skipNative true
1966         }
1967     },
1968
1969     filter: function(nodes, selector) {
1970         var ret = [],
1971             i, node;
1972
1973         if (nodes && selector) {
1974             for (i = 0; (node = nodes[i++]);) {
1975                 if (Y.Selector.test(node, selector)) {
1976                     ret[ret.length] = node;
1977                 }
1978             }
1979         } else {
1980         }
1981
1982         return ret;
1983     },
1984
1985     test: function(node, selector, root) {
1986         var ret = false,
1987             groups = selector.split(','),
1988             item,
1989             i, group;
1990
1991         if (node && node.tagName) { // only test HTMLElements
1992             root = root || node.ownerDocument;
1993
1994             if (!node.id) {
1995                 node.id = TMP_PREFIX + g_counter++;
1996             }
1997             for (i = 0; (group = groups[i++]);) { // TODO: off-dom test
1998                 group += '#' + node.id; // add ID for uniqueness
1999                 item = Y.Selector.query(group, root, true);
2000                 ret = (item === node);
2001                 if (ret) {
2002                     break;
2003                 }
2004             }
2005         }
2006
2007         return ret;
2008     }
2009 };
2010
2011 Y.mix(Y.Selector, Selector, true);
2012
2013 })(Y);
2014
2015
2016 }, '3.0.0' ,{requires:['dom-base']});
2017 YUI.add('selector-css2', function(Y) {
2018
2019 /**
2020  * The selector module provides helper methods allowing CSS2 Selectors to be used with DOM elements.
2021  * @module dom
2022  * @submodule selector-css2
2023  * @for Selector
2024  */
2025
2026 /**
2027  * Provides helper methods for collecting and filtering DOM elements.
2028  */
2029
2030 var PARENT_NODE = 'parentNode',
2031     TAG_NAME = 'tagName',
2032     ATTRIBUTES = 'attributes',
2033     COMBINATOR = 'combinator',
2034     PSEUDOS = 'pseudos',
2035
2036     Selector = Y.Selector,
2037
2038     SelectorCSS2 = {
2039         SORT_RESULTS: true,
2040         _children: function(node, tag) {
2041             var ret = node.children,
2042                 i,
2043                 children = [],
2044                 childNodes,
2045                 child;
2046
2047             if (node.children && tag && node.children.tags) {
2048                 children = node.children.tags(tag);
2049             } else if ((!ret && node[TAG_NAME]) || (ret && tag)) { // only HTMLElements have children
2050                 childNodes = ret || node.childNodes;
2051                 ret = [];
2052                 for (i = 0; (child = childNodes[i++]);) {
2053                     if (child.tagName) {
2054                         if (!tag || tag === child.tagName) {
2055                             ret.push(child);
2056                         }
2057                     }
2058                 }
2059             }
2060
2061             return ret || [];
2062         },
2063
2064         _regexCache: {},
2065
2066         _re: {
2067             attr: /(\[.*\])/g,
2068             pseudos: /:([\-\w]+(?:\(?:['"]?(.+)['"]?\)))*/i
2069         },
2070
2071         /**
2072          * Mapping of shorthand tokens to corresponding attribute selector 
2073          * @property shorthand
2074          * @type object
2075          */
2076         shorthand: {
2077             '\\#(-?[_a-z]+[-\\w]*)': '[id=$1]',
2078             '\\.(-?[_a-z]+[-\\w]*)': '[className~=$1]'
2079         },
2080
2081         /**
2082          * List of operators and corresponding boolean functions. 
2083          * These functions are passed the attribute and the current node's value of the attribute.
2084          * @property operators
2085          * @type object
2086          */
2087         operators: {
2088             '': function(node, attr) { return Y.DOM.getAttribute(node, attr) !== ''; }, // Just test for existence of attribute
2089             //'': '.+',
2090             //'=': '^{val}$', // equality
2091             '~=': '(?:^|\\s+){val}(?:\\s+|$)', // space-delimited
2092             '|=': '^{val}-?' // optional hyphen-delimited
2093         },
2094
2095         pseudos: {
2096            'first-child': function(node) { 
2097                 return Y.Selector._children(node[PARENT_NODE])[0] === node; 
2098             } 
2099         },
2100
2101         _bruteQuery: function(selector, root, firstOnly) {
2102             var ret = [],
2103                 nodes = [],
2104                 tokens = Selector._tokenize(selector),
2105                 token = tokens[tokens.length - 1],
2106                 rootDoc = Y.DOM._getDoc(root),
2107                 id,
2108                 className,
2109                 tagName;
2110
2111
2112             // if we have an initial ID, set to root when in document
2113             if (tokens[0] && rootDoc === root &&  
2114                     (id = tokens[0].id) &&
2115                     rootDoc.getElementById(id)) {
2116                 root = rootDoc.getElementById(id);
2117             }
2118
2119             if (token) {
2120                 // prefilter nodes
2121                 id = token.id;
2122                 className = token.className;
2123                 tagName = token.tagName || '*';
2124
2125                 // try ID first
2126                 if (id) {
2127                     if (rootDoc.getElementById(id)) { // if in document
2128                     nodes = [rootDoc.getElementById(id)]; // TODO: DOM.byId?
2129                 }
2130                 // try className if supported
2131                 } else if (className) {
2132                     nodes = root.getElementsByClassName(className);
2133                 } else if (tagName) { // default to tagName
2134                     nodes = root.getElementsByTagName(tagName || '*');
2135                 }
2136
2137                 if (nodes.length) {
2138                     ret = Selector._filterNodes(nodes, tokens, firstOnly);
2139                 }
2140             }
2141
2142             return ret;
2143         },
2144         
2145         _filterNodes: function(nodes, tokens, firstOnly) {
2146             var i = 0,
2147                 j,
2148                 len = tokens.length,
2149                 n = len - 1,
2150                 result = [],
2151                 node = nodes[0],
2152                 tmpNode = node,
2153                 getters = Y.Selector.getters,
2154                 operator,
2155                 combinator,
2156                 token,
2157                 path,
2158                 pass,
2159                 //FUNCTION = 'function',
2160                 value,
2161                 tests,
2162                 test;
2163
2164             //do {
2165             for (i = 0; (tmpNode = node = nodes[i++]);) {
2166                 n = len - 1;
2167                 path = null;
2168                 
2169                 testLoop:
2170                 while (tmpNode && tmpNode.tagName) {
2171                     token = tokens[n];
2172                     tests = token.tests;
2173                     j = tests.length;
2174                     if (j && !pass) {
2175                         while ((test = tests[--j])) {
2176                             operator = test[1];
2177                             if (getters[test[0]]) {
2178                                 value = getters[test[0]](tmpNode, test[0]);
2179                             } else {
2180                                 value = tmpNode[test[0]];
2181                                 // use getAttribute for non-standard attributes
2182                                 if (value === undefined && tmpNode.getAttribute) {
2183                                     value = tmpNode.getAttribute(test[0]);
2184                                 }
2185                             }
2186
2187                             if ((operator === '=' && value !== test[2]) ||  // fast path for equality
2188                                 (operator.test && !operator.test(value)) ||  // regex test
2189                                 (operator.call && !operator(tmpNode, test[0]))) { // function test
2190
2191                                 // skip non element nodes or non-matching tags
2192                                 if ((tmpNode = tmpNode[path])) {
2193                                     while (tmpNode &&
2194                                         (!tmpNode.tagName ||
2195                                             (token.tagName && token.tagName !== tmpNode.tagName))
2196                                     ) {
2197                                         tmpNode = tmpNode[path]; 
2198                                     }
2199                                 }
2200                                 continue testLoop;
2201                             }
2202                         }
2203                     }
2204
2205                     n--; // move to next token
2206                     // now that we've passed the test, move up the tree by combinator
2207                     if (!pass && (combinator = token.combinator)) {
2208                         path = combinator.axis;
2209                         tmpNode = tmpNode[path];
2210
2211                         // skip non element nodes
2212                         while (tmpNode && !tmpNode.tagName) {
2213                             tmpNode = tmpNode[path]; 
2214                         }
2215
2216                         if (combinator.direct) { // one pass only
2217                             path = null; 
2218                         }
2219
2220                     } else { // success if we made it this far
2221                         result.push(node);
2222                         if (firstOnly) {
2223                             return result;
2224                         }
2225                         break;
2226                     }
2227                 }
2228             }// while (tmpNode = node = nodes[++i]);
2229             node = tmpNode = null;
2230             return result;
2231         },
2232
2233         _getRegExp: function(str, flags) {
2234             var regexCache = Selector._regexCache;
2235             flags = flags || '';
2236             if (!regexCache[str + flags]) {
2237                 regexCache[str + flags] = new RegExp(str, flags);
2238             }
2239             return regexCache[str + flags];
2240         },
2241
2242         combinators: {
2243             ' ': {
2244                 axis: 'parentNode'
2245             },
2246
2247             '>': {
2248                 axis: 'parentNode',
2249                 direct: true
2250             },
2251
2252
2253             '+': {
2254                 axis: 'previousSibling',
2255                 direct: true
2256             }
2257         },
2258
2259         _parsers: [
2260             {
2261                 name: ATTRIBUTES,
2262                 re: /^\[([a-z]+\w*)+([~\|\^\$\*!=]=?)?['"]?([^\]]*?)['"]?\]/i,
2263                 fn: function(match, token) {
2264                     var operator = match[2] || '',
2265                         operators = Y.Selector.operators,
2266                         test;
2267
2268                     // add prefiltering for ID and CLASS
2269                     if ((match[1] === 'id' && operator === '=') ||
2270                             (match[1] === 'className' &&
2271                             document.getElementsByClassName &&
2272                             (operator === '~=' || operator === '='))) {
2273                         token.prefilter = match[1];
2274                         token[match[1]] = match[3];
2275                     }
2276
2277                     // add tests
2278                     if (operator in operators) {
2279                         test = operators[operator];
2280                         if (typeof test === 'string') {
2281                             test = Y.Selector._getRegExp(test.replace('{val}', match[3]));
2282                         }
2283                         match[2] = test;
2284                     }
2285                     if (!token.last || token.prefilter !== match[1]) {
2286                         return match.slice(1);
2287                     }
2288                 }
2289
2290             },
2291             {
2292                 name: TAG_NAME,
2293                 re: /^((?:-?[_a-z]+[\w-]*)|\*)/i,
2294                 fn: function(match, token) {
2295                     var tag = match[1].toUpperCase();
2296                     token.tagName = tag;
2297
2298                     if (tag !== '*' && (!token.last || token.prefilter)) {
2299                         return [TAG_NAME, '=', tag];
2300                     }
2301                     if (!token.prefilter) {
2302                         token.prefilter = 'tagName';
2303                     }
2304                 }
2305             },
2306             {
2307                 name: COMBINATOR,
2308                 re: /^\s*([>+~]|\s)\s*/,
2309                 fn: function(match, token) {
2310                 }
2311             },
2312             {
2313                 name: PSEUDOS,
2314                 re: /^:([\-\w]+)(?:\(['"]?(.+)['"]?\))*/i,
2315                 fn: function(match, token) {
2316                     var test = Selector[PSEUDOS][match[1]];
2317                     if (test) { // reorder match array
2318                         return [match[2], test];
2319                     } else { // selector token not supported (possibly missing CSS3 module)
2320                         return false;
2321                     }
2322                 }
2323             }
2324             ],
2325
2326         _getToken: function(token) {
2327             return {
2328                 tagName: null,
2329                 id: null,
2330                 className: null,
2331                 attributes: {},
2332                 combinator: null,
2333                 tests: []
2334             };
2335         },
2336
2337         /**
2338             Break selector into token units per simple selector.
2339             Combinator is attached to the previous token.
2340          */
2341         _tokenize: function(selector) {
2342             selector = selector || '';
2343             selector = Selector._replaceShorthand(Y.Lang.trim(selector)); 
2344             var token = Selector._getToken(),     // one token per simple selector (left selector holds combinator)
2345                 query = selector, // original query for debug report
2346                 tokens = [],    // array of tokens
2347                 found = false,  // whether or not any matches were found this pass
2348                 match,         // the regex match
2349                 test,
2350                 i, parser;
2351
2352             /*
2353                 Search for selector patterns, store, and strip them from the selector string
2354                 until no patterns match (invalid selector) or we run out of chars.
2355
2356                 Multiple attributes and pseudos are allowed, in any order.
2357                 for example:
2358                     'form:first-child[type=button]:not(button)[lang|=en]'
2359             */
2360             outer:
2361             do {
2362                 found = false; // reset after full pass
2363                 for (i = 0; (parser = Selector._parsers[i++]);) {
2364                     if ( (match = parser.re.exec(selector)) ) { // note assignment
2365                         if (parser !== COMBINATOR ) {
2366                             token.selector = selector;
2367                         }
2368                         selector = selector.replace(match[0], ''); // strip current match from selector
2369                         if (!selector.length) {
2370                             token.last = true;
2371                         }
2372
2373                         if (Selector._attrFilters[match[1]]) { // convert class to className, etc.
2374                             match[1] = Selector._attrFilters[match[1]];
2375                         }
2376
2377                         test = parser.fn(match, token);
2378                         if (test === false) { // selector not supported
2379                             found = false;
2380                             break outer;
2381                         } else if (test) {
2382                             token.tests.push(test);
2383                         }
2384
2385                         if (!selector.length || parser.name === COMBINATOR) {
2386                             tokens.push(token);
2387                             token = Selector._getToken(token);
2388                             if (parser.name === COMBINATOR) {
2389                                 token.combinator = Y.Selector.combinators[match[1]];
2390                             }
2391                         }
2392                         found = true;
2393                     }
2394                 }
2395             } while (found && selector.length);
2396
2397             if (!found || selector.length) { // not fully parsed
2398                 tokens = [];
2399             }
2400             return tokens;
2401         },
2402
2403         _replaceShorthand: function(selector) {
2404             var shorthand = Selector.shorthand,
2405                 attrs = selector.match(Selector._re.attr), // pull attributes to avoid false pos on "." and "#"
2406                 pseudos = selector.match(Selector._re.pseudos), // pull attributes to avoid false pos on "." and "#"
2407                 re, i, len;
2408
2409             if (pseudos) {
2410                 selector = selector.replace(Selector._re.pseudos, '!!REPLACED_PSEUDO!!');
2411             }
2412
2413             if (attrs) {
2414                 selector = selector.replace(Selector._re.attr, '!!REPLACED_ATTRIBUTE!!');
2415             }
2416
2417             for (re in shorthand) {
2418                 if (shorthand.hasOwnProperty(re)) {
2419                     selector = selector.replace(Selector._getRegExp(re, 'gi'), shorthand[re]);
2420                 }
2421             }
2422
2423             if (attrs) {
2424                 for (i = 0, len = attrs.length; i < len; ++i) {
2425                     selector = selector.replace('!!REPLACED_ATTRIBUTE!!', attrs[i]);
2426                 }
2427             }
2428             if (pseudos) {
2429                 for (i = 0, len = pseudos.length; i < len; ++i) {
2430                     selector = selector.replace('!!REPLACED_PSEUDO!!', pseudos[i]);
2431                 }
2432             }
2433             return selector;
2434         },
2435
2436         _attrFilters: {
2437             'class': 'className',
2438             'for': 'htmlFor'
2439         },
2440
2441         getters: {
2442             href: function(node, attr) {
2443                 return Y.DOM.getAttribute(node, attr);
2444             }
2445         }
2446     };
2447
2448 Y.mix(Y.Selector, SelectorCSS2, true);
2449 Y.Selector.getters.src = Y.Selector.getters.rel = Y.Selector.getters.href;
2450
2451 // IE wants class with native queries
2452 if (Y.Selector.useNative && document.querySelector) {
2453     Y.Selector.shorthand['\\.(-?[_a-z]+[-\\w]*)'] = '[class~=$1]';
2454 }
2455
2456
2457
2458 }, '3.0.0' ,{requires:['selector-native']});
2459
2460
2461 YUI.add('selector', function(Y){}, '3.0.0' ,{use:['selector-native', 'selector-css2']});
2462
2463
2464
2465 YUI.add('dom', function(Y){}, '3.0.0' ,{use:['dom-base', 'dom-style', 'dom-screen', 'selector']});
2466