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