aboutsummaryrefslogtreecommitdiff
path: root/js/lib/node.js
blob: 84fb122d9fcc53a8a4a3dd09e60416d335fddfac (plain)
  1. "use strict";
  2. function isContainer(node) {
  3. var t = node.t;
  4. return (t === 'Document' ||
  5. t === 'BlockQuote' ||
  6. t === 'List' ||
  7. t === 'ListItem' ||
  8. t === 'Paragraph' ||
  9. t === 'Header' ||
  10. t === 'Emph' ||
  11. t === 'Strong' ||
  12. t === 'Link' ||
  13. t === 'Image');
  14. }
  15. function NodeWalker(root) {
  16. this.current = root;
  17. this.root = root;
  18. this.entering = true;
  19. }
  20. NodeWalker.prototype.resumeAt = function(node, entering) {
  21. this.current = node;
  22. this.entering = (entering === true);
  23. };
  24. NodeWalker.prototype.next = function(){
  25. var cur = this.current;
  26. var entering = this.entering;
  27. if (!cur) {
  28. return null;
  29. }
  30. var container = isContainer(cur);
  31. if (entering && container) {
  32. if (cur.firstChild) {
  33. this.current = cur.firstChild;
  34. this.entering = true;
  35. } else {
  36. // stay on node but exit
  37. this.entering = false;
  38. }
  39. } else if (cur.next) {
  40. this.current = cur.next;
  41. this.entering = true;
  42. } else {
  43. this.current = cur.parent;
  44. this.entering = false;
  45. }
  46. return {entering: entering, node: cur};
  47. };
  48. function Node(nodeType, sourcepos) {
  49. this.t = nodeType;
  50. this.parent = null;
  51. this.firstChild = null;
  52. this.lastChild = null;
  53. this.prev = null;
  54. this.next = null;
  55. this.sourcepos = sourcepos;
  56. this.last_line_blank = false;
  57. this.open = true;
  58. this.strings = undefined;
  59. this.string_content = undefined;
  60. this.literal = undefined;
  61. this.list_data = undefined;
  62. this.info = undefined;
  63. this.destination = undefined;
  64. this.title = undefined;
  65. this.fence_char = undefined;
  66. this.fence_length = undefined;
  67. this.fence_offset = undefined;
  68. this.level = undefined;
  69. }
  70. Node.prototype.isContainer = function() {
  71. return isContainer(this);
  72. };
  73. Node.prototype.appendChild = function(child) {
  74. child.unlink();
  75. child.parent = this;
  76. if (this.lastChild) {
  77. this.lastChild.next = child;
  78. child.prev = this.lastChild;
  79. this.lastChild = child;
  80. } else {
  81. this.firstChild = child;
  82. this.lastChild = child;
  83. }
  84. };
  85. Node.prototype.prependChild = function(child) {
  86. child.unlink();
  87. child.parent = this;
  88. if (this.firstChild) {
  89. this.firstChild.prev = child;
  90. child.next = this.firstChild;
  91. this.firstChild = child;
  92. } else {
  93. this.firstChild = child;
  94. this.lastChild = child;
  95. }
  96. };
  97. Node.prototype.unlink = function() {
  98. if (this.prev) {
  99. this.prev.next = this.next;
  100. } else if (this.parent) {
  101. this.parent.firstChild = this.next;
  102. }
  103. if (this.next) {
  104. this.next.prev = this.prev;
  105. } else if (this.parent) {
  106. this.parent.lastChild = this.prev;
  107. }
  108. this.parent = null;
  109. this.next = null;
  110. this.prev = null;
  111. };
  112. Node.prototype.insertAfter = function(sibling) {
  113. sibling.unlink();
  114. sibling.next = this.next;
  115. if (sibling.next) {
  116. sibling.next.prev = sibling;
  117. }
  118. sibling.prev = this;
  119. this.next = sibling;
  120. sibling.parent = this.parent;
  121. if (!sibling.next) {
  122. sibling.parent.lastChild = sibling;
  123. }
  124. };
  125. Node.prototype.insertBefore = function(sibling) {
  126. sibling.unlink();
  127. sibling.prev = this.prev;
  128. if (sibling.prev) {
  129. sibling.prev.next = sibling;
  130. }
  131. sibling.next = this;
  132. this.prev = sibling;
  133. sibling.parent = this.parent;
  134. if (!sibling.prev) {
  135. sibling.parent.firstChild = sibling;
  136. }
  137. };
  138. Node.prototype.walker = function() {
  139. var walker = new NodeWalker(this);
  140. return walker;
  141. };
  142. var toASTNode = function(node) {
  143. var result = {};
  144. var propsToShow = ['t', 'literal', 'list_data', 'sourcepos',
  145. 'info', 'level'];
  146. for (var i = 0, len = propsToShow.length; i < len; i++) {
  147. var prop = propsToShow[i];
  148. if (node[prop] !== undefined) {
  149. result[prop] = node[prop];
  150. }
  151. }
  152. return result;
  153. };
  154. Node.prototype.toAST = function() {
  155. var childrenStack = [];
  156. var walker = this.walker();
  157. var event;
  158. while ((event = walker.next())) {
  159. var node = event.node;
  160. var entering = event.entering;
  161. var container = node.isContainer();
  162. var astnode;
  163. if (container) {
  164. if (entering) {
  165. childrenStack.push([]);
  166. } else {
  167. astnode = toASTNode(node);
  168. astnode.children = childrenStack.pop();
  169. if (childrenStack.length > 0) {
  170. childrenStack[childrenStack.length - 1].push(astnode);
  171. }
  172. }
  173. } else {
  174. astnode = toASTNode(node);
  175. childrenStack[childrenStack.length - 1].push(astnode);
  176. }
  177. }
  178. return astnode;
  179. };
  180. module.exports = Node;
  181. /* Example of use of walker:
  182. var walker = w.walker();
  183. var event;
  184. while (event = walker.next()) {
  185. console.log(event.entering, event.node.t);
  186. }
  187. */