You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

XMLNode.js 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999
  1. // Generated by CoffeeScript 2.4.1
  2. (function() {
  3. var DocumentPosition, NodeType, XMLCData, XMLComment, XMLDeclaration, XMLDocType, XMLDummy, XMLElement, XMLNamedNodeMap, XMLNode, XMLNodeList, XMLProcessingInstruction, XMLRaw, XMLText, getValue, isEmpty, isFunction, isObject,
  4. hasProp = {}.hasOwnProperty,
  5. splice = [].splice;
  6. ({isObject, isFunction, isEmpty, getValue} = require('./Utility'));
  7. XMLElement = null;
  8. XMLCData = null;
  9. XMLComment = null;
  10. XMLDeclaration = null;
  11. XMLDocType = null;
  12. XMLRaw = null;
  13. XMLText = null;
  14. XMLProcessingInstruction = null;
  15. XMLDummy = null;
  16. NodeType = null;
  17. XMLNodeList = null;
  18. XMLNamedNodeMap = null;
  19. DocumentPosition = null;
  20. // Represents a generic XMl element
  21. module.exports = XMLNode = (function() {
  22. class XMLNode {
  23. // Initializes a new instance of `XMLNode`
  24. // `parent` the parent node
  25. constructor(parent1) {
  26. this.parent = parent1;
  27. if (this.parent) {
  28. this.options = this.parent.options;
  29. this.stringify = this.parent.stringify;
  30. }
  31. this.value = null;
  32. this.children = [];
  33. this.baseURI = null;
  34. // first execution, load dependencies that are otherwise
  35. // circular (so we can't load them at the top)
  36. if (!XMLElement) {
  37. XMLElement = require('./XMLElement');
  38. XMLCData = require('./XMLCData');
  39. XMLComment = require('./XMLComment');
  40. XMLDeclaration = require('./XMLDeclaration');
  41. XMLDocType = require('./XMLDocType');
  42. XMLRaw = require('./XMLRaw');
  43. XMLText = require('./XMLText');
  44. XMLProcessingInstruction = require('./XMLProcessingInstruction');
  45. XMLDummy = require('./XMLDummy');
  46. NodeType = require('./NodeType');
  47. XMLNodeList = require('./XMLNodeList');
  48. XMLNamedNodeMap = require('./XMLNamedNodeMap');
  49. DocumentPosition = require('./DocumentPosition');
  50. }
  51. }
  52. // Sets the parent node of this node and its children recursively
  53. // `parent` the parent node
  54. setParent(parent) {
  55. var child, j, len, ref1, results;
  56. this.parent = parent;
  57. if (parent) {
  58. this.options = parent.options;
  59. this.stringify = parent.stringify;
  60. }
  61. ref1 = this.children;
  62. results = [];
  63. for (j = 0, len = ref1.length; j < len; j++) {
  64. child = ref1[j];
  65. results.push(child.setParent(this));
  66. }
  67. return results;
  68. }
  69. // Creates a child element node
  70. // `name` node name or an object describing the XML tree
  71. // `attributes` an object containing name/value pairs of attributes
  72. // `text` element text
  73. element(name, attributes, text) {
  74. var childNode, item, j, k, key, lastChild, len, len1, val;
  75. lastChild = null;
  76. if (attributes === null && (text == null)) {
  77. [attributes, text] = [{}, null];
  78. }
  79. if (attributes == null) {
  80. attributes = {};
  81. }
  82. attributes = getValue(attributes);
  83. // swap argument order: text <-> attributes
  84. if (!isObject(attributes)) {
  85. [text, attributes] = [attributes, text];
  86. }
  87. if (name != null) {
  88. name = getValue(name);
  89. }
  90. // expand if array
  91. if (Array.isArray(name)) {
  92. for (j = 0, len = name.length; j < len; j++) {
  93. item = name[j];
  94. lastChild = this.element(item);
  95. }
  96. // evaluate if function
  97. } else if (isFunction(name)) {
  98. lastChild = this.element(name.apply());
  99. // expand if object
  100. } else if (isObject(name)) {
  101. for (key in name) {
  102. if (!hasProp.call(name, key)) continue;
  103. val = name[key];
  104. if (isFunction(val)) {
  105. // evaluate if function
  106. val = val.apply();
  107. }
  108. // assign attributes
  109. if (!this.options.ignoreDecorators && this.stringify.convertAttKey && key.indexOf(this.stringify.convertAttKey) === 0) {
  110. lastChild = this.attribute(key.substr(this.stringify.convertAttKey.length), val);
  111. // skip empty arrays
  112. } else if (!this.options.separateArrayItems && Array.isArray(val) && isEmpty(val)) {
  113. lastChild = this.dummy();
  114. // empty objects produce one node
  115. } else if (isObject(val) && isEmpty(val)) {
  116. lastChild = this.element(key);
  117. // skip null and undefined nodes
  118. } else if (!this.options.keepNullNodes && (val == null)) {
  119. lastChild = this.dummy();
  120. // expand list by creating child nodes
  121. } else if (!this.options.separateArrayItems && Array.isArray(val)) {
  122. for (k = 0, len1 = val.length; k < len1; k++) {
  123. item = val[k];
  124. childNode = {};
  125. childNode[key] = item;
  126. lastChild = this.element(childNode);
  127. }
  128. // expand child nodes under parent
  129. } else if (isObject(val)) {
  130. // if the key is #text expand child nodes under this node to support mixed content
  131. if (!this.options.ignoreDecorators && this.stringify.convertTextKey && key.indexOf(this.stringify.convertTextKey) === 0) {
  132. lastChild = this.element(val);
  133. } else {
  134. lastChild = this.element(key);
  135. lastChild.element(val);
  136. }
  137. } else {
  138. // text node
  139. lastChild = this.element(key, val);
  140. }
  141. }
  142. // skip null nodes
  143. } else if (!this.options.keepNullNodes && text === null) {
  144. lastChild = this.dummy();
  145. } else {
  146. // text node
  147. if (!this.options.ignoreDecorators && this.stringify.convertTextKey && name.indexOf(this.stringify.convertTextKey) === 0) {
  148. lastChild = this.text(text);
  149. // cdata node
  150. } else if (!this.options.ignoreDecorators && this.stringify.convertCDataKey && name.indexOf(this.stringify.convertCDataKey) === 0) {
  151. lastChild = this.cdata(text);
  152. // comment node
  153. } else if (!this.options.ignoreDecorators && this.stringify.convertCommentKey && name.indexOf(this.stringify.convertCommentKey) === 0) {
  154. lastChild = this.comment(text);
  155. // raw text node
  156. } else if (!this.options.ignoreDecorators && this.stringify.convertRawKey && name.indexOf(this.stringify.convertRawKey) === 0) {
  157. lastChild = this.raw(text);
  158. // processing instruction
  159. } else if (!this.options.ignoreDecorators && this.stringify.convertPIKey && name.indexOf(this.stringify.convertPIKey) === 0) {
  160. lastChild = this.instruction(name.substr(this.stringify.convertPIKey.length), text);
  161. } else {
  162. // element node
  163. lastChild = this.node(name, attributes, text);
  164. }
  165. }
  166. if (lastChild == null) {
  167. throw new Error("Could not create any elements with: " + name + ". " + this.debugInfo());
  168. }
  169. return lastChild;
  170. }
  171. // Creates a child element node before the current node
  172. // `name` node name or an object describing the XML tree
  173. // `attributes` an object containing name/value pairs of attributes
  174. // `text` element text
  175. insertBefore(name, attributes, text) {
  176. var child, i, newChild, refChild, removed;
  177. // DOM level 1
  178. // insertBefore(newChild, refChild) inserts the child node newChild before refChild
  179. if (name != null ? name.type : void 0) {
  180. newChild = name;
  181. refChild = attributes;
  182. newChild.setParent(this);
  183. if (refChild) {
  184. // temporarily remove children starting *with* refChild
  185. i = children.indexOf(refChild);
  186. removed = children.splice(i);
  187. // add the new child
  188. children.push(newChild);
  189. // add back removed children after new child
  190. Array.prototype.push.apply(children, removed);
  191. } else {
  192. children.push(newChild);
  193. }
  194. return newChild;
  195. } else {
  196. if (this.isRoot) {
  197. throw new Error("Cannot insert elements at root level. " + this.debugInfo(name));
  198. }
  199. // temporarily remove children starting *with* this
  200. i = this.parent.children.indexOf(this);
  201. removed = this.parent.children.splice(i);
  202. // add the new child
  203. child = this.parent.element(name, attributes, text);
  204. // add back removed children after new child
  205. Array.prototype.push.apply(this.parent.children, removed);
  206. return child;
  207. }
  208. }
  209. // Creates a child element node after the current node
  210. // `name` node name or an object describing the XML tree
  211. // `attributes` an object containing name/value pairs of attributes
  212. // `text` element text
  213. insertAfter(name, attributes, text) {
  214. var child, i, removed;
  215. if (this.isRoot) {
  216. throw new Error("Cannot insert elements at root level. " + this.debugInfo(name));
  217. }
  218. // temporarily remove children starting *after* this
  219. i = this.parent.children.indexOf(this);
  220. removed = this.parent.children.splice(i + 1);
  221. // add the new child
  222. child = this.parent.element(name, attributes, text);
  223. // add back removed children after new child
  224. Array.prototype.push.apply(this.parent.children, removed);
  225. return child;
  226. }
  227. // Deletes a child element node
  228. remove() {
  229. var i, ref1;
  230. if (this.isRoot) {
  231. throw new Error("Cannot remove the root element. " + this.debugInfo());
  232. }
  233. i = this.parent.children.indexOf(this);
  234. splice.apply(this.parent.children, [i, i - i + 1].concat(ref1 = [])), ref1;
  235. return this.parent;
  236. }
  237. // Creates a node
  238. // `name` name of the node
  239. // `attributes` an object containing name/value pairs of attributes
  240. // `text` element text
  241. node(name, attributes, text) {
  242. var child;
  243. if (name != null) {
  244. name = getValue(name);
  245. }
  246. attributes || (attributes = {});
  247. attributes = getValue(attributes);
  248. // swap argument order: text <-> attributes
  249. if (!isObject(attributes)) {
  250. [text, attributes] = [attributes, text];
  251. }
  252. child = new XMLElement(this, name, attributes);
  253. if (text != null) {
  254. child.text(text);
  255. }
  256. this.children.push(child);
  257. return child;
  258. }
  259. // Creates a text node
  260. // `value` element text
  261. text(value) {
  262. var child;
  263. if (isObject(value)) {
  264. this.element(value);
  265. }
  266. child = new XMLText(this, value);
  267. this.children.push(child);
  268. return this;
  269. }
  270. // Creates a CDATA node
  271. // `value` element text without CDATA delimiters
  272. cdata(value) {
  273. var child;
  274. child = new XMLCData(this, value);
  275. this.children.push(child);
  276. return this;
  277. }
  278. // Creates a comment node
  279. // `value` comment text
  280. comment(value) {
  281. var child;
  282. child = new XMLComment(this, value);
  283. this.children.push(child);
  284. return this;
  285. }
  286. // Creates a comment node before the current node
  287. // `value` comment text
  288. commentBefore(value) {
  289. var child, i, removed;
  290. // temporarily remove children starting *with* this
  291. i = this.parent.children.indexOf(this);
  292. removed = this.parent.children.splice(i);
  293. // add the new child
  294. child = this.parent.comment(value);
  295. // add back removed children after new child
  296. Array.prototype.push.apply(this.parent.children, removed);
  297. return this;
  298. }
  299. // Creates a comment node after the current node
  300. // `value` comment text
  301. commentAfter(value) {
  302. var child, i, removed;
  303. // temporarily remove children starting *after* this
  304. i = this.parent.children.indexOf(this);
  305. removed = this.parent.children.splice(i + 1);
  306. // add the new child
  307. child = this.parent.comment(value);
  308. // add back removed children after new child
  309. Array.prototype.push.apply(this.parent.children, removed);
  310. return this;
  311. }
  312. // Adds unescaped raw text
  313. // `value` text
  314. raw(value) {
  315. var child;
  316. child = new XMLRaw(this, value);
  317. this.children.push(child);
  318. return this;
  319. }
  320. // Adds a dummy node
  321. dummy() {
  322. var child;
  323. child = new XMLDummy(this);
  324. // Normally when a new node is created it is added to the child node collection.
  325. // However, dummy nodes are never added to the XML tree. They are created while
  326. // converting JS objects to XML nodes in order not to break the recursive function
  327. // chain. They can be thought of as invisible nodes. They can be traversed through
  328. // by using prev(), next(), up(), etc. functions but they do not exists in the tree.
  329. // @children.push child
  330. return child;
  331. }
  332. // Adds a processing instruction
  333. // `target` instruction target
  334. // `value` instruction value
  335. instruction(target, value) {
  336. var insTarget, insValue, instruction, j, len;
  337. if (target != null) {
  338. target = getValue(target);
  339. }
  340. if (value != null) {
  341. value = getValue(value);
  342. }
  343. if (Array.isArray(target)) { // expand if array
  344. for (j = 0, len = target.length; j < len; j++) {
  345. insTarget = target[j];
  346. this.instruction(insTarget);
  347. }
  348. } else if (isObject(target)) { // expand if object
  349. for (insTarget in target) {
  350. if (!hasProp.call(target, insTarget)) continue;
  351. insValue = target[insTarget];
  352. this.instruction(insTarget, insValue);
  353. }
  354. } else {
  355. if (isFunction(value)) {
  356. value = value.apply();
  357. }
  358. instruction = new XMLProcessingInstruction(this, target, value);
  359. this.children.push(instruction);
  360. }
  361. return this;
  362. }
  363. // Creates a processing instruction node before the current node
  364. // `target` instruction target
  365. // `value` instruction value
  366. instructionBefore(target, value) {
  367. var child, i, removed;
  368. // temporarily remove children starting *with* this
  369. i = this.parent.children.indexOf(this);
  370. removed = this.parent.children.splice(i);
  371. // add the new child
  372. child = this.parent.instruction(target, value);
  373. // add back removed children after new child
  374. Array.prototype.push.apply(this.parent.children, removed);
  375. return this;
  376. }
  377. // Creates a processing instruction node after the current node
  378. // `target` instruction target
  379. // `value` instruction value
  380. instructionAfter(target, value) {
  381. var child, i, removed;
  382. // temporarily remove children starting *after* this
  383. i = this.parent.children.indexOf(this);
  384. removed = this.parent.children.splice(i + 1);
  385. // add the new child
  386. child = this.parent.instruction(target, value);
  387. // add back removed children after new child
  388. Array.prototype.push.apply(this.parent.children, removed);
  389. return this;
  390. }
  391. // Creates the xml declaration
  392. // `version` A version number string, e.g. 1.0
  393. // `encoding` Encoding declaration, e.g. UTF-8
  394. // `standalone` standalone document declaration: true or false
  395. declaration(version, encoding, standalone) {
  396. var doc, xmldec;
  397. doc = this.document();
  398. xmldec = new XMLDeclaration(doc, version, encoding, standalone);
  399. // Replace XML declaration if exists, otherwise insert at top
  400. if (doc.children.length === 0) {
  401. doc.children.unshift(xmldec);
  402. } else if (doc.children[0].type === NodeType.Declaration) {
  403. doc.children[0] = xmldec;
  404. } else {
  405. doc.children.unshift(xmldec);
  406. }
  407. return doc.root() || doc;
  408. }
  409. // Creates the document type declaration
  410. // `pubID` the public identifier of the external subset
  411. // `sysID` the system identifier of the external subset
  412. dtd(pubID, sysID) {
  413. var child, doc, doctype, i, j, k, len, len1, ref1, ref2;
  414. doc = this.document();
  415. doctype = new XMLDocType(doc, pubID, sysID);
  416. ref1 = doc.children;
  417. // Replace DTD if exists
  418. for (i = j = 0, len = ref1.length; j < len; i = ++j) {
  419. child = ref1[i];
  420. if (child.type === NodeType.DocType) {
  421. doc.children[i] = doctype;
  422. return doctype;
  423. }
  424. }
  425. ref2 = doc.children;
  426. // insert before root node if the root node exists
  427. for (i = k = 0, len1 = ref2.length; k < len1; i = ++k) {
  428. child = ref2[i];
  429. if (child.isRoot) {
  430. doc.children.splice(i, 0, doctype);
  431. return doctype;
  432. }
  433. }
  434. // otherwise append to end
  435. doc.children.push(doctype);
  436. return doctype;
  437. }
  438. // Gets the parent node
  439. up() {
  440. if (this.isRoot) {
  441. throw new Error("The root node has no parent. Use doc() if you need to get the document object.");
  442. }
  443. return this.parent;
  444. }
  445. // Gets the root node
  446. root() {
  447. var node;
  448. node = this;
  449. while (node) {
  450. if (node.type === NodeType.Document) {
  451. return node.rootObject;
  452. } else if (node.isRoot) {
  453. return node;
  454. } else {
  455. node = node.parent;
  456. }
  457. }
  458. }
  459. // Gets the node representing the XML document
  460. document() {
  461. var node;
  462. node = this;
  463. while (node) {
  464. if (node.type === NodeType.Document) {
  465. return node;
  466. } else {
  467. node = node.parent;
  468. }
  469. }
  470. }
  471. // Ends the document and converts string
  472. end(options) {
  473. return this.document().end(options);
  474. }
  475. // Gets the previous node
  476. prev() {
  477. var i;
  478. i = this.parent.children.indexOf(this);
  479. if (i < 1) {
  480. throw new Error("Already at the first node. " + this.debugInfo());
  481. }
  482. return this.parent.children[i - 1];
  483. }
  484. // Gets the next node
  485. next() {
  486. var i;
  487. i = this.parent.children.indexOf(this);
  488. if (i === -1 || i === this.parent.children.length - 1) {
  489. throw new Error("Already at the last node. " + this.debugInfo());
  490. }
  491. return this.parent.children[i + 1];
  492. }
  493. // Imports cloned root from another XML document
  494. // `doc` the XML document to insert nodes from
  495. importDocument(doc) {
  496. var child, clonedRoot, j, len, ref1;
  497. clonedRoot = doc.root().clone();
  498. clonedRoot.parent = this;
  499. clonedRoot.isRoot = false;
  500. this.children.push(clonedRoot);
  501. // set properties if imported element becomes the root node
  502. if (this.type === NodeType.Document) {
  503. clonedRoot.isRoot = true;
  504. clonedRoot.documentObject = this;
  505. this.rootObject = clonedRoot;
  506. // set dtd name
  507. if (this.children) {
  508. ref1 = this.children;
  509. for (j = 0, len = ref1.length; j < len; j++) {
  510. child = ref1[j];
  511. if (child.type === NodeType.DocType) {
  512. child.name = clonedRoot.name;
  513. break;
  514. }
  515. }
  516. }
  517. }
  518. return this;
  519. }
  520. // Returns debug string for this node
  521. debugInfo(name) {
  522. var ref1, ref2;
  523. name = name || this.name;
  524. if ((name == null) && !((ref1 = this.parent) != null ? ref1.name : void 0)) {
  525. return "";
  526. } else if (name == null) {
  527. return "parent: <" + this.parent.name + ">";
  528. } else if (!((ref2 = this.parent) != null ? ref2.name : void 0)) {
  529. return "node: <" + name + ">";
  530. } else {
  531. return "node: <" + name + ">, parent: <" + this.parent.name + ">";
  532. }
  533. }
  534. // Aliases
  535. ele(name, attributes, text) {
  536. return this.element(name, attributes, text);
  537. }
  538. nod(name, attributes, text) {
  539. return this.node(name, attributes, text);
  540. }
  541. txt(value) {
  542. return this.text(value);
  543. }
  544. dat(value) {
  545. return this.cdata(value);
  546. }
  547. com(value) {
  548. return this.comment(value);
  549. }
  550. ins(target, value) {
  551. return this.instruction(target, value);
  552. }
  553. doc() {
  554. return this.document();
  555. }
  556. dec(version, encoding, standalone) {
  557. return this.declaration(version, encoding, standalone);
  558. }
  559. e(name, attributes, text) {
  560. return this.element(name, attributes, text);
  561. }
  562. n(name, attributes, text) {
  563. return this.node(name, attributes, text);
  564. }
  565. t(value) {
  566. return this.text(value);
  567. }
  568. d(value) {
  569. return this.cdata(value);
  570. }
  571. c(value) {
  572. return this.comment(value);
  573. }
  574. r(value) {
  575. return this.raw(value);
  576. }
  577. i(target, value) {
  578. return this.instruction(target, value);
  579. }
  580. u() {
  581. return this.up();
  582. }
  583. // can be deprecated in a future release
  584. importXMLBuilder(doc) {
  585. return this.importDocument(doc);
  586. }
  587. // Adds or modifies an attribute.
  588. // `name` attribute name
  589. // `value` attribute value
  590. attribute(name, value) {
  591. throw new Error("attribute() applies to element nodes only.");
  592. }
  593. att(name, value) {
  594. return this.attribute(name, value);
  595. }
  596. a(name, value) {
  597. return this.attribute(name, value);
  598. }
  599. // Removes an attribute
  600. // `name` attribute name
  601. removeAttribute(name) {
  602. throw new Error("attribute() applies to element nodes only.");
  603. }
  604. // DOM level 1 functions to be implemented later
  605. replaceChild(newChild, oldChild) {
  606. throw new Error("This DOM method is not implemented." + this.debugInfo());
  607. }
  608. removeChild(oldChild) {
  609. throw new Error("This DOM method is not implemented." + this.debugInfo());
  610. }
  611. appendChild(newChild) {
  612. throw new Error("This DOM method is not implemented." + this.debugInfo());
  613. }
  614. hasChildNodes() {
  615. return this.children.length !== 0;
  616. }
  617. cloneNode(deep) {
  618. throw new Error("This DOM method is not implemented." + this.debugInfo());
  619. }
  620. normalize() {
  621. throw new Error("This DOM method is not implemented." + this.debugInfo());
  622. }
  623. // DOM level 2
  624. isSupported(feature, version) {
  625. return true;
  626. }
  627. hasAttributes() {
  628. return this.attribs.length !== 0;
  629. }
  630. // DOM level 3 functions to be implemented later
  631. compareDocumentPosition(other) {
  632. var ref, res;
  633. ref = this;
  634. if (ref === other) {
  635. return 0;
  636. } else if (this.document() !== other.document()) {
  637. res = DocumentPosition.Disconnected | DocumentPosition.ImplementationSpecific;
  638. if (Math.random() < 0.5) {
  639. res |= DocumentPosition.Preceding;
  640. } else {
  641. res |= DocumentPosition.Following;
  642. }
  643. return res;
  644. } else if (ref.isAncestor(other)) {
  645. return DocumentPosition.Contains | DocumentPosition.Preceding;
  646. } else if (ref.isDescendant(other)) {
  647. return DocumentPosition.Contains | DocumentPosition.Following;
  648. } else if (ref.isPreceding(other)) {
  649. return DocumentPosition.Preceding;
  650. } else {
  651. return DocumentPosition.Following;
  652. }
  653. }
  654. isSameNode(other) {
  655. throw new Error("This DOM method is not implemented." + this.debugInfo());
  656. }
  657. lookupPrefix(namespaceURI) {
  658. throw new Error("This DOM method is not implemented." + this.debugInfo());
  659. }
  660. isDefaultNamespace(namespaceURI) {
  661. throw new Error("This DOM method is not implemented." + this.debugInfo());
  662. }
  663. lookupNamespaceURI(prefix) {
  664. throw new Error("This DOM method is not implemented." + this.debugInfo());
  665. }
  666. isEqualNode(node) {
  667. var i, j, ref1;
  668. if (node.nodeType !== this.nodeType) {
  669. return false;
  670. }
  671. if (node.children.length !== this.children.length) {
  672. return false;
  673. }
  674. for (i = j = 0, ref1 = this.children.length - 1; (0 <= ref1 ? j <= ref1 : j >= ref1); i = 0 <= ref1 ? ++j : --j) {
  675. if (!this.children[i].isEqualNode(node.children[i])) {
  676. return false;
  677. }
  678. }
  679. return true;
  680. }
  681. getFeature(feature, version) {
  682. throw new Error("This DOM method is not implemented." + this.debugInfo());
  683. }
  684. setUserData(key, data, handler) {
  685. throw new Error("This DOM method is not implemented." + this.debugInfo());
  686. }
  687. getUserData(key) {
  688. throw new Error("This DOM method is not implemented." + this.debugInfo());
  689. }
  690. // Returns true if other is an inclusive descendant of node,
  691. // and false otherwise.
  692. contains(other) {
  693. if (!other) {
  694. return false;
  695. }
  696. return other === this || this.isDescendant(other);
  697. }
  698. // An object A is called a descendant of an object B, if either A is
  699. // a child of B or A is a child of an object C that is a descendant of B.
  700. isDescendant(node) {
  701. var child, isDescendantChild, j, len, ref1;
  702. ref1 = this.children;
  703. for (j = 0, len = ref1.length; j < len; j++) {
  704. child = ref1[j];
  705. if (node === child) {
  706. return true;
  707. }
  708. isDescendantChild = child.isDescendant(node);
  709. if (isDescendantChild) {
  710. return true;
  711. }
  712. }
  713. return false;
  714. }
  715. // An object A is called an ancestor of an object B if and only if
  716. // B is a descendant of A.
  717. isAncestor(node) {
  718. return node.isDescendant(this);
  719. }
  720. // An object A is preceding an object B if A and B are in the
  721. // same tree and A comes before B in tree order.
  722. isPreceding(node) {
  723. var nodePos, thisPos;
  724. nodePos = this.treePosition(node);
  725. thisPos = this.treePosition(this);
  726. if (nodePos === -1 || thisPos === -1) {
  727. return false;
  728. } else {
  729. return nodePos < thisPos;
  730. }
  731. }
  732. // An object A is folllowing an object B if A and B are in the
  733. // same tree and A comes after B in tree order.
  734. isFollowing(node) {
  735. var nodePos, thisPos;
  736. nodePos = this.treePosition(node);
  737. thisPos = this.treePosition(this);
  738. if (nodePos === -1 || thisPos === -1) {
  739. return false;
  740. } else {
  741. return nodePos > thisPos;
  742. }
  743. }
  744. // Returns the preorder position of the given node in the tree, or -1
  745. // if the node is not in the tree.
  746. treePosition(node) {
  747. var found, pos;
  748. pos = 0;
  749. found = false;
  750. this.foreachTreeNode(this.document(), function(childNode) {
  751. pos++;
  752. if (!found && childNode === node) {
  753. return found = true;
  754. }
  755. });
  756. if (found) {
  757. return pos;
  758. } else {
  759. return -1;
  760. }
  761. }
  762. // Depth-first preorder traversal through the XML tree
  763. foreachTreeNode(node, func) {
  764. var child, j, len, ref1, res;
  765. node || (node = this.document());
  766. ref1 = node.children;
  767. for (j = 0, len = ref1.length; j < len; j++) {
  768. child = ref1[j];
  769. if (res = func(child)) {
  770. return res;
  771. } else {
  772. res = this.foreachTreeNode(child, func);
  773. if (res) {
  774. return res;
  775. }
  776. }
  777. }
  778. }
  779. };
  780. // DOM level 1
  781. Object.defineProperty(XMLNode.prototype, 'nodeName', {
  782. get: function() {
  783. return this.name;
  784. }
  785. });
  786. Object.defineProperty(XMLNode.prototype, 'nodeType', {
  787. get: function() {
  788. return this.type;
  789. }
  790. });
  791. Object.defineProperty(XMLNode.prototype, 'nodeValue', {
  792. get: function() {
  793. return this.value;
  794. }
  795. });
  796. Object.defineProperty(XMLNode.prototype, 'parentNode', {
  797. get: function() {
  798. return this.parent;
  799. }
  800. });
  801. Object.defineProperty(XMLNode.prototype, 'childNodes', {
  802. get: function() {
  803. if (!this.childNodeList || !this.childNodeList.nodes) {
  804. this.childNodeList = new XMLNodeList(this.children);
  805. }
  806. return this.childNodeList;
  807. }
  808. });
  809. Object.defineProperty(XMLNode.prototype, 'firstChild', {
  810. get: function() {
  811. return this.children[0] || null;
  812. }
  813. });
  814. Object.defineProperty(XMLNode.prototype, 'lastChild', {
  815. get: function() {
  816. return this.children[this.children.length - 1] || null;
  817. }
  818. });
  819. Object.defineProperty(XMLNode.prototype, 'previousSibling', {
  820. get: function() {
  821. var i;
  822. i = this.parent.children.indexOf(this);
  823. return this.parent.children[i - 1] || null;
  824. }
  825. });
  826. Object.defineProperty(XMLNode.prototype, 'nextSibling', {
  827. get: function() {
  828. var i;
  829. i = this.parent.children.indexOf(this);
  830. return this.parent.children[i + 1] || null;
  831. }
  832. });
  833. Object.defineProperty(XMLNode.prototype, 'ownerDocument', {
  834. get: function() {
  835. return this.document() || null;
  836. }
  837. });
  838. // DOM level 3
  839. Object.defineProperty(XMLNode.prototype, 'textContent', {
  840. get: function() {
  841. var child, j, len, ref1, str;
  842. if (this.nodeType === NodeType.Element || this.nodeType === NodeType.DocumentFragment) {
  843. str = '';
  844. ref1 = this.children;
  845. for (j = 0, len = ref1.length; j < len; j++) {
  846. child = ref1[j];
  847. if (child.textContent) {
  848. str += child.textContent;
  849. }
  850. }
  851. return str;
  852. } else {
  853. return null;
  854. }
  855. },
  856. set: function(value) {
  857. throw new Error("This DOM method is not implemented." + this.debugInfo());
  858. }
  859. });
  860. return XMLNode;
  861. }).call(this);
  862. }).call(this);