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