src/cm/media/js/lib/yui/yui_3.0.0b1/build/dom/dom-base-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});