API Docs for: 3.18.1
Show:

File: node/js/node-core.js

  1. /**
  2. * The Node Utility provides a DOM-like interface for interacting with DOM nodes.
  3. * @module node
  4. * @main node
  5. * @submodule node-core
  6. */
  7.  
  8. /**
  9. * The Node class provides a wrapper for manipulating DOM Nodes.
  10. * Node properties can be accessed via the set/get methods.
  11. * Use `Y.one()` to retrieve Node instances.
  12. *
  13. * <strong>NOTE:</strong> Node properties are accessed using
  14. * the <code>set</code> and <code>get</code> methods.
  15. *
  16. * @class Node
  17. * @constructor
  18. * @param {HTMLElement} node the DOM node to be mapped to the Node instance.
  19. * @uses EventTarget
  20. */
  21.  
  22. // "globals"
  23. var DOT = '.',
  24. NODE_NAME = 'nodeName',
  25. NODE_TYPE = 'nodeType',
  26. OWNER_DOCUMENT = 'ownerDocument',
  27. TAG_NAME = 'tagName',
  28. UID = '_yuid',
  29. EMPTY_OBJ = {},
  30.  
  31. _slice = Array.prototype.slice,
  32.  
  33. Y_DOM = Y.DOM,
  34.  
  35. Y_Node = function(node) {
  36. if (!this.getDOMNode) { // support optional "new"
  37. return new Y_Node(node);
  38. }
  39.  
  40. if (typeof node == 'string') {
  41. node = Y_Node._fromString(node);
  42. if (!node) {
  43. return null; // NOTE: return
  44. }
  45. }
  46.  
  47. var uid = (node.nodeType !== 9) ? node.uniqueID : node[UID];
  48.  
  49. if (uid && Y_Node._instances[uid] && Y_Node._instances[uid]._node !== node) {
  50. node[UID] = null; // unset existing uid to prevent collision (via clone or hack)
  51. }
  52.  
  53. uid = uid || Y.stamp(node);
  54. if (!uid) { // stamp failed; likely IE non-HTMLElement
  55. uid = Y.guid();
  56. }
  57.  
  58. this[UID] = uid;
  59.  
  60. /**
  61. * The underlying DOM node bound to the Y.Node instance
  62. * @property _node
  63. * @type HTMLElement
  64. * @private
  65. */
  66. this._node = node;
  67.  
  68. this._stateProxy = node; // when augmented with Attribute
  69.  
  70. if (this._initPlugins) { // when augmented with Plugin.Host
  71. this._initPlugins();
  72. }
  73. },
  74.  
  75. // used with previous/next/ancestor tests
  76. _wrapFn = function(fn) {
  77. var ret = null;
  78. if (fn) {
  79. ret = (typeof fn == 'string') ?
  80. function(n) {
  81. return Y.Selector.test(n, fn);
  82. } :
  83. function(n) {
  84. return fn(Y.one(n));
  85. };
  86. }
  87.  
  88. return ret;
  89. };
  90. // end "globals"
  91.  
  92. Y_Node.ATTRS = {};
  93. Y_Node.DOM_EVENTS = {};
  94.  
  95. Y_Node._fromString = function(node) {
  96. if (node) {
  97. if (node.indexOf('doc') === 0) { // doc OR document
  98. node = Y.config.doc;
  99. } else if (node.indexOf('win') === 0) { // win OR window
  100. node = Y.config.win;
  101. } else {
  102. node = Y.Selector.query(node, null, true);
  103. }
  104. }
  105.  
  106. return node || null;
  107. };
  108.  
  109. /**
  110. * The name of the component
  111. * @static
  112. * @type String
  113. * @property NAME
  114. */
  115. Y_Node.NAME = 'node';
  116.  
  117. /*
  118. * The pattern used to identify ARIA attributes
  119. */
  120. Y_Node.re_aria = /^(?:role$|aria-)/;
  121.  
  122. Y_Node.SHOW_TRANSITION = 'fadeIn';
  123. Y_Node.HIDE_TRANSITION = 'fadeOut';
  124.  
  125. /**
  126. * A list of Node instances that have been created
  127. * @private
  128. * @type Object
  129. * @property _instances
  130. * @static
  131. *
  132. */
  133. Y_Node._instances = {};
  134.  
  135. /**
  136. * Retrieves the DOM node bound to a Node instance
  137. * @method getDOMNode
  138. * @static
  139. *
  140. * @param {Node|HTMLElement} node The Node instance or an HTMLElement
  141. * @return {HTMLElement} The DOM node bound to the Node instance. If a DOM node is passed
  142. * as the node argument, it is simply returned.
  143. */
  144. Y_Node.getDOMNode = function(node) {
  145. if (node) {
  146. return (node.nodeType) ? node : node._node || null;
  147. }
  148. return null;
  149. };
  150.  
  151. /**
  152. * Checks Node return values and wraps DOM Nodes as Y.Node instances
  153. * and DOM Collections / Arrays as Y.NodeList instances.
  154. * Other return values just pass thru. If undefined is returned (e.g. no return)
  155. * then the Node instance is returned for chainability.
  156. * @method scrubVal
  157. * @static
  158. *
  159. * @param {HTMLElement|HTMLElement[]|Node} node The Node instance or an HTMLElement
  160. * @return {Node | NodeList | Any} Depends on what is returned from the DOM node.
  161. */
  162. Y_Node.scrubVal = function(val, node) {
  163. if (val) { // only truthy values are risky
  164. if (typeof val == 'object' || typeof val == 'function') { // safari nodeList === function
  165. if (NODE_TYPE in val || Y_DOM.isWindow(val)) {// node || window
  166. val = Y.one(val);
  167. } else if ((val.item && !val._nodes) || // dom collection or Node instance
  168. (val[0] && val[0][NODE_TYPE])) { // array of DOM Nodes
  169. val = Y.all(val);
  170. }
  171. }
  172. } else if (typeof val === 'undefined') {
  173. val = node; // for chaining
  174. } else if (val === null) {
  175. val = null; // IE: DOM null not the same as null
  176. }
  177.  
  178. return val;
  179. };
  180.  
  181. /**
  182. * Adds methods to the Y.Node prototype, routing through scrubVal.
  183. * @method addMethod
  184. * @static
  185. *
  186. * @param {String} name The name of the method to add
  187. * @param {Function} fn The function that becomes the method
  188. * @param {Object} context An optional context to call the method with
  189. * (defaults to the Node instance)
  190. * @return {any} Depends on what is returned from the DOM node.
  191. */
  192. Y_Node.addMethod = function(name, fn, context) {
  193. if (name && fn && typeof fn == 'function') {
  194. Y_Node.prototype[name] = function() {
  195. var args = _slice.call(arguments),
  196. node = this,
  197. ret;
  198.  
  199. if (args[0] && args[0]._node) {
  200. args[0] = args[0]._node;
  201. }
  202.  
  203. if (args[1] && args[1]._node) {
  204. args[1] = args[1]._node;
  205. }
  206. args.unshift(node._node);
  207.  
  208. ret = fn.apply(context || node, args);
  209.  
  210. if (ret) { // scrub truthy
  211. ret = Y_Node.scrubVal(ret, node);
  212. }
  213.  
  214. (typeof ret != 'undefined') || (ret = node);
  215. return ret;
  216. };
  217. } else {
  218. Y.log('unable to add method: ' + name, 'warn', 'Node');
  219. }
  220. };
  221.  
  222. /**
  223. * Imports utility methods to be added as Y.Node methods.
  224. * @method importMethod
  225. * @static
  226. *
  227. * @param {Object} host The object that contains the method to import.
  228. * @param {String} name The name of the method to import
  229. * @param {String} altName An optional name to use in place of the host name
  230. * @param {Object} context An optional context to call the method with
  231. */
  232. Y_Node.importMethod = function(host, name, altName) {
  233. if (typeof name == 'string') {
  234. altName = altName || name;
  235. Y_Node.addMethod(altName, host[name], host);
  236. } else {
  237. Y.Array.each(name, function(n) {
  238. Y_Node.importMethod(host, n);
  239. });
  240. }
  241. };
  242.  
  243. /**
  244. * Retrieves a NodeList based on the given CSS selector.
  245. * @method all
  246. *
  247. * @param {string} selector The CSS selector to test against.
  248. * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
  249. * @for YUI
  250. */
  251.  
  252. /**
  253. * Returns a single Node instance bound to the node or the
  254. * first element matching the given selector. Returns null if no match found.
  255. * <strong>Note:</strong> For chaining purposes you may want to
  256. * use <code>Y.all</code>, which returns a NodeList when no match is found.
  257. * @method one
  258. * @param {String | HTMLElement} node a node or Selector
  259. * @return {Node | null} a Node instance or null if no match found.
  260. * @for YUI
  261. */
  262.  
  263. /**
  264. * Returns a single Node instance bound to the node or the
  265. * first element matching the given selector. Returns null if no match found.
  266. * <strong>Note:</strong> For chaining purposes you may want to
  267. * use <code>Y.all</code>, which returns a NodeList when no match is found.
  268. * @method one
  269. * @static
  270. * @param {String | HTMLElement} node a node or Selector
  271. * @return {Node | null} a Node instance or null if no match found.
  272. * @for Node
  273. */
  274. Y_Node.one = function(node) {
  275. var instance = null,
  276. cachedNode,
  277. uid;
  278.  
  279. if (node) {
  280. if (typeof node == 'string') {
  281. node = Y_Node._fromString(node);
  282. if (!node) {
  283. return null; // NOTE: return
  284. }
  285. } else if (node.getDOMNode) {
  286. return node; // NOTE: return
  287. }
  288.  
  289. if (node.nodeType || Y.DOM.isWindow(node)) { // avoid bad input (numbers, boolean, etc)
  290. uid = (node.uniqueID && node.nodeType !== 9) ? node.uniqueID : node._yuid;
  291. instance = Y_Node._instances[uid]; // reuse exising instances
  292. cachedNode = instance ? instance._node : null;
  293. if (!instance || (cachedNode && node !== cachedNode)) { // new Node when nodes don't match
  294. instance = new Y_Node(node);
  295. if (node.nodeType != 11) { // dont cache document fragment
  296. Y_Node._instances[instance[UID]] = instance; // cache node
  297. }
  298. }
  299. }
  300. }
  301.  
  302. return instance;
  303. };
  304.  
  305. /**
  306. * The default setter for DOM properties
  307. * Called with instance context (this === the Node instance)
  308. * @method DEFAULT_SETTER
  309. * @static
  310. * @param {String} name The attribute/property being set
  311. * @param {any} val The value to be set
  312. * @return {any} The value
  313. */
  314. Y_Node.DEFAULT_SETTER = function(name, val) {
  315. var node = this._stateProxy,
  316. strPath;
  317.  
  318. if (name.indexOf(DOT) > -1) {
  319. strPath = name;
  320. name = name.split(DOT);
  321. // only allow when defined on node
  322. Y.Object.setValue(node, name, val);
  323. } else if (typeof node[name] != 'undefined') { // pass thru DOM properties
  324. node[name] = val;
  325. }
  326.  
  327. return val;
  328. };
  329.  
  330. /**
  331. * The default getter for DOM properties
  332. * Called with instance context (this === the Node instance)
  333. * @method DEFAULT_GETTER
  334. * @static
  335. * @param {String} name The attribute/property to look up
  336. * @return {any} The current value
  337. */
  338. Y_Node.DEFAULT_GETTER = function(name) {
  339. var node = this._stateProxy,
  340. val;
  341.  
  342. if (name.indexOf && name.indexOf(DOT) > -1) {
  343. val = Y.Object.getValue(node, name.split(DOT));
  344. } else if (typeof node[name] != 'undefined') { // pass thru from DOM
  345. val = node[name];
  346. }
  347.  
  348. return val;
  349. };
  350.  
  351. Y.mix(Y_Node.prototype, {
  352. DATA_PREFIX: 'data-',
  353.  
  354. /**
  355. * The method called when outputting Node instances as strings
  356. * @method toString
  357. * @return {String} A string representation of the Node instance
  358. */
  359. toString: function() {
  360. var str = this[UID] + ': not bound to a node',
  361. node = this._node,
  362. attrs, id, className;
  363.  
  364. if (node) {
  365. attrs = node.attributes;
  366. id = (attrs && attrs.id) ? node.getAttribute('id') : null;
  367. className = (attrs && attrs.className) ? node.getAttribute('className') : null;
  368. str = node[NODE_NAME];
  369.  
  370. if (id) {
  371. str += '#' + id;
  372. }
  373.  
  374. if (className) {
  375. str += '.' + className.replace(' ', '.');
  376. }
  377.  
  378. // TODO: add yuid?
  379. str += ' ' + this[UID];
  380. }
  381. return str;
  382. },
  383.  
  384. /**
  385. * Returns an attribute value on the Node instance.
  386. * Unless pre-configured (via `Node.ATTRS`), get hands
  387. * off to the underlying DOM node. Only valid
  388. * attributes/properties for the node will be queried.
  389. * @method get
  390. * @param {String} attr The attribute
  391. * @return {any} The current value of the attribute
  392. */
  393. get: function(attr) {
  394. var val;
  395.  
  396. if (this._getAttr) { // use Attribute imple
  397. val = this._getAttr(attr);
  398. } else {
  399. val = this._get(attr);
  400. }
  401.  
  402. if (val) {
  403. val = Y_Node.scrubVal(val, this);
  404. } else if (val === null) {
  405. val = null; // IE: DOM null is not true null (even though they ===)
  406. }
  407. return val;
  408. },
  409.  
  410. /**
  411. * Helper method for get.
  412. * @method _get
  413. * @private
  414. * @param {String} attr The attribute
  415. * @return {any} The current value of the attribute
  416. */
  417. _get: function(attr) {
  418. var attrConfig = Y_Node.ATTRS[attr],
  419. val;
  420.  
  421. if (attrConfig && attrConfig.getter) {
  422. val = attrConfig.getter.call(this);
  423. } else if (Y_Node.re_aria.test(attr)) {
  424. val = this._node.getAttribute(attr, 2);
  425. } else {
  426. val = Y_Node.DEFAULT_GETTER.apply(this, arguments);
  427. }
  428.  
  429. return val;
  430. },
  431.  
  432. /**
  433. * Sets an attribute on the Node instance.
  434. * Unless pre-configured (via Node.ATTRS), set hands
  435. * off to the underlying DOM node. Only valid
  436. * attributes/properties for the node will be set.
  437. * To set custom attributes use setAttribute.
  438. * @method set
  439. * @param {String} attr The attribute to be set.
  440. * @param {any} val The value to set the attribute to.
  441. * @chainable
  442. */
  443. set: function(attr, val) {
  444. var attrConfig = Y_Node.ATTRS[attr];
  445.  
  446. if (this._setAttr) { // use Attribute imple
  447. this._setAttr.apply(this, arguments);
  448. } else { // use setters inline
  449. if (attrConfig && attrConfig.setter) {
  450. attrConfig.setter.call(this, val, attr);
  451. } else if (Y_Node.re_aria.test(attr)) { // special case Aria
  452. this._node.setAttribute(attr, val);
  453. } else {
  454. Y_Node.DEFAULT_SETTER.apply(this, arguments);
  455. }
  456. }
  457.  
  458. return this;
  459. },
  460.  
  461. /**
  462. * Sets multiple attributes.
  463. * @method setAttrs
  464. * @param {Object} attrMap an object of name/value pairs to set
  465. * @chainable
  466. */
  467. setAttrs: function(attrMap) {
  468. if (this._setAttrs) { // use Attribute imple
  469. this._setAttrs(attrMap);
  470. } else { // use setters inline
  471. Y.Object.each(attrMap, function(v, n) {
  472. this.set(n, v);
  473. }, this);
  474. }
  475.  
  476. return this;
  477. },
  478.  
  479. /**
  480. * Returns an object containing the values for the requested attributes.
  481. * @method getAttrs
  482. * @param {Array} attrs an array of attributes to get values
  483. * @return {Object} An object with attribute name/value pairs.
  484. */
  485. getAttrs: function(attrs) {
  486. var ret = {};
  487. if (this._getAttrs) { // use Attribute imple
  488. this._getAttrs(attrs);
  489. } else { // use setters inline
  490. Y.Array.each(attrs, function(v, n) {
  491. ret[v] = this.get(v);
  492. }, this);
  493. }
  494.  
  495. return ret;
  496. },
  497.  
  498. /**
  499. * Compares nodes to determine if they match.
  500. * Node instances can be compared to each other and/or HTMLElements.
  501. * @method compareTo
  502. * @param {HTMLElement | Node} refNode The reference node to compare to the node.
  503. * @return {Boolean} True if the nodes match, false if they do not.
  504. */
  505. compareTo: function(refNode) {
  506. var node = this._node;
  507.  
  508. if (refNode && refNode._node) {
  509. refNode = refNode._node;
  510. }
  511. return node === refNode;
  512. },
  513.  
  514. /**
  515. * Determines whether the node is appended to the document.
  516. * @method inDoc
  517. * @param {Node|HTMLElement} doc optional An optional document to check against.
  518. * Defaults to current document.
  519. * @return {Boolean} Whether or not this node is appended to the document.
  520. */
  521. inDoc: function(doc) {
  522. var node = this._node;
  523.  
  524. if (node) {
  525. doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT];
  526. if (doc.documentElement) {
  527. return Y_DOM.contains(doc.documentElement, node);
  528. }
  529. }
  530.  
  531. return false;
  532. },
  533.  
  534. getById: function(id) {
  535. var node = this._node,
  536. ret = Y_DOM.byId(id, node[OWNER_DOCUMENT]);
  537. if (ret && Y_DOM.contains(node, ret)) {
  538. ret = Y.one(ret);
  539. } else {
  540. ret = null;
  541. }
  542. return ret;
  543. },
  544.  
  545. /**
  546. * Returns the nearest ancestor that passes the test applied by supplied boolean method.
  547. * @method ancestor
  548. * @param {String | Function} fn A selector string or boolean method for testing elements.
  549. * If a function is used, it receives the current node being tested as the only argument.
  550. * If fn is not passed as an argument, the parent node will be returned.
  551. * @param {Boolean} testSelf optional Whether or not to include the element in the scan
  552. * @param {String | Function} stopFn optional A selector string or boolean
  553. * method to indicate when the search should stop. The search bails when the function
  554. * returns true or the selector matches.
  555. * If a function is used, it receives the current node being tested as the only argument.
  556. * @return {Node} The matching Node instance or null if not found
  557. */
  558. ancestor: function(fn, testSelf, stopFn) {
  559. // testSelf is optional, check for stopFn as 2nd arg
  560. if (arguments.length === 2 &&
  561. (typeof testSelf == 'string' || typeof testSelf == 'function')) {
  562. stopFn = testSelf;
  563. }
  564.  
  565. return Y.one(Y_DOM.ancestor(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn)));
  566. },
  567.  
  568. /**
  569. * Returns the ancestors that pass the test applied by supplied boolean method.
  570. * @method ancestors
  571. * @param {String | Function} fn A selector string or boolean method for testing elements.
  572. * @param {Boolean} testSelf optional Whether or not to include the element in the scan
  573. * If a function is used, it receives the current node being tested as the only argument.
  574. * @return {NodeList} A NodeList instance containing the matching elements
  575. */
  576. ancestors: function(fn, testSelf, stopFn) {
  577. if (arguments.length === 2 &&
  578. (typeof testSelf == 'string' || typeof testSelf == 'function')) {
  579. stopFn = testSelf;
  580. }
  581. return Y.all(Y_DOM.ancestors(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn)));
  582. },
  583.  
  584. /**
  585. * Returns the previous matching sibling.
  586. * Returns the nearest element node sibling if no method provided.
  587. * @method previous
  588. * @param {String | Function} fn A selector or boolean method for testing elements.
  589. * If a function is used, it receives the current node being tested as the only argument.
  590. * @param {Boolean} [all] Whether text nodes as well as element nodes should be returned, or
  591. * just element nodes will be returned(default)
  592. * @return {Node} Node instance or null if not found
  593. */
  594. previous: function(fn, all) {
  595. return Y.one(Y_DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all));
  596. },
  597.  
  598. /**
  599. * Returns the next matching sibling.
  600. * Returns the nearest element node sibling if no method provided.
  601. * @method next
  602. * @param {String | Function} fn A selector or boolean method for testing elements.
  603. * If a function is used, it receives the current node being tested as the only argument.
  604. * @param {Boolean} [all] Whether text nodes as well as element nodes should be returned, or
  605. * just element nodes will be returned(default)
  606. * @return {Node} Node instance or null if not found
  607. */
  608. next: function(fn, all) {
  609. return Y.one(Y_DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all));
  610. },
  611.  
  612. /**
  613. * Returns all matching siblings.
  614. * Returns all siblings if no method provided.
  615. * @method siblings
  616. * @param {String | Function} fn A selector or boolean method for testing elements.
  617. * If a function is used, it receives the current node being tested as the only argument.
  618. * @return {NodeList} NodeList instance bound to found siblings
  619. */
  620. siblings: function(fn) {
  621. return Y.all(Y_DOM.siblings(this._node, _wrapFn(fn)));
  622. },
  623.  
  624. /**
  625. * Retrieves a single Node instance, the first element matching the given
  626. * CSS selector.
  627. * Returns null if no match found.
  628. * @method one
  629. *
  630. * @param {string} selector The CSS selector to test against.
  631. * @return {Node | null} A Node instance for the matching HTMLElement or null
  632. * if no match found.
  633. */
  634. one: function(selector) {
  635. return Y.one(Y.Selector.query(selector, this._node, true));
  636. },
  637.  
  638. /**
  639. * Retrieves a NodeList based on the given CSS selector.
  640. * @method all
  641. *
  642. * @param {string} selector The CSS selector to test against.
  643. * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
  644. */
  645. all: function(selector) {
  646. var nodelist;
  647.  
  648. if (this._node) {
  649. nodelist = Y.all(Y.Selector.query(selector, this._node));
  650. nodelist._query = selector;
  651. nodelist._queryRoot = this._node;
  652. }
  653.  
  654. return nodelist || Y.all([]);
  655. },
  656.  
  657. // TODO: allow fn test
  658. /**
  659. * Test if the supplied node matches the supplied selector.
  660. * @method test
  661. *
  662. * @param {string} selector The CSS selector to test against.
  663. * @return {boolean} Whether or not the node matches the selector.
  664. */
  665. test: function(selector) {
  666. return Y.Selector.test(this._node, selector);
  667. },
  668.  
  669. /**
  670. * Removes the node from its parent.
  671. * Shortcut for myNode.get('parentNode').removeChild(myNode);
  672. * @method remove
  673. * @param {Boolean} destroy whether or not to call destroy() on the node
  674. * after removal.
  675. * @chainable
  676. *
  677. */
  678. remove: function(destroy) {
  679. var node = this._node;
  680.  
  681. if (node && node.parentNode) {
  682. node.parentNode.removeChild(node);
  683. }
  684.  
  685. if (destroy) {
  686. this.destroy();
  687. }
  688.  
  689. return this;
  690. },
  691.  
  692. /**
  693. * Replace the node with the other node. This is a DOM update only
  694. * and does not change the node bound to the Node instance.
  695. * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode);
  696. * @method replace
  697. * @param {Node | HTMLElement} newNode Node to be inserted
  698. * @chainable
  699. *
  700. */
  701. replace: function(newNode) {
  702. var node = this._node;
  703. if (typeof newNode == 'string') {
  704. newNode = Y_Node.create(newNode);
  705. }
  706. node.parentNode.replaceChild(Y_Node.getDOMNode(newNode), node);
  707. return this;
  708. },
  709.  
  710. /**
  711. * @method replaceChild
  712. * @for Node
  713. * @param {String | HTMLElement | Node} node Node to be inserted
  714. * @param {HTMLElement | Node} refNode Node to be replaced
  715. * @return {Node} The replaced node
  716. */
  717. replaceChild: function(node, refNode) {
  718. if (typeof node == 'string') {
  719. node = Y_DOM.create(node);
  720. }
  721.  
  722. return Y.one(this._node.replaceChild(Y_Node.getDOMNode(node), Y_Node.getDOMNode(refNode)));
  723. },
  724.  
  725. /**
  726. * Nulls internal node references, removes any plugins and event listeners.
  727. * Note that destroy() will not remove the node from its parent or from the DOM. For that
  728. * functionality, call remove(true).
  729. * @method destroy
  730. * @param {Boolean} recursivePurge (optional) Whether or not to remove listeners from the
  731. * node's subtree (default is false)
  732. *
  733. */
  734. destroy: function(recursive) {
  735. var UID = Y.config.doc.uniqueID ? 'uniqueID' : '_yuid',
  736. instance;
  737.  
  738. this.purge(); // TODO: only remove events add via this Node
  739.  
  740. if (this.unplug) { // may not be a PluginHost
  741. this.unplug();
  742. }
  743.  
  744. this.clearData();
  745.  
  746. if (recursive) {
  747. Y.NodeList.each(this.all('*'), function(node) {
  748. instance = Y_Node._instances[node[UID]];
  749. if (instance) {
  750. instance.destroy();
  751. } else { // purge in case added by other means
  752. Y.Event.purgeElement(node);
  753. }
  754. });
  755. }
  756.  
  757. this._node = null;
  758. this._stateProxy = null;
  759.  
  760. delete Y_Node._instances[this._yuid];
  761. },
  762.  
  763. /**
  764. * Invokes a method on the Node instance
  765. * @method invoke
  766. * @param {String} method The name of the method to invoke
  767. * @param {any} [args*] Arguments to invoke the method with.
  768. * @return {any} Whatever the underly method returns.
  769. * DOM Nodes and Collections return values
  770. * are converted to Node/NodeList instances.
  771. *
  772. */
  773. invoke: function(method, a, b, c, d, e) {
  774. var node = this._node,
  775. ret;
  776.  
  777. if (a && a._node) {
  778. a = a._node;
  779. }
  780.  
  781. if (b && b._node) {
  782. b = b._node;
  783. }
  784.  
  785. ret = node[method](a, b, c, d, e);
  786. return Y_Node.scrubVal(ret, this);
  787. },
  788.  
  789. /**
  790. * @method swap
  791. * @description Swap DOM locations with the given node.
  792. * This does not change which DOM node each Node instance refers to.
  793. * @param {Node} otherNode The node to swap with
  794. * @chainable
  795. */
  796. swap: Y.config.doc.documentElement.swapNode ?
  797. function(otherNode) {
  798. this._node.swapNode(Y_Node.getDOMNode(otherNode));
  799. } :
  800. function(otherNode) {
  801. otherNode = Y_Node.getDOMNode(otherNode);
  802. var node = this._node,
  803. parent = otherNode.parentNode,
  804. nextSibling = otherNode.nextSibling;
  805.  
  806. if (nextSibling === node) {
  807. parent.insertBefore(node, otherNode);
  808. } else if (otherNode === node.nextSibling) {
  809. parent.insertBefore(otherNode, node);
  810. } else {
  811. node.parentNode.replaceChild(otherNode, node);
  812. Y_DOM.addHTML(parent, node, nextSibling);
  813. }
  814. return this;
  815. },
  816.  
  817.  
  818. hasMethod: function(method) {
  819. var node = this._node;
  820. return !!(node && method in node &&
  821. typeof node[method] != 'unknown' &&
  822. (typeof node[method] == 'function' ||
  823. String(node[method]).indexOf('function') === 1)); // IE reports as object, prepends space
  824. },
  825.  
  826. isFragment: function() {
  827. return (this.get('nodeType') === 11);
  828. },
  829.  
  830. /**
  831. * Removes and destroys all of the nodes within the node.
  832. * @method empty
  833. * @chainable
  834. */
  835. empty: function() {
  836. this.get('childNodes').remove().destroy(true);
  837. return this;
  838. },
  839.  
  840. /**
  841. * Returns the DOM node bound to the Node instance
  842. * @method getDOMNode
  843. * @return {HTMLElement}
  844. */
  845. getDOMNode: function() {
  846. return this._node;
  847. }
  848. }, true);
  849.  
  850. Y.Node = Y_Node;
  851. Y.one = Y_Node.one;
  852.