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