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