aboutsummaryrefslogtreecommitdiff
path: root/js/lib/node.js
blob: 5f82eca25c37f03a575871327851f90d24264843 (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 (!entering && cur === this.root) {
  40. // don't move past root
  41. this.current = null;
  42. } else if (cur.next) {
  43. this.current = cur.next;
  44. this.entering = true;
  45. } else {
  46. this.current = cur.parent;
  47. this.entering = false;
  48. }
  49. return {entering: entering, node: cur};
  50. };
  51. function Node(nodeType, pos) {
  52. this.t = nodeType;
  53. this.parent = null;
  54. this.firstChild = null;
  55. this.lastChild = null;
  56. this.prev = null;
  57. this.next = null;
  58. this.pos = pos || {};
  59. this.last_line_blank = false;
  60. this.open = true;
  61. this.strings = [];
  62. this.string_content = "";
  63. this.c = undefined;
  64. this.list_data = undefined;
  65. this.tight = undefined;
  66. this.info = undefined;
  67. this.destination = undefined;
  68. this.title = undefined;
  69. this.fence_char = undefined;
  70. this.fence_length = undefined;
  71. this.fence_offset = undefined;
  72. this.level = undefined;
  73. }
  74. Node.prototype.isContainer = function() {
  75. return isContainer(this);
  76. };
  77. Node.prototype.appendChild = function(child) {
  78. child.unlink();
  79. child.parent = this;
  80. if (this.lastChild) {
  81. this.lastChild.next = child;
  82. child.prev = this.lastChild;
  83. this.lastChild = child;
  84. } else {
  85. this.firstChild = child;
  86. this.lastChild = child;
  87. }
  88. };
  89. Node.prototype.prependChild = function(child) {
  90. child.unlink();
  91. child.parent = this;
  92. if (this.firstChild) {
  93. this.firstChild.prev = child;
  94. child.next = this.firstChild;
  95. this.firstChild = child;
  96. } else {
  97. this.firstChild = child;
  98. this.lastChild = child;
  99. }
  100. };
  101. Node.prototype.unlink = function() {
  102. if (this.prev) {
  103. this.prev.next = this.next;
  104. } else if (this.parent) {
  105. this.parent.firstChild = this.next;
  106. }
  107. if (this.next) {
  108. this.next.prev = this.prev;
  109. } else if (this.parent) {
  110. this.parent.lastChild = this.prev;
  111. }
  112. this.parent = null;
  113. this.next = null;
  114. this.prev = null;
  115. };
  116. Node.prototype.insertAfter = function(sibling) {
  117. sibling.unlink();
  118. sibling.next = this.next;
  119. if (sibling.next) {
  120. sibling.next.prev = sibling;
  121. }
  122. sibling.prev = this;
  123. this.next = sibling;
  124. sibling.parent = this.parent;
  125. if (!sibling.next) {
  126. sibling.parent.lastChild = sibling;
  127. }
  128. };
  129. Node.prototype.insertBefore = function(sibling) {
  130. sibling.unlink();
  131. sibling.prev = this.prev;
  132. if (sibling.prev) {
  133. sibling.prev.next = sibling;
  134. }
  135. sibling.next = this;
  136. this.prev = sibling;
  137. sibling.parent = this.parent;
  138. if (!sibling.prev) {
  139. sibling.parent.firstChild = sibling;
  140. }
  141. };
  142. Node.prototype.walker = function() {
  143. var walker = new NodeWalker(this);
  144. return walker;
  145. };
  146. Node.prototype.toAST = function() {
  147. var children;
  148. var cur;
  149. var result = { t: this.t };
  150. var propsToShow = ['t', 'c', 'list_data', 'string_content',
  151. 'pos', 'tight', 'info', 'level'];
  152. for (var i = 0; i < propsToShow.length; i++) {
  153. var prop = propsToShow[i];
  154. if (this[prop] !== undefined) {
  155. result[prop] = this[prop];
  156. }
  157. }
  158. if (isContainer(this)) {
  159. children = [];
  160. if (this.firstChild) {
  161. cur = this.firstChild;
  162. while (cur) {
  163. // TODO avoid recursion here...
  164. children.push(cur.toAST());
  165. cur = cur.next;
  166. }
  167. result.children = children;
  168. }
  169. }
  170. return result;
  171. };
  172. module.exports = Node;
  173. /* Example of use of walker:
  174. var walker = w.walker();
  175. var event;
  176. while (event = walker.next()) {
  177. console.log(event.entering, event.node.t);
  178. }
  179. */