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