aboutsummaryrefslogtreecommitdiff
path: root/js/lib/node.js
blob: 4615689354c2e1c9b235291c0ddea9bfedfe99d5 (plain)
  1. "use strict";
  2. function isContainer(node) {
  3. switch (node._type) {
  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._type = 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._lastLineBlank = false;
  63. this._open = true;
  64. this._strings = null;
  65. this._string_content = null;
  66. this._literal = null;
  67. this._listData = null;
  68. this._info = null;
  69. this._destination = null;
  70. this._title = null;
  71. this._isFenced = false;
  72. this._fenceChar = null;
  73. this._fenceLength = 0;
  74. this._fenceOffset = null;
  75. this._level = null;
  76. };
  77. var proto = Node.prototype;
  78. Node.prototype.isContainer = function() {
  79. return isContainer(this);
  80. };
  81. Object.defineProperty(proto, 'type', {
  82. get: function() { return this._type; }
  83. });
  84. Object.defineProperty(proto, 'firstChild', {
  85. get: function() { return this._firstChild; }
  86. });
  87. Object.defineProperty(proto, 'lastChild', {
  88. get: function() { return this._lastChild; }
  89. });
  90. Object.defineProperty(proto, 'next', {
  91. get: function() { return this._next; }
  92. });
  93. Object.defineProperty(proto, 'prev', {
  94. get: function() { return this._prev; }
  95. });
  96. Object.defineProperty(proto, 'parent', {
  97. get: function() { return this._parent; }
  98. });
  99. Object.defineProperty(proto, 'sourcepos', {
  100. get: function() { return this._sourcepos; }
  101. });
  102. Object.defineProperty(proto, 'literal', {
  103. get: function() { return this._literal; },
  104. set: function(s) { this._literal = s; }
  105. });
  106. Object.defineProperty(proto, 'destination', {
  107. get: function() { return this._destination; },
  108. set: function(s) { this._destination = s; }
  109. });
  110. Object.defineProperty(proto, 'title', {
  111. get: function() { return this._title; },
  112. set: function(s) { this._title = s; }
  113. });
  114. Object.defineProperty(proto, 'info', {
  115. get: function() { return this._info; },
  116. set: function(s) { this._info = s; }
  117. });
  118. Object.defineProperty(proto, 'level', {
  119. get: function() { return this._level; },
  120. set: function(s) { this._level = s; }
  121. });
  122. Object.defineProperty(proto, 'listType', {
  123. get: function() { return this._listData.type; },
  124. set: function(t) { this._listData.type = t; }
  125. });
  126. Object.defineProperty(proto, 'listTight', {
  127. get: function() { return this._listData.tight; },
  128. set: function(t) { this._listData.tight = t; }
  129. });
  130. Object.defineProperty(proto, 'listStart', {
  131. get: function() { return this._listData.start; },
  132. set: function(n) { this._listData.start = n; }
  133. });
  134. Object.defineProperty(proto, 'listDelimiter', {
  135. get: function() { return this._listData.delimiter; },
  136. set: function(delim) { this._listData.delimiter = delim; }
  137. });
  138. Node.prototype.appendChild = function(child) {
  139. child.unlink();
  140. child._parent = this;
  141. if (this._lastChild) {
  142. this._lastChild._next = child;
  143. child._prev = this._lastChild;
  144. this._lastChild = child;
  145. } else {
  146. this._firstChild = child;
  147. this._lastChild = child;
  148. }
  149. };
  150. Node.prototype.prependChild = function(child) {
  151. child.unlink();
  152. child._parent = this;
  153. if (this._firstChild) {
  154. this._firstChild._prev = child;
  155. child._next = this._firstChild;
  156. this._firstChild = child;
  157. } else {
  158. this._firstChild = child;
  159. this._lastChild = child;
  160. }
  161. };
  162. Node.prototype.unlink = function() {
  163. if (this._prev) {
  164. this._prev._next = this._next;
  165. } else if (this._parent) {
  166. this._parent._firstChild = this._next;
  167. }
  168. if (this._next) {
  169. this._next._prev = this._prev;
  170. } else if (this._parent) {
  171. this._parent._lastChild = this._prev;
  172. }
  173. this._parent = null;
  174. this._next = null;
  175. this._prev = null;
  176. };
  177. Node.prototype.insertAfter = function(sibling) {
  178. sibling.unlink();
  179. sibling._next = this._next;
  180. if (sibling._next) {
  181. sibling._next._prev = sibling;
  182. }
  183. sibling._prev = this;
  184. this._next = sibling;
  185. sibling._parent = this._parent;
  186. if (!sibling._next) {
  187. sibling._parent._lastChild = sibling;
  188. }
  189. };
  190. Node.prototype.insertBefore = function(sibling) {
  191. sibling.unlink();
  192. sibling._prev = this._prev;
  193. if (sibling._prev) {
  194. sibling._prev._next = sibling;
  195. }
  196. sibling._next = this;
  197. this._prev = sibling;
  198. sibling._parent = this._parent;
  199. if (!sibling._prev) {
  200. sibling._parent._firstChild = sibling;
  201. }
  202. };
  203. Node.prototype.walker = function() {
  204. var walker = new NodeWalker(this);
  205. return walker;
  206. };
  207. module.exports = Node;
  208. /* Example of use of walker:
  209. var walker = w.walker();
  210. var event;
  211. while (event = walker.next()) {
  212. console.log(event.entering, event.node.type());
  213. }
  214. */