/ajax/libs/yui/3.0.0beta1m2/dom/dom-base.js

https://gitlab.com/Mirros/cdnjs · JavaScript · 887 lines · 510 code · 109 blank · 268 comment · 140 complexity · b6441173200e9fae75dd0507f019f96e MD5 · raw file

  1. YUI.add('dom-base', function(Y) {
  2. /**
  3. * The DOM utility provides a cross-browser abtraction layer
  4. * normalizing DOM tasks, and adds extra helper functionality
  5. * for other common tasks.
  6. * @module dom
  7. * @submodule dom-base
  8. *
  9. */
  10. /**
  11. * Provides DOM helper methods.
  12. * @class DOM
  13. *
  14. */
  15. var NODE_TYPE = 'nodeType',
  16. OWNER_DOCUMENT = 'ownerDocument',
  17. DOCUMENT_ELEMENT = 'documentElement',
  18. DEFAULT_VIEW = 'defaultView',
  19. PARENT_WINDOW = 'parentWindow',
  20. TAG_NAME = 'tagName',
  21. PARENT_NODE = 'parentNode',
  22. FIRST_CHILD = 'firstChild',
  23. LAST_CHILD = 'lastChild',
  24. PREVIOUS_SIBLING = 'previousSibling',
  25. NEXT_SIBLING = 'nextSibling',
  26. CONTAINS = 'contains',
  27. COMPARE_DOCUMENT_POSITION = 'compareDocumentPosition',
  28. INNER_TEXT = 'innerText',
  29. TEXT_CONTENT = 'textContent',
  30. LENGTH = 'length',
  31. UNDEFINED = undefined,
  32. re_tag = /<([a-z]+)/i;
  33. Y.DOM = {
  34. /**
  35. * Returns the HTMLElement with the given ID (Wrapper for document.getElementById).
  36. * @method byId
  37. * @param {String} id the id attribute
  38. * @param {Object} doc optional The document to search. Defaults to current document
  39. * @return {HTMLElement | null} The HTMLElement with the id, or null if none found.
  40. */
  41. byId: function(id, doc) {
  42. doc = doc || Y.config.doc;
  43. // TODO: IE Name
  44. return doc.getElementById(id);
  45. },
  46. /**
  47. * Returns the text content of the HTMLElement.
  48. * @method getText
  49. * @param {HTMLElement} element The html element.
  50. * @return {String} The text content of the element (includes text of any descending elements).
  51. */
  52. getText: function(element) {
  53. var text = element ? element[TEXT_CONTENT] : '';
  54. if (text === UNDEFINED && INNER_TEXT in element) {
  55. text = element[INNER_TEXT];
  56. }
  57. return text || '';
  58. },
  59. // TODO: pull out sugar (rely on _childBy, byAxis, etc)?
  60. /**
  61. * Finds the firstChild of the given HTMLElement.
  62. * @method firstChild
  63. * @param {HTMLElement} element The html element.
  64. * @param {Function} fn optional An optional boolean test to apply.
  65. * The optional function is passed the current HTMLElement being tested as its only argument.
  66. * If no function is given, the first found is returned.
  67. * @return {HTMLElement | null} The first matching child html element.
  68. */
  69. firstChild: function(element, fn) {
  70. return Y.DOM._childBy(element, null, fn);
  71. },
  72. firstChildByTag: function(element, tag, fn) {
  73. return Y.DOM._childBy(element, tag, fn);
  74. },
  75. /**
  76. * Finds the lastChild of the given HTMLElement.
  77. * @method lastChild
  78. * @param {HTMLElement} element The html element.
  79. * @param {String} tag The tag to search for.
  80. * @param {Function} fn optional An optional boolean test to apply.
  81. * The optional function is passed the current HTMLElement being tested as its only argument.
  82. * If no function is given, the first found is returned.
  83. * @return {HTMLElement | null} The first matching child html element.
  84. */
  85. lastChild: function(element, fn) {
  86. return Y.DOM._childBy(element, null, fn, true);
  87. },
  88. lastChildByTag: function(element, tag, fn) {
  89. return Y.DOM._childBy(element, tag, fn, true);
  90. },
  91. /*
  92. * Finds all HTMLElement childNodes matching the given tag.
  93. * @method childrenByTag
  94. * @param {HTMLElement} element The html element.
  95. * @param {String} tag The tag to search for.
  96. * @param {Function} fn optional An optional boolean test to apply.
  97. * The optional function is passed the current HTMLElement being tested as its only argument.
  98. * If no function is given, all children with the given tag are collected.
  99. * @return {Array} The collection of child elements.
  100. * TODO: deprecate? Webkit children.tags() returns grandchildren
  101. */
  102. _childrenByTag: function() {
  103. if (document[DOCUMENT_ELEMENT].children) {
  104. return function(element, tag, fn, toArray) { // TODO: keep toArray option?
  105. tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
  106. var elements = [],
  107. wrapFn = fn;
  108. if (element) {
  109. if (tag && !Y.UA.webkit) { // children.tags() broken in safari
  110. elements = element.children.tags(tag);
  111. } else {
  112. elements = element.children;
  113. if (tag) {
  114. wrapFn = function(el) {
  115. return el[TAG_NAME].toUpperCase() === tag && (!fn || fn(el));
  116. }
  117. }
  118. }
  119. elements = Y.DOM.filterElementsBy(elements, wrapFn);
  120. }
  121. return elements;
  122. };
  123. } else {
  124. return function(element, tag, fn) {
  125. tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
  126. var elements = [],
  127. wrapFn = fn;
  128. if (element) {
  129. elements = element.childNodes;
  130. if (tag) { // wrap fn and add tag test TODO: allow tag in filterElementsBy?
  131. wrapFn = function(el) {
  132. return el[TAG_NAME].toUpperCase() === tag && (!fn || fn(el));
  133. };
  134. }
  135. elements = Y.DOM.filterElementsBy(elements, wrapFn);
  136. }
  137. return elements;
  138. };
  139. }
  140. }(),
  141. /**
  142. * Finds all HTMLElement childNodes.
  143. * @method children
  144. * @param {HTMLElement} element The html element.
  145. * @param {Function} fn optional An optional boolean test to apply.
  146. * The optional function is passed the current HTMLElement being tested as its only argument.
  147. * If no function is given, all children are collected.
  148. * @return {Array} The collection of child elements.
  149. */
  150. children: function(element, fn) {
  151. return Y.DOM._childrenByTag(element, null, fn);
  152. },
  153. /**
  154. * Finds the previous sibling of the element.
  155. * @method previous
  156. * @param {HTMLElement} element The html element.
  157. * @param {Function} fn optional An optional boolean test to apply.
  158. * The optional function is passed the current DOM node being tested as its only argument.
  159. * If no function is given, the first sibling is returned.
  160. * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
  161. * @return {HTMLElement | null} The matching DOM node or null if none found.
  162. */
  163. previous: function(element, fn, all) {
  164. return Y.DOM.elementByAxis(element, PREVIOUS_SIBLING, fn, all);
  165. },
  166. /**
  167. * Finds the next sibling of the element.
  168. * @method next
  169. * @param {HTMLElement} element The html element.
  170. * @param {Function} fn optional An optional boolean test to apply.
  171. * The optional function is passed the current DOM node being tested as its only argument.
  172. * If no function is given, the first sibling is returned.
  173. * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
  174. * @return {HTMLElement | null} The matching DOM node or null if none found.
  175. */
  176. next: function(element, fn, all) {
  177. return Y.DOM.elementByAxis(element, NEXT_SIBLING, fn, all);
  178. },
  179. /**
  180. * Finds the ancestor of the element.
  181. * @method ancestor
  182. * @param {HTMLElement} element The html element.
  183. * @param {Function} fn optional An optional boolean test to apply.
  184. * The optional function is passed the current DOM node being tested as its only argument.
  185. * If no function is given, the parentNode is returned.
  186. * @param {Boolean} all optional Whether all node types should be scanned, or just element nodes.
  187. * @return {HTMLElement | null} The matching DOM node or null if none found.
  188. */
  189. // TODO: optional stopAt node?
  190. ancestor: function(element, fn, all) {
  191. return Y.DOM.elementByAxis(element, PARENT_NODE, fn, all);
  192. },
  193. /**
  194. * Searches the element by the given axis for the first matching element.
  195. * @method elementByAxis
  196. * @param {HTMLElement} element The html element.
  197. * @param {String} axis The axis to search (parentNode, nextSibling, previousSibling).
  198. * @param {Function} fn optional An optional boolean test to apply.
  199. * @param {Boolean} all optional Whether all node types should be returned, or just element nodes.
  200. * The optional function is passed the current HTMLElement being tested as its only argument.
  201. * If no function is given, the first element is returned.
  202. * @return {HTMLElement | null} The matching element or null if none found.
  203. */
  204. elementByAxis: function(element, axis, fn, all) {
  205. while (element && (element = element[axis])) { // NOTE: assignment
  206. if ( (all || element[TAG_NAME]) && (!fn || fn(element)) ) {
  207. return element;
  208. }
  209. }
  210. return null;
  211. },
  212. /**
  213. * Finds all elements with the given tag.
  214. * @method byTag
  215. * @param {String} tag The tag being search for.
  216. * @param {HTMLElement} root optional An optional root element to start from.
  217. * @param {Function} fn optional An optional boolean test to apply.
  218. * The optional function is passed the current HTMLElement being tested as its only argument.
  219. * If no function is given, all elements with the given tag are returned.
  220. * @return {Array} The collection of matching elements.
  221. */
  222. byTag: function(tag, root, fn) {
  223. root = root || Y.config.doc;
  224. var elements = root.getElementsByTagName(tag),
  225. retNodes = [];
  226. for (var i = 0, len = elements[LENGTH]; i < len; ++i) {
  227. if ( !fn || fn(elements[i]) ) {
  228. retNodes[retNodes[LENGTH]] = elements[i];
  229. }
  230. }
  231. return retNodes;
  232. },
  233. /**
  234. * Finds the first element with the given tag.
  235. * @method firstByTag
  236. * @param {String} tag The tag being search for.
  237. * @param {HTMLElement} root optional An optional root element to start from.
  238. * @param {Function} fn optional An optional boolean test to apply.
  239. * The optional function is passed the current HTMLElement being tested as its only argument.
  240. * If no function is given, the first match is returned.
  241. * @return {HTMLElement} The matching element.
  242. */
  243. firstByTag: function(tag, root, fn) {
  244. root = root || Y.config.doc;
  245. var elements = root.getElementsByTagName(tag),
  246. ret = null;
  247. for (var i = 0, len = elements[LENGTH]; i < len; ++i) {
  248. if ( !fn || fn(elements[i]) ) {
  249. ret = elements[i];
  250. break;
  251. }
  252. }
  253. return ret;
  254. },
  255. /**
  256. * Filters a collection of HTMLElements by the given attributes.
  257. * @method filterElementsBy
  258. * @param {Array} elements The collection of HTMLElements to filter.
  259. * @param {Function} fn A boolean test to apply.
  260. * The function is passed the current HTMLElement being tested as its only argument.
  261. * If no function is given, all HTMLElements are kept.
  262. * @return {Array} The filtered collection of HTMLElements.
  263. */
  264. filterElementsBy: function(elements, fn, firstOnly) {
  265. var ret = (firstOnly) ? null : [];
  266. for (var i = 0, len = elements[LENGTH]; i < len; ++i) {
  267. if (elements[i][TAG_NAME] && (!fn || fn(elements[i]))) {
  268. if (firstOnly) {
  269. ret = elements[i];
  270. break;
  271. } else {
  272. ret[ret[LENGTH]] = elements[i];
  273. }
  274. }
  275. }
  276. return ret;
  277. },
  278. /**
  279. * Determines whether or not one HTMLElement is or contains another HTMLElement.
  280. * @method contains
  281. * @param {HTMLElement} element The containing html element.
  282. * @param {HTMLElement} needle The html element that may be contained.
  283. * @return {Boolean} Whether or not the element is or contains the needle.
  284. */
  285. contains: function(element, needle) {
  286. var ret = false;
  287. if ( !needle || !element || !needle[NODE_TYPE] || !element[NODE_TYPE]) {
  288. ret = false;
  289. } else if (element[CONTAINS]) {
  290. if (Y.UA.opera || needle[NODE_TYPE] === 1) { // IE & SAF contains fail if needle not an ELEMENT_NODE
  291. ret = element[CONTAINS](needle);
  292. } else {
  293. ret = Y.DOM._bruteContains(element, needle);
  294. }
  295. } else if (element[COMPARE_DOCUMENT_POSITION]) { // gecko
  296. if (element === needle || !!(element[COMPARE_DOCUMENT_POSITION](needle) & 16)) {
  297. ret = true;
  298. }
  299. }
  300. return ret;
  301. },
  302. /**
  303. * Determines whether or not the HTMLElement is part of the document.
  304. * @method inDoc
  305. * @param {HTMLElement} element The containing html element.
  306. * @param {HTMLElement} doc optional The document to check.
  307. * @return {Boolean} Whether or not the element is attached to the document.
  308. */
  309. inDoc: function(element, doc) {
  310. doc = doc || Y.config.doc;
  311. return Y.DOM.contains(doc.documentElement, element);
  312. },
  313. /**
  314. * Inserts the new node as the previous sibling of the reference node
  315. * @method insertBefore
  316. * @param {String | HTMLElement} newNode The node to be inserted
  317. * @param {String | HTMLElement} referenceNode The node to insert the new node before
  318. * @return {HTMLElement} The node that was inserted (or null if insert fails)
  319. */
  320. insertBefore: function(newNode, referenceNode) {
  321. if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
  322. YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'DOM');
  323. return null;
  324. }
  325. return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode);
  326. },
  327. /**
  328. * Inserts the new node as the next sibling of the reference node
  329. * @method insertAfter
  330. * @param {String | HTMLElement} newNode The node to be inserted
  331. * @param {String | HTMLElement} referenceNode The node to insert the new node after
  332. * @return {HTMLElement} The node that was inserted (or null if insert fails)
  333. */
  334. insertAfter: function(newNode, referenceNode) {
  335. if (!newNode || !referenceNode || !referenceNode[PARENT_NODE]) {
  336. YAHOO.log('insertAfter failed: missing or invalid arg(s)', 'error', 'DOM');
  337. return null;
  338. }
  339. if (referenceNode[NEXT_SIBLING]) {
  340. return referenceNode[PARENT_NODE].insertBefore(newNode, referenceNode[NEXT_SIBLING]);
  341. } else {
  342. return referenceNode[PARENT_NODE].appendChild(newNode);
  343. }
  344. },
  345. /**
  346. * Creates a new dom node using the provided markup string.
  347. * @method create
  348. * @param {String} html The markup used to create the element
  349. * @param {HTMLDocument} doc An optional document context
  350. * @param {Boolean} execScripts Whether or not any provided scripts should be executed.
  351. * If execScripts is false, all scripts are stripped.
  352. */
  353. create: function(html, doc, execScripts) {
  354. doc = doc || Y.config.doc;
  355. var m = re_tag.exec(html),
  356. create = Y.DOM._create,
  357. custom = Y.DOM.creators,
  358. tag, node;
  359. if (m && custom[m[1]]) {
  360. if (typeof custom[m[1]] === 'function') {
  361. create = custom[m[1]];
  362. } else {
  363. tag = custom[m[1]];
  364. }
  365. }
  366. node = create(html, doc, tag);
  367. return node;
  368. },
  369. CUSTOM_ATTRIBUTES: (!document.documentElement.hasAttribute) ? { // IE < 8
  370. 'for': 'htmlFor',
  371. 'class': 'className'
  372. } : { // w3c
  373. 'htmlFor': 'for',
  374. 'className': 'class'
  375. },
  376. /**
  377. * Provides a normalized attribute interface.
  378. * @method setAttibute
  379. * @param {String | HTMLElement} el The target element for the attribute.
  380. * @param {String} attr The attribute to set.
  381. * @param {String} val The value of the attribute.
  382. */
  383. setAttribute: function(el, attr, val) {
  384. if (el && el.setAttribute) {
  385. attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
  386. el.setAttribute(attr, val);
  387. }
  388. },
  389. /**
  390. * Provides a normalized attribute interface.
  391. * @method getAttibute
  392. * @param {String | HTMLElement} el The target element for the attribute.
  393. * @param {String} attr The attribute to get.
  394. * @return {String} The current value of the attribute.
  395. */
  396. getAttribute: function(el, attr) {
  397. var ret = '';
  398. if (el && el.getAttribute) {
  399. attr = Y.DOM.CUSTOM_ATTRIBUTES[attr] || attr;
  400. ret = el.getAttribute(attr, 2);
  401. if (ret === null) {
  402. ret = ''; // per DOM spec
  403. }
  404. }
  405. return ret;
  406. },
  407. srcIndex: (document.documentElement.sourceIndex) ?
  408. function(node) {
  409. return (node && node.sourceIndex) ? node.sourceIndex : null;
  410. } :
  411. function(node) {
  412. return (node && node[OWNER_DOCUMENT]) ?
  413. [].indexOf.call(node[OWNER_DOCUMENT].
  414. getElementsByTagName('*'), node) : null;
  415. },
  416. isWindow: function(obj) {
  417. return obj.alert && obj.document;
  418. },
  419. _create: function(html, doc, tag) {
  420. tag = tag || 'div';
  421. var frag = doc.createElement(tag);
  422. frag.innerHTML = Y.Lang.trim(html);
  423. return frag.removeChild(frag[FIRST_CHILD]);
  424. },
  425. insertHTML: function(node, content, where, execScripts) {
  426. var scripts,
  427. newNode = Y.DOM.create(content);
  428. switch(where) {
  429. case 'innerHTML':
  430. node.innerHTML = content; // TODO: purge?
  431. newNode = node;
  432. break;
  433. case 'beforeBegin':
  434. Y.DOM.insertBefore(newNode, node);
  435. break;
  436. case 'afterBegin':
  437. Y.DOM.insertBefore(newNode, node[FIRST_CHILD]);
  438. break;
  439. case 'afterEnd':
  440. Y.DOM.insertAfter(newNode, node);
  441. break;
  442. default: // and 'beforeEnd'
  443. node.appendChild(newNode);
  444. }
  445. if (execScripts) {
  446. if (newNode.nodeName.toUpperCase() === 'SCRIPT' && !Y.UA.gecko) {
  447. scripts = [newNode]; // execute the new script
  448. } else {
  449. scripts = newNode.getElementsByTagName('script');
  450. }
  451. Y.DOM._execScripts(scripts);
  452. } else { // prevent any scripts from being injected
  453. Y.DOM._stripScripts(newNode);
  454. }
  455. return newNode;
  456. },
  457. VALUE_SETTERS: {},
  458. VALUE_GETTERS: {},
  459. getValue: function(node) {
  460. var ret = '', // TODO: return null?
  461. getter;
  462. if (node && node[TAG_NAME]) {
  463. getter = Y.DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
  464. if (getter) {
  465. ret = getter(node);
  466. } else {
  467. ret = node.value;
  468. }
  469. }
  470. return (typeof ret === 'string') ? ret : '';
  471. },
  472. setValue: function(node, val) {
  473. var setter;
  474. if (node && node[TAG_NAME]) {
  475. setter = Y.DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
  476. if (setter) {
  477. setter(node, val);
  478. } else {
  479. node.value = val;
  480. }
  481. }
  482. },
  483. _stripScripts: function(node) {
  484. var scripts = node.getElementsByTagName('script');
  485. for (var i = 0, script; script = scripts[i++];) {
  486. script.parentNode.removeChild(script);
  487. }
  488. },
  489. _execScripts: function(scripts, startIndex) {
  490. var newScript;
  491. startIndex = startIndex || 0;
  492. for (var i = startIndex, script; script = scripts[i++];) {
  493. newScript = script.ownerDocument.createElement('script');
  494. script.parentNode.replaceChild(newScript, script);
  495. if (script.text) {
  496. newScript.text = script.text;
  497. } else if (script.src) {
  498. newScript.src = script.src;
  499. // "pause" while loading to ensure exec order
  500. // FF reports typeof onload as "undefined", so try IE first
  501. if (typeof newScript.onreadystatechange !== 'undefined') {
  502. newScript.onreadystatechange = function() {
  503. if (/loaded|complete/.test(script.readyState)) {
  504. event.srcElement.onreadystatechange = null;
  505. // timer to help ensure exec order
  506. setTimeout(function() {Y.DOM._execScripts(scripts, i++)}, 0);
  507. }
  508. };
  509. } else {
  510. newScript.onload = function(e) {
  511. e.target.onload = null;
  512. Y.DOM._execScripts(scripts, i++);
  513. };
  514. }
  515. return; // NOTE: early return to chain async loading
  516. }
  517. }
  518. },
  519. /**
  520. * Brute force version of contains.
  521. * Used for browsers without contains support for non-HTMLElement Nodes (textNodes, etc).
  522. * @method _bruteContains
  523. * @private
  524. * @param {HTMLElement} element The containing html element.
  525. * @param {HTMLElement} needle The html element that may be contained.
  526. * @return {Boolean} Whether or not the element is or contains the needle.
  527. */
  528. _bruteContains: function(element, needle) {
  529. while (needle) {
  530. if (element === needle) {
  531. return true;
  532. }
  533. needle = needle.parentNode;
  534. }
  535. return false;
  536. },
  537. // TODO: move to Lang?
  538. /**
  539. * Memoizes dynamic regular expressions to boost runtime performance.
  540. * @method _getRegExp
  541. * @private
  542. * @param {String} str The string to convert to a regular expression.
  543. * @param {String} flags optional An optinal string of flags.
  544. * @return {RegExp} An instance of RegExp
  545. */
  546. _getRegExp: function(str, flags) {
  547. flags = flags || '';
  548. Y.DOM._regexCache = Y.DOM._regexCache || {};
  549. if (!Y.DOM._regexCache[str + flags]) {
  550. Y.DOM._regexCache[str + flags] = new RegExp(str, flags);
  551. }
  552. return Y.DOM._regexCache[str + flags];
  553. },
  554. // TODO: make getDoc/Win true privates?
  555. /**
  556. * returns the appropriate document.
  557. * @method _getDoc
  558. * @private
  559. * @param {HTMLElement} element optional Target element.
  560. * @return {Object} The document for the given element or the default document.
  561. */
  562. _getDoc: function(element) {
  563. element = element || {};
  564. return (element[NODE_TYPE] === 9) ? element : // element === document
  565. element[OWNER_DOCUMENT] || // element === DOM node
  566. element.document || // element === window
  567. Y.config.doc; // default
  568. },
  569. /**
  570. * returns the appropriate window.
  571. * @method _getWin
  572. * @private
  573. * @param {HTMLElement} element optional Target element.
  574. * @return {Object} The window for the given element or the default window.
  575. */
  576. _getWin: function(element) {
  577. var doc = Y.DOM._getDoc(element);
  578. return doc[DEFAULT_VIEW] || doc[PARENT_WINDOW] || Y.config.win;
  579. },
  580. // TODO: document this
  581. _childBy: function(element, tag, fn, rev) {
  582. var ret = null,
  583. root, axis;
  584. if (element) {
  585. if (rev) {
  586. root = element[LAST_CHILD];
  587. axis = PREVIOUS_SIBLING;
  588. } else {
  589. root = element[FIRST_CHILD];
  590. axis = NEXT_SIBLING;
  591. }
  592. if (Y.DOM._testElement(root, tag, fn)) { // is the matching element
  593. ret = root;
  594. } else { // need to scan nextSibling axis of firstChild to find matching element
  595. ret = Y.DOM.elementByAxis(root, axis, fn);
  596. }
  597. }
  598. return ret;
  599. },
  600. _testElement: function(element, tag, fn) {
  601. tag = (tag && tag !== '*') ? tag.toUpperCase() : null;
  602. return (element && element[TAG_NAME] &&
  603. (!tag || element[TAG_NAME].toUpperCase() === tag) &&
  604. (!fn || fn(element)));
  605. },
  606. creators: {},
  607. _IESimpleCreate: function(html, doc) {
  608. doc = doc || Y.config.doc;
  609. return doc.createElement(html);
  610. }
  611. };
  612. (function() {
  613. var creators = Y.DOM.creators,
  614. create = Y.DOM.create,
  615. re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
  616. TABLE_OPEN = '<table>',
  617. TABLE_CLOSE = '</table>';
  618. if (Y.UA.gecko || Y.UA.ie) { // require custom creation code for certain element types
  619. Y.mix(creators, {
  620. option: function(html, doc) {
  621. var frag = create('<select>' + html + '</select>');
  622. return frag[FIRST_CHILD];
  623. },
  624. tr: function(html, doc) {
  625. var frag = creators.tbody('<tbody>' + html + '</tbody>', doc);
  626. return frag[FIRST_CHILD];
  627. },
  628. td: function(html, doc) {
  629. var frag = creators.tr('<tr>' + html + '</tr>', doc);
  630. return frag[FIRST_CHILD];
  631. },
  632. tbody: function(html, doc) {
  633. var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc);
  634. return frag[FIRST_CHILD];
  635. },
  636. legend: 'fieldset'
  637. });
  638. creators.col = creators.tbody; // IE wraps in colgroup
  639. }
  640. if (Y.UA.ie) {
  641. creators.col = creators.link = Y.DOM._IESimpleCreate;
  642. Y.mix(creators, {
  643. // TODO: thead/tfoot with nested tbody
  644. tbody: function(html, doc) {
  645. var frag = create(TABLE_OPEN + html + TABLE_CLOSE, doc),
  646. tb = frag.children.tags('tbody')[0];
  647. if (frag.children[LENGTH] > 1 && tb && !re_tbody.test(html)) {
  648. tb[PARENT_NODE].removeChild(tb);
  649. }
  650. return frag;
  651. },
  652. script: function(html, doc) {
  653. var frag = doc.createElement('div');
  654. frag.innerHTML = '-' + html;
  655. return frag.removeChild(frag[FIRST_CHILD][NEXT_SIBLING]);
  656. }
  657. });
  658. Y.mix(Y.DOM.VALUE_GETTERS, {
  659. button: function(node) {
  660. return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
  661. }
  662. });
  663. Y.mix(Y.DOM.VALUE_SETTERS, {
  664. // IE: node.value changes the button text, which should be handled via innerHTML
  665. button: function(node, val) {
  666. var attr = node.attributes['value'];
  667. if (!attr) {
  668. attr = node[OWNER_DOCUMENT].createAttribute('value');
  669. node.setAttributeNode(attr);
  670. }
  671. attr.value = val;
  672. }
  673. });
  674. }
  675. if (Y.UA.gecko || Y.UA.ie) {
  676. Y.mix(creators, {
  677. th: creators.td,
  678. thead: creators.tbody,
  679. tfoot: creators.tbody,
  680. caption: creators.tbody,
  681. colgroup: creators.tbody,
  682. col: creators.tbody,
  683. optgroup: creators.option
  684. });
  685. }
  686. Y.mix(Y.DOM.VALUE_GETTERS, {
  687. option: function(node) {
  688. var attrs = node.attributes;
  689. return (attrs.value && attrs.value.specified) ? node.value : node.text;
  690. },
  691. select: function(node) {
  692. var val = node.value,
  693. options = node.options,
  694. i, opt;
  695. if (options && val === '') {
  696. if (node.multiple) {
  697. } else {
  698. val = Y.DOM.getValue(options[node.selectedIndex], 'value');
  699. }
  700. }
  701. return val;
  702. }
  703. });
  704. })();
  705. /**
  706. * The DOM utility provides a cross-browser abtraction layer
  707. * normalizing DOM tasks, and adds extra helper functionality
  708. * for other common tasks.
  709. * @module dom
  710. * @submodule dom-base
  711. * @for DOM
  712. */
  713. var CLASS_NAME = 'className';
  714. Y.mix(Y.DOM, {
  715. /**
  716. * Determines whether a DOM element has the given className.
  717. * @method hasClass
  718. * @param {HTMLElement} element The DOM element.
  719. * @param {String} className the class name to search for
  720. * @return {Boolean} Whether or not the element has the given class.
  721. */
  722. hasClass: function(node, className) {
  723. var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
  724. return re.test(node[CLASS_NAME]);
  725. },
  726. /**
  727. * Adds a class name to a given DOM element.
  728. * @method addClass
  729. * @param {HTMLElement} element The DOM element.
  730. * @param {String} className the class name to add to the class attribute
  731. */
  732. addClass: function(node, className) {
  733. if (!Y.DOM.hasClass(node, className)) { // skip if already present
  734. node[CLASS_NAME] = Y.Lang.trim([node[CLASS_NAME], className].join(' '));
  735. }
  736. },
  737. /**
  738. * Removes a class name from a given element.
  739. * @method removeClass
  740. * @param {HTMLElement} element The DOM element.
  741. * @param {String} className the class name to remove from the class attribute
  742. */
  743. removeClass: function(node, className) {
  744. if (className && Y.DOM.hasClass(node, className)) {
  745. node[CLASS_NAME] = Y.Lang.trim(node[CLASS_NAME].replace(Y.DOM._getRegExp('(?:^|\\s+)' +
  746. className + '(?:\\s+|$)'), ' '));
  747. if ( Y.DOM.hasClass(node, className) ) { // in case of multiple adjacent
  748. Y.DOM.removeClass(node, className);
  749. }
  750. }
  751. },
  752. /**
  753. * Replace a class with another class for a given element.
  754. * If no oldClassName is present, the newClassName is simply added.
  755. * @method replaceClass
  756. * @param {HTMLElement} element The DOM element.
  757. * @param {String} oldClassName the class name to be replaced
  758. * @param {String} newClassName the class name that will be replacing the old class name
  759. */
  760. replaceClass: function(node, oldC, newC) {
  761. Y.DOM.addClass(node, newC);
  762. Y.DOM.removeClass(node, oldC);
  763. },
  764. /**
  765. * If the className exists on the node it is removed, if it doesn't exist it is added.
  766. * @method toggleClass
  767. * @param {HTMLElement} element The DOM element.
  768. * @param {String} className the class name to be toggled
  769. */
  770. toggleClass: function(node, className) {
  771. if (Y.DOM.hasClass(node, className)) {
  772. Y.DOM.removeClass(node, className);
  773. } else {
  774. Y.DOM.addClass(node, className);
  775. }
  776. }
  777. });
  778. }, '@VERSION@' ,{requires:['event'], skinnable:false});