PageRenderTime 50ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/yuilib/3.9.1/build/dom-base/dom-base.js

https://bitbucket.org/kudutest1/moodlegit
JavaScript | 685 lines | 463 code | 100 blank | 122 comment | 128 complexity | 5f69b08a8976031352a4f2218ac4ddca MD5 | raw file
  1. /* YUI 3.9.1 (build 5852) Copyright 2013 Yahoo! Inc. http://yuilibrary.com/license/ */
  2. YUI.add('dom-base', function (Y, NAME) {
  3. /**
  4. * @for DOM
  5. * @module dom
  6. */
  7. var documentElement = Y.config.doc.documentElement,
  8. Y_DOM = Y.DOM,
  9. TAG_NAME = 'tagName',
  10. OWNER_DOCUMENT = 'ownerDocument',
  11. EMPTY_STRING = '',
  12. addFeature = Y.Features.add,
  13. testFeature = Y.Features.test;
  14. Y.mix(Y_DOM, {
  15. /**
  16. * Returns the text content of the HTMLElement.
  17. * @method getText
  18. * @param {HTMLElement} element The html element.
  19. * @return {String} The text content of the element (includes text of any descending elements).
  20. */
  21. getText: (documentElement.textContent !== undefined) ?
  22. function(element) {
  23. var ret = '';
  24. if (element) {
  25. ret = element.textContent;
  26. }
  27. return ret || '';
  28. } : function(element) {
  29. var ret = '';
  30. if (element) {
  31. ret = element.innerText || element.nodeValue; // might be a textNode
  32. }
  33. return ret || '';
  34. },
  35. /**
  36. * Sets the text content of the HTMLElement.
  37. * @method setText
  38. * @param {HTMLElement} element The html element.
  39. * @param {String} content The content to add.
  40. */
  41. setText: (documentElement.textContent !== undefined) ?
  42. function(element, content) {
  43. if (element) {
  44. element.textContent = content;
  45. }
  46. } : function(element, content) {
  47. if ('innerText' in element) {
  48. element.innerText = content;
  49. } else if ('nodeValue' in element) {
  50. element.nodeValue = content;
  51. }
  52. },
  53. CUSTOM_ATTRIBUTES: (!documentElement.hasAttribute) ? { // IE < 8
  54. 'for': 'htmlFor',
  55. 'class': 'className'
  56. } : { // w3c
  57. 'htmlFor': 'for',
  58. 'className': 'class'
  59. },
  60. /**
  61. * Provides a normalized attribute interface.
  62. * @method setAttribute
  63. * @param {HTMLElement} el The target element for the attribute.
  64. * @param {String} attr The attribute to set.
  65. * @param {String} val The value of the attribute.
  66. */
  67. setAttribute: function(el, attr, val, ieAttr) {
  68. if (el && attr && el.setAttribute) {
  69. attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr;
  70. el.setAttribute(attr, val, ieAttr);
  71. }
  72. },
  73. /**
  74. * Provides a normalized attribute interface.
  75. * @method getAttribute
  76. * @param {HTMLElement} el The target element for the attribute.
  77. * @param {String} attr The attribute to get.
  78. * @return {String} The current value of the attribute.
  79. */
  80. getAttribute: function(el, attr, ieAttr) {
  81. ieAttr = (ieAttr !== undefined) ? ieAttr : 2;
  82. var ret = '';
  83. if (el && attr && el.getAttribute) {
  84. attr = Y_DOM.CUSTOM_ATTRIBUTES[attr] || attr;
  85. ret = el.getAttribute(attr, ieAttr);
  86. if (ret === null) {
  87. ret = ''; // per DOM spec
  88. }
  89. }
  90. return ret;
  91. },
  92. VALUE_SETTERS: {},
  93. VALUE_GETTERS: {},
  94. getValue: function(node) {
  95. var ret = '', // TODO: return null?
  96. getter;
  97. if (node && node[TAG_NAME]) {
  98. getter = Y_DOM.VALUE_GETTERS[node[TAG_NAME].toLowerCase()];
  99. if (getter) {
  100. ret = getter(node);
  101. } else {
  102. ret = node.value;
  103. }
  104. }
  105. // workaround for IE8 JSON stringify bug
  106. // which converts empty string values to null
  107. if (ret === EMPTY_STRING) {
  108. ret = EMPTY_STRING; // for real
  109. }
  110. return (typeof ret === 'string') ? ret : '';
  111. },
  112. setValue: function(node, val) {
  113. var setter;
  114. if (node && node[TAG_NAME]) {
  115. setter = Y_DOM.VALUE_SETTERS[node[TAG_NAME].toLowerCase()];
  116. if (setter) {
  117. setter(node, val);
  118. } else {
  119. node.value = val;
  120. }
  121. }
  122. },
  123. creators: {}
  124. });
  125. addFeature('value-set', 'select', {
  126. test: function() {
  127. var node = Y.config.doc.createElement('select');
  128. node.innerHTML = '<option>1</option><option>2</option>';
  129. node.value = '2';
  130. return (node.value && node.value === '2');
  131. }
  132. });
  133. if (!testFeature('value-set', 'select')) {
  134. Y_DOM.VALUE_SETTERS.select = function(node, val) {
  135. for (var i = 0, options = node.getElementsByTagName('option'), option;
  136. option = options[i++];) {
  137. if (Y_DOM.getValue(option) === val) {
  138. option.selected = true;
  139. //Y_DOM.setAttribute(option, 'selected', 'selected');
  140. break;
  141. }
  142. }
  143. };
  144. }
  145. Y.mix(Y_DOM.VALUE_GETTERS, {
  146. button: function(node) {
  147. return (node.attributes && node.attributes.value) ? node.attributes.value.value : '';
  148. }
  149. });
  150. Y.mix(Y_DOM.VALUE_SETTERS, {
  151. // IE: node.value changes the button text, which should be handled via innerHTML
  152. button: function(node, val) {
  153. var attr = node.attributes.value;
  154. if (!attr) {
  155. attr = node[OWNER_DOCUMENT].createAttribute('value');
  156. node.setAttributeNode(attr);
  157. }
  158. attr.value = val;
  159. }
  160. });
  161. Y.mix(Y_DOM.VALUE_GETTERS, {
  162. option: function(node) {
  163. var attrs = node.attributes;
  164. return (attrs.value && attrs.value.specified) ? node.value : node.text;
  165. },
  166. select: function(node) {
  167. var val = node.value,
  168. options = node.options;
  169. if (options && options.length) {
  170. // TODO: implement multipe select
  171. if (node.multiple) {
  172. } else if (node.selectedIndex > -1) {
  173. val = Y_DOM.getValue(options[node.selectedIndex]);
  174. }
  175. }
  176. return val;
  177. }
  178. });
  179. var addClass, hasClass, removeClass;
  180. Y.mix(Y.DOM, {
  181. /**
  182. * Determines whether a DOM element has the given className.
  183. * @method hasClass
  184. * @for DOM
  185. * @param {HTMLElement} element The DOM element.
  186. * @param {String} className the class name to search for
  187. * @return {Boolean} Whether or not the element has the given class.
  188. */
  189. hasClass: function(node, className) {
  190. var re = Y.DOM._getRegExp('(?:^|\\s+)' + className + '(?:\\s+|$)');
  191. return re.test(node.className);
  192. },
  193. /**
  194. * Adds a class name to a given DOM element.
  195. * @method addClass
  196. * @for DOM
  197. * @param {HTMLElement} element The DOM element.
  198. * @param {String} className the class name to add to the class attribute
  199. */
  200. addClass: function(node, className) {
  201. if (!Y.DOM.hasClass(node, className)) { // skip if already present
  202. node.className = Y.Lang.trim([node.className, className].join(' '));
  203. }
  204. },
  205. /**
  206. * Removes a class name from a given element.
  207. * @method removeClass
  208. * @for DOM
  209. * @param {HTMLElement} element The DOM element.
  210. * @param {String} className the class name to remove from the class attribute
  211. */
  212. removeClass: function(node, className) {
  213. if (className && hasClass(node, className)) {
  214. node.className = Y.Lang.trim(node.className.replace(Y.DOM._getRegExp('(?:^|\\s+)' +
  215. className + '(?:\\s+|$)'), ' '));
  216. if ( hasClass(node, className) ) { // in case of multiple adjacent
  217. removeClass(node, className);
  218. }
  219. }
  220. },
  221. /**
  222. * Replace a class with another class for a given element.
  223. * If no oldClassName is present, the newClassName is simply added.
  224. * @method replaceClass
  225. * @for DOM
  226. * @param {HTMLElement} element The DOM element
  227. * @param {String} oldClassName the class name to be replaced
  228. * @param {String} newClassName the class name that will be replacing the old class name
  229. */
  230. replaceClass: function(node, oldC, newC) {
  231. removeClass(node, oldC); // remove first in case oldC === newC
  232. addClass(node, newC);
  233. },
  234. /**
  235. * If the className exists on the node it is removed, if it doesn't exist it is added.
  236. * @method toggleClass
  237. * @for DOM
  238. * @param {HTMLElement} element The DOM element
  239. * @param {String} className the class name to be toggled
  240. * @param {Boolean} addClass optional boolean to indicate whether class
  241. * should be added or removed regardless of current state
  242. */
  243. toggleClass: function(node, className, force) {
  244. var add = (force !== undefined) ? force :
  245. !(hasClass(node, className));
  246. if (add) {
  247. addClass(node, className);
  248. } else {
  249. removeClass(node, className);
  250. }
  251. }
  252. });
  253. hasClass = Y.DOM.hasClass;
  254. removeClass = Y.DOM.removeClass;
  255. addClass = Y.DOM.addClass;
  256. var re_tag = /<([a-z]+)/i,
  257. Y_DOM = Y.DOM,
  258. addFeature = Y.Features.add,
  259. testFeature = Y.Features.test,
  260. creators = {},
  261. createFromDIV = function(html, tag) {
  262. var div = Y.config.doc.createElement('div'),
  263. ret = true;
  264. div.innerHTML = html;
  265. if (!div.firstChild || div.firstChild.tagName !== tag.toUpperCase()) {
  266. ret = false;
  267. }
  268. return ret;
  269. },
  270. re_tbody = /(?:\/(?:thead|tfoot|tbody|caption|col|colgroup)>)+\s*<tbody/,
  271. TABLE_OPEN = '<table>',
  272. TABLE_CLOSE = '</table>';
  273. Y.mix(Y.DOM, {
  274. _fragClones: {},
  275. _create: function(html, doc, tag) {
  276. tag = tag || 'div';
  277. var frag = Y_DOM._fragClones[tag];
  278. if (frag) {
  279. frag = frag.cloneNode(false);
  280. } else {
  281. frag = Y_DOM._fragClones[tag] = doc.createElement(tag);
  282. }
  283. frag.innerHTML = html;
  284. return frag;
  285. },
  286. _children: function(node, tag) {
  287. var i = 0,
  288. children = node.children,
  289. childNodes,
  290. hasComments,
  291. child;
  292. if (children && children.tags) { // use tags filter when possible
  293. if (tag) {
  294. children = node.children.tags(tag);
  295. } else { // IE leaks comments into children
  296. hasComments = children.tags('!').length;
  297. }
  298. }
  299. if (!children || (!children.tags && tag) || hasComments) {
  300. childNodes = children || node.childNodes;
  301. children = [];
  302. while ((child = childNodes[i++])) {
  303. if (child.nodeType === 1) {
  304. if (!tag || tag === child.tagName) {
  305. children.push(child);
  306. }
  307. }
  308. }
  309. }
  310. return children || [];
  311. },
  312. /**
  313. * Creates a new dom node using the provided markup string.
  314. * @method create
  315. * @param {String} html The markup used to create the element
  316. * @param {HTMLDocument} doc An optional document context
  317. * @return {HTMLElement|DocumentFragment} returns a single HTMLElement
  318. * when creating one node, and a documentFragment when creating
  319. * multiple nodes.
  320. */
  321. create: function(html, doc) {
  322. if (typeof html === 'string') {
  323. html = Y.Lang.trim(html); // match IE which trims whitespace from innerHTML
  324. }
  325. doc = doc || Y.config.doc;
  326. var m = re_tag.exec(html),
  327. create = Y_DOM._create,
  328. custom = creators,
  329. ret = null,
  330. creator,
  331. tag, nodes;
  332. if (html != undefined) { // not undefined or null
  333. if (m && m[1]) {
  334. creator = custom[m[1].toLowerCase()];
  335. if (typeof creator === 'function') {
  336. create = creator;
  337. } else {
  338. tag = creator;
  339. }
  340. }
  341. nodes = create(html, doc, tag).childNodes;
  342. if (nodes.length === 1) { // return single node, breaking parentNode ref from "fragment"
  343. ret = nodes[0].parentNode.removeChild(nodes[0]);
  344. } else if (nodes[0] && nodes[0].className === 'yui3-big-dummy') { // using dummy node to preserve some attributes (e.g. OPTION not selected)
  345. if (nodes.length === 2) {
  346. ret = nodes[0].nextSibling;
  347. } else {
  348. nodes[0].parentNode.removeChild(nodes[0]);
  349. ret = Y_DOM._nl2frag(nodes, doc);
  350. }
  351. } else { // return multiple nodes as a fragment
  352. ret = Y_DOM._nl2frag(nodes, doc);
  353. }
  354. }
  355. return ret;
  356. },
  357. _nl2frag: function(nodes, doc) {
  358. var ret = null,
  359. i, len;
  360. if (nodes && (nodes.push || nodes.item) && nodes[0]) {
  361. doc = doc || nodes[0].ownerDocument;
  362. ret = doc.createDocumentFragment();
  363. if (nodes.item) { // convert live list to static array
  364. nodes = Y.Array(nodes, 0, true);
  365. }
  366. for (i = 0, len = nodes.length; i < len; i++) {
  367. ret.appendChild(nodes[i]);
  368. }
  369. } // else inline with log for minification
  370. return ret;
  371. },
  372. /**
  373. * Inserts content in a node at the given location
  374. * @method addHTML
  375. * @param {HTMLElement} node The node to insert into
  376. * @param {HTMLElement | Array | HTMLCollection} content The content to be inserted
  377. * @param {HTMLElement} where Where to insert the content
  378. * If no "where" is given, content is appended to the node
  379. * Possible values for "where"
  380. * <dl>
  381. * <dt>HTMLElement</dt>
  382. * <dd>The element to insert before</dd>
  383. * <dt>"replace"</dt>
  384. * <dd>Replaces the existing HTML</dd>
  385. * <dt>"before"</dt>
  386. * <dd>Inserts before the existing HTML</dd>
  387. * <dt>"before"</dt>
  388. * <dd>Inserts content before the node</dd>
  389. * <dt>"after"</dt>
  390. * <dd>Inserts content after the node</dd>
  391. * </dl>
  392. */
  393. addHTML: function(node, content, where) {
  394. var nodeParent = node.parentNode,
  395. i = 0,
  396. item,
  397. ret = content,
  398. newNode;
  399. if (content != undefined) { // not null or undefined (maybe 0)
  400. if (content.nodeType) { // DOM node, just add it
  401. newNode = content;
  402. } else if (typeof content == 'string' || typeof content == 'number') {
  403. ret = newNode = Y_DOM.create(content);
  404. } else if (content[0] && content[0].nodeType) { // array or collection
  405. newNode = Y.config.doc.createDocumentFragment();
  406. while ((item = content[i++])) {
  407. newNode.appendChild(item); // append to fragment for insertion
  408. }
  409. }
  410. }
  411. if (where) {
  412. if (newNode && where.parentNode) { // insert regardless of relationship to node
  413. where.parentNode.insertBefore(newNode, where);
  414. } else {
  415. switch (where) {
  416. case 'replace':
  417. while (node.firstChild) {
  418. node.removeChild(node.firstChild);
  419. }
  420. if (newNode) { // allow empty content to clear node
  421. node.appendChild(newNode);
  422. }
  423. break;
  424. case 'before':
  425. if (newNode) {
  426. nodeParent.insertBefore(newNode, node);
  427. }
  428. break;
  429. case 'after':
  430. if (newNode) {
  431. if (node.nextSibling) { // IE errors if refNode is null
  432. nodeParent.insertBefore(newNode, node.nextSibling);
  433. } else {
  434. nodeParent.appendChild(newNode);
  435. }
  436. }
  437. break;
  438. default:
  439. if (newNode) {
  440. node.appendChild(newNode);
  441. }
  442. }
  443. }
  444. } else if (newNode) {
  445. node.appendChild(newNode);
  446. }
  447. return ret;
  448. },
  449. wrap: function(node, html) {
  450. var parent = (html && html.nodeType) ? html : Y.DOM.create(html),
  451. nodes = parent.getElementsByTagName('*');
  452. if (nodes.length) {
  453. parent = nodes[nodes.length - 1];
  454. }
  455. if (node.parentNode) {
  456. node.parentNode.replaceChild(parent, node);
  457. }
  458. parent.appendChild(node);
  459. },
  460. unwrap: function(node) {
  461. var parent = node.parentNode,
  462. lastChild = parent.lastChild,
  463. next = node,
  464. grandparent;
  465. if (parent) {
  466. grandparent = parent.parentNode;
  467. if (grandparent) {
  468. node = parent.firstChild;
  469. while (node !== lastChild) {
  470. next = node.nextSibling;
  471. grandparent.insertBefore(node, parent);
  472. node = next;
  473. }
  474. grandparent.replaceChild(lastChild, parent);
  475. } else {
  476. parent.removeChild(node);
  477. }
  478. }
  479. }
  480. });
  481. addFeature('innerhtml', 'table', {
  482. test: function() {
  483. var node = Y.config.doc.createElement('table');
  484. try {
  485. node.innerHTML = '<tbody></tbody>';
  486. } catch(e) {
  487. return false;
  488. }
  489. return (node.firstChild && node.firstChild.nodeName === 'TBODY');
  490. }
  491. });
  492. addFeature('innerhtml-div', 'tr', {
  493. test: function() {
  494. return createFromDIV('<tr></tr>', 'tr');
  495. }
  496. });
  497. addFeature('innerhtml-div', 'script', {
  498. test: function() {
  499. return createFromDIV('<script></script>', 'script');
  500. }
  501. });
  502. if (!testFeature('innerhtml', 'table')) {
  503. // TODO: thead/tfoot with nested tbody
  504. // IE adds TBODY when creating TABLE elements (which may share this impl)
  505. creators.tbody = function(html, doc) {
  506. var frag = Y_DOM.create(TABLE_OPEN + html + TABLE_CLOSE, doc),
  507. tb = Y.DOM._children(frag, 'tbody')[0];
  508. if (frag.children.length > 1 && tb && !re_tbody.test(html)) {
  509. tb.parentNode.removeChild(tb); // strip extraneous tbody
  510. }
  511. return frag;
  512. };
  513. }
  514. if (!testFeature('innerhtml-div', 'script')) {
  515. creators.script = function(html, doc) {
  516. var frag = doc.createElement('div');
  517. frag.innerHTML = '-' + html;
  518. frag.removeChild(frag.firstChild);
  519. return frag;
  520. };
  521. creators.link = creators.style = creators.script;
  522. }
  523. if (!testFeature('innerhtml-div', 'tr')) {
  524. Y.mix(creators, {
  525. option: function(html, doc) {
  526. return Y_DOM.create('<select><option class="yui3-big-dummy" selected></option>' + html + '</select>', doc);
  527. },
  528. tr: function(html, doc) {
  529. return Y_DOM.create('<tbody>' + html + '</tbody>', doc);
  530. },
  531. td: function(html, doc) {
  532. return Y_DOM.create('<tr>' + html + '</tr>', doc);
  533. },
  534. col: function(html, doc) {
  535. return Y_DOM.create('<colgroup>' + html + '</colgroup>', doc);
  536. },
  537. tbody: 'table'
  538. });
  539. Y.mix(creators, {
  540. legend: 'fieldset',
  541. th: creators.td,
  542. thead: creators.tbody,
  543. tfoot: creators.tbody,
  544. caption: creators.tbody,
  545. colgroup: creators.tbody,
  546. optgroup: creators.option
  547. });
  548. }
  549. Y_DOM.creators = creators;
  550. Y.mix(Y.DOM, {
  551. /**
  552. * Sets the width of the element to the given size, regardless
  553. * of box model, border, padding, etc.
  554. * @method setWidth
  555. * @param {HTMLElement} element The DOM element.
  556. * @param {String|Number} size The pixel height to size to
  557. */
  558. setWidth: function(node, size) {
  559. Y.DOM._setSize(node, 'width', size);
  560. },
  561. /**
  562. * Sets the height of the element to the given size, regardless
  563. * of box model, border, padding, etc.
  564. * @method setHeight
  565. * @param {HTMLElement} element The DOM element.
  566. * @param {String|Number} size The pixel height to size to
  567. */
  568. setHeight: function(node, size) {
  569. Y.DOM._setSize(node, 'height', size);
  570. },
  571. _setSize: function(node, prop, val) {
  572. val = (val > 0) ? val : 0;
  573. var size = 0;
  574. node.style[prop] = val + 'px';
  575. size = (prop === 'height') ? node.offsetHeight : node.offsetWidth;
  576. if (size > val) {
  577. val = val - (size - val);
  578. if (val < 0) {
  579. val = 0;
  580. }
  581. node.style[prop] = val + 'px';
  582. }
  583. }
  584. });
  585. }, '3.9.1', {"requires": ["dom-core"]});