API Docs for: 3.10.3
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 {DOMNode} 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 DOMNode
  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 | HTMLNode} node The Node instance or an HTMLNode
  141. * @return {HTMLNode} 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 {any} node The Node instance or an HTMLNode
  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(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. doc = (doc) ? doc._node || doc : node[OWNER_DOCUMENT];
  524. if (doc.documentElement) {
  525. return Y_DOM.contains(doc.documentElement, node);
  526. }
  527. },
  528.  
  529. getById: function(id) {
  530. var node = this._node,
  531. ret = Y_DOM.byId(id, node[OWNER_DOCUMENT]);
  532. if (ret && Y_DOM.contains(node, ret)) {
  533. ret = Y.one(ret);
  534. } else {
  535. ret = null;
  536. }
  537. return ret;
  538. },
  539.  
  540. /**
  541. * Returns the nearest ancestor that passes the test applied by supplied boolean method.
  542. * @method ancestor
  543. * @param {String | Function} fn A selector string or boolean method for testing elements.
  544. * If a function is used, it receives the current node being tested as the only argument.
  545. * If fn is not passed as an argument, the parent node will be returned.
  546. * @param {Boolean} testSelf optional Whether or not to include the element in the scan
  547. * @param {String | Function} stopFn optional A selector string or boolean
  548. * method to indicate when the search should stop. The search bails when the function
  549. * returns true or the selector matches.
  550. * If a function is used, it receives the current node being tested as the only argument.
  551. * @return {Node} The matching Node instance or null if not found
  552. */
  553. ancestor: function(fn, testSelf, stopFn) {
  554. // testSelf is optional, check for stopFn as 2nd arg
  555. if (arguments.length === 2 &&
  556. (typeof testSelf == 'string' || typeof testSelf == 'function')) {
  557. stopFn = testSelf;
  558. }
  559.  
  560. return Y.one(Y_DOM.ancestor(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn)));
  561. },
  562.  
  563. /**
  564. * Returns the ancestors that pass the test applied by supplied boolean method.
  565. * @method ancestors
  566. * @param {String | Function} fn A selector string or boolean method for testing elements.
  567. * @param {Boolean} testSelf optional Whether or not to include the element in the scan
  568. * If a function is used, it receives the current node being tested as the only argument.
  569. * @return {NodeList} A NodeList instance containing the matching elements
  570. */
  571. ancestors: function(fn, testSelf, stopFn) {
  572. if (arguments.length === 2 &&
  573. (typeof testSelf == 'string' || typeof testSelf == 'function')) {
  574. stopFn = testSelf;
  575. }
  576. return Y.all(Y_DOM.ancestors(this._node, _wrapFn(fn), testSelf, _wrapFn(stopFn)));
  577. },
  578.  
  579. /**
  580. * Returns the previous matching sibling.
  581. * Returns the nearest element node sibling if no method provided.
  582. * @method previous
  583. * @param {String | Function} fn A selector or boolean method for testing elements.
  584. * If a function is used, it receives the current node being tested as the only argument.
  585. * @return {Node} Node instance or null if not found
  586. */
  587. previous: function(fn, all) {
  588. return Y.one(Y_DOM.elementByAxis(this._node, 'previousSibling', _wrapFn(fn), all));
  589. },
  590.  
  591. /**
  592. * Returns the next matching sibling.
  593. * Returns the nearest element node sibling if no method provided.
  594. * @method next
  595. * @param {String | Function} fn A selector or boolean method for testing elements.
  596. * If a function is used, it receives the current node being tested as the only argument.
  597. * @return {Node} Node instance or null if not found
  598. */
  599. next: function(fn, all) {
  600. return Y.one(Y_DOM.elementByAxis(this._node, 'nextSibling', _wrapFn(fn), all));
  601. },
  602.  
  603. /**
  604. * Returns all matching siblings.
  605. * Returns all siblings if no method provided.
  606. * @method siblings
  607. * @param {String | Function} fn A selector or boolean method for testing elements.
  608. * If a function is used, it receives the current node being tested as the only argument.
  609. * @return {NodeList} NodeList instance bound to found siblings
  610. */
  611. siblings: function(fn) {
  612. return Y.all(Y_DOM.siblings(this._node, _wrapFn(fn)));
  613. },
  614.  
  615. /**
  616. * Retrieves a single Node instance, the first element matching the given
  617. * CSS selector.
  618. * Returns null if no match found.
  619. * @method one
  620. *
  621. * @param {string} selector The CSS selector to test against.
  622. * @return {Node | null} A Node instance for the matching HTMLElement or null
  623. * if no match found.
  624. */
  625. one: function(selector) {
  626. return Y.one(Y.Selector.query(selector, this._node, true));
  627. },
  628.  
  629. /**
  630. * Retrieves a NodeList based on the given CSS selector.
  631. * @method all
  632. *
  633. * @param {string} selector The CSS selector to test against.
  634. * @return {NodeList} A NodeList instance for the matching HTMLCollection/Array.
  635. */
  636. all: function(selector) {
  637. var nodelist;
  638. if (this._node) {
  639. nodelist = Y.all(Y.Selector.query(selector, this._node));
  640. nodelist._query = selector;
  641. nodelist._queryRoot = this._node;
  642. }
  643.  
  644. return nodelist || Y.all([]);
  645. },
  646.  
  647. // TODO: allow fn test
  648. /**
  649. * Test if the supplied node matches the supplied selector.
  650. * @method test
  651. *
  652. * @param {string} selector The CSS selector to test against.
  653. * @return {boolean} Whether or not the node matches the selector.
  654. */
  655. test: function(selector) {
  656. return Y.Selector.test(this._node, selector);
  657. },
  658.  
  659. /**
  660. * Removes the node from its parent.
  661. * Shortcut for myNode.get('parentNode').removeChild(myNode);
  662. * @method remove
  663. * @param {Boolean} destroy whether or not to call destroy() on the node
  664. * after removal.
  665. * @chainable
  666. *
  667. */
  668. remove: function(destroy) {
  669. var node = this._node;
  670.  
  671. if (node && node.parentNode) {
  672. node.parentNode.removeChild(node);
  673. }
  674.  
  675. if (destroy) {
  676. this.destroy();
  677. }
  678.  
  679. return this;
  680. },
  681.  
  682. /**
  683. * Replace the node with the other node. This is a DOM update only
  684. * and does not change the node bound to the Node instance.
  685. * Shortcut for myNode.get('parentNode').replaceChild(newNode, myNode);
  686. * @method replace
  687. * @param {Node | HTMLNode} newNode Node to be inserted
  688. * @chainable
  689. *
  690. */
  691. replace: function(newNode) {
  692. var node = this._node;
  693. if (typeof newNode == 'string') {
  694. newNode = Y_Node.create(newNode);
  695. }
  696. node.parentNode.replaceChild(Y_Node.getDOMNode(newNode), node);
  697. return this;
  698. },
  699.  
  700. /**
  701. * @method replaceChild
  702. * @for Node
  703. * @param {String | HTMLElement | Node} node Node to be inserted
  704. * @param {HTMLElement | Node} refNode Node to be replaced
  705. * @return {Node} The replaced node
  706. */
  707. replaceChild: function(node, refNode) {
  708. if (typeof node == 'string') {
  709. node = Y_DOM.create(node);
  710. }
  711.  
  712. return Y.one(this._node.replaceChild(Y_Node.getDOMNode(node), Y_Node.getDOMNode(refNode)));
  713. },
  714.  
  715. /**
  716. * Nulls internal node references, removes any plugins and event listeners.
  717. * Note that destroy() will not remove the node from its parent or from the DOM. For that
  718. * functionality, call remove(true).
  719. * @method destroy
  720. * @param {Boolean} recursivePurge (optional) Whether or not to remove listeners from the
  721. * node's subtree (default is false)
  722. *
  723. */
  724. destroy: function(recursive) {
  725. var UID = Y.config.doc.uniqueID ? 'uniqueID' : '_yuid',
  726. instance;
  727.  
  728. this.purge(); // TODO: only remove events add via this Node
  729.  
  730. if (this.unplug) { // may not be a PluginHost
  731. this.unplug();
  732. }
  733.  
  734. this.clearData();
  735.  
  736. if (recursive) {
  737. Y.NodeList.each(this.all('*'), function(node) {
  738. instance = Y_Node._instances[node[UID]];
  739. if (instance) {
  740. instance.destroy();
  741. } else { // purge in case added by other means
  742. Y.Event.purgeElement(node);
  743. }
  744. });
  745. }
  746.  
  747. this._node = null;
  748. this._stateProxy = null;
  749.  
  750. delete Y_Node._instances[this._yuid];
  751. },
  752.  
  753. /**
  754. * Invokes a method on the Node instance
  755. * @method invoke
  756. * @param {String} method The name of the method to invoke
  757. * @param {Any} a, b, c, etc. Arguments to invoke the method with.
  758. * @return Whatever the underly method returns.
  759. * DOM Nodes and Collections return values
  760. * are converted to Node/NodeList instances.
  761. *
  762. */
  763. invoke: function(method, a, b, c, d, e) {
  764. var node = this._node,
  765. ret;
  766.  
  767. if (a && a._node) {
  768. a = a._node;
  769. }
  770.  
  771. if (b && b._node) {
  772. b = b._node;
  773. }
  774.  
  775. ret = node[method](a, b, c, d, e);
  776. return Y_Node.scrubVal(ret, this);
  777. },
  778.  
  779. /**
  780. * @method swap
  781. * @description Swap DOM locations with the given node.
  782. * This does not change which DOM node each Node instance refers to.
  783. * @param {Node} otherNode The node to swap with
  784. * @chainable
  785. */
  786. swap: Y.config.doc.documentElement.swapNode ?
  787. function(otherNode) {
  788. this._node.swapNode(Y_Node.getDOMNode(otherNode));
  789. } :
  790. function(otherNode) {
  791. otherNode = Y_Node.getDOMNode(otherNode);
  792. var node = this._node,
  793. parent = otherNode.parentNode,
  794. nextSibling = otherNode.nextSibling;
  795.  
  796. if (nextSibling === node) {
  797. parent.insertBefore(node, otherNode);
  798. } else if (otherNode === node.nextSibling) {
  799. parent.insertBefore(otherNode, node);
  800. } else {
  801. node.parentNode.replaceChild(otherNode, node);
  802. Y_DOM.addHTML(parent, node, nextSibling);
  803. }
  804. return this;
  805. },
  806.  
  807.  
  808. hasMethod: function(method) {
  809. var node = this._node;
  810. return !!(node && method in node &&
  811. typeof node[method] != 'unknown' &&
  812. (typeof node[method] == 'function' ||
  813. String(node[method]).indexOf('function') === 1)); // IE reports as object, prepends space
  814. },
  815.  
  816. isFragment: function() {
  817. return (this.get('nodeType') === 11);
  818. },
  819.  
  820. /**
  821. * Removes and destroys all of the nodes within the node.
  822. * @method empty
  823. * @chainable
  824. */
  825. empty: function() {
  826. this.get('childNodes').remove().destroy(true);
  827. return this;
  828. },
  829.  
  830. /**
  831. * Returns the DOM node bound to the Node instance
  832. * @method getDOMNode
  833. * @return {DOMNode}
  834. */
  835. getDOMNode: function() {
  836. return this._node;
  837. }
  838. }, true);
  839.  
  840. Y.Node = Y_Node;
  841. Y.one = Y_Node.one;
  842.