UNPKG

65.6 kBJavaScriptView Raw
1/**
2 * @author Titus Wormer
3 * @copyright 2015 Titus Wormer
4 * @license MIT
5 * @module mdast:parse
6 * @version 2.2.2
7 * @fileoverview Parse a markdown document into an
8 * abstract syntax tree.
9 */
10
11'use strict';
12
13/* eslint-env commonjs */
14
15/*
16 * Dependencies.
17 */
18
19var he = require('he');
20var repeat = require('repeat-string');
21var trim = require('trim');
22var trimTrailingLines = require('trim-trailing-lines');
23var extend = require('extend.js');
24var utilities = require('./utilities.js');
25var defaultExpressions = require('./expressions.js');
26var defaultOptions = require('./defaults.js').parse;
27
28/*
29 * Methods.
30 */
31
32var raise = utilities.raise;
33var clean = utilities.clean;
34var validate = utilities.validate;
35var normalize = utilities.normalizeIdentifier;
36var arrayPush = [].push;
37
38/*
39 * Characters.
40 */
41
42var AT_SIGN = '@';
43var CARET = '^';
44var EQUALS = '=';
45var EXCLAMATION_MARK = '!';
46var MAILTO_PROTOCOL = 'mailto:';
47var NEW_LINE = '\n';
48var SPACE = ' ';
49var TAB = '\t';
50var EMPTY = '';
51var LT = '<';
52var GT = '>';
53var BRACKET_OPEN = '[';
54
55/*
56 * Types.
57 */
58
59var BLOCK = 'block';
60var INLINE = 'inline';
61var HORIZONTAL_RULE = 'horizontalRule';
62var HTML = 'html';
63var YAML = 'yaml';
64var TABLE = 'table';
65var TABLE_CELL = 'tableCell';
66var TABLE_HEADER = 'tableHeader';
67var TABLE_ROW = 'tableRow';
68var PARAGRAPH = 'paragraph';
69var TEXT = 'text';
70var CODE = 'code';
71var LIST = 'list';
72var LIST_ITEM = 'listItem';
73var FOOTNOTE_DEFINITION = 'footnoteDefinition';
74var HEADING = 'heading';
75var BLOCKQUOTE = 'blockquote';
76var LINK = 'link';
77var IMAGE = 'image';
78var FOOTNOTE = 'footnote';
79var ESCAPE = 'escape';
80var STRONG = 'strong';
81var EMPHASIS = 'emphasis';
82var DELETE = 'delete';
83var INLINE_CODE = 'inlineCode';
84var BREAK = 'break';
85var ROOT = 'root';
86
87/**
88 * Wrapper around he's `decode` function.
89 *
90 * @example
91 * decode('&amp;'); // '&'
92 * decode('&amp'); // '&'
93 *
94 * @param {string} value
95 * @param {function(string)} eat
96 * @return {string}
97 * @throws {Error} - When `eat.file.quiet` is not `true`.
98 * However, by default `he` does not throw on incorrect
99 * encoded entities, but when
100 * `he.decode.options.strict: true`, they occur on
101 * entities with a missing closing semi-colon.
102 */
103function decode(value, eat) {
104 try {
105 return he.decode(value);
106 } catch (exception) {
107 eat.file.fail(exception, eat.now());
108 }
109}
110
111/**
112 * Factory to de-escape a value, based on an expression
113 * at `key` in `scope`.
114 *
115 * @example
116 * var expressions = {escape: /\\(a)/}
117 * var descape = descapeFactory(expressions, 'escape');
118 *
119 * @param {Object} scope - Map of expressions.
120 * @param {string} key - Key in `map` at which the
121 * non-global expression exists.
122 * @return {function(string): string} - Function which
123 * takes a value and returns its unescaped version.
124 */
125function descapeFactory(scope, key) {
126 var globalExpression;
127 var expression;
128
129 /**
130 * Private method to get a global expression
131 * from the expression at `key` in `scope`.
132 * This method is smart about not recreating
133 * the expressions every time.
134 *
135 * @private
136 * @return {RegExp}
137 */
138 function generate() {
139 if (scope[key] !== globalExpression) {
140 globalExpression = scope[key];
141 expression = new RegExp(
142 scope[key].source.replace(CARET, EMPTY), 'g'
143 );
144 }
145
146 return expression;
147 }
148
149 /**
150 * De-escape a string using the expression at `key`
151 * in `scope`.
152 *
153 * @example
154 * var expressions = {escape: /\\(a)/}
155 * var descape = descapeFactory(expressions, 'escape');
156 * descape('\a'); // 'a'
157 *
158 * @param {string} value - Escaped string.
159 * @return {string} - Unescaped string.
160 */
161 function descape(value) {
162 return value.replace(generate(), '$1');
163 }
164
165 return descape;
166}
167
168/*
169 * Tab size.
170 */
171
172var TAB_SIZE = 4;
173
174/*
175 * Expressions.
176 */
177
178var EXPRESSION_RIGHT_ALIGNMENT = /^[ \t]*-+:[ \t]*$/;
179var EXPRESSION_CENTER_ALIGNMENT = /^[ \t]*:-+:[ \t]*$/;
180var EXPRESSION_LEFT_ALIGNMENT = /^[ \t]*:-+[ \t]*$/;
181var EXPRESSION_TABLE_FENCE = /^[ \t]*|\|[ \t]*$/g;
182var EXPRESSION_TABLE_BORDER = /[ \t]*\|[ \t]*/;
183var EXPRESSION_BLOCK_QUOTE = /^[ \t]*>[ \t]?/gm;
184var EXPRESSION_BULLET = /^([ \t]*)([*+-]|\d+[.)])( {1,4}(?! )| |\t)([^\n]*)/;
185var EXPRESSION_PEDANTIC_BULLET = /^([ \t]*)([*+-]|\d+[.)])([ \t]+)/;
186var EXPRESSION_INITIAL_INDENT = /^( {1,4}|\t)?/gm;
187var EXPRESSION_INITIAL_TAB = /^( {4}|\t)?/gm;
188var EXPRESSION_HTML_LINK_OPEN = /^<a /i;
189var EXPRESSION_HTML_LINK_CLOSE = /^<\/a>/i;
190var EXPRESSION_LOOSE_LIST_ITEM = /\n\n(?!\s*$)/;
191var EXPRESSION_TASK_ITEM = /^\[([\ \t]|x|X)\][\ \t]/;
192
193/*
194 * A map of characters, and their column length,
195 * which can be used as indentation.
196 */
197
198var INDENTATION_CHARACTERS = {};
199
200INDENTATION_CHARACTERS[SPACE] = SPACE.length;
201INDENTATION_CHARACTERS[TAB] = TAB_SIZE;
202
203/**
204 * Gets indentation information for a line.
205 *
206 * @example
207 * getIndent(' foo');
208 * // {indent: 2, stops: {1: 0, 2: 1}}
209 *
210 * getIndent('\tfoo');
211 * // {indent: 4, stops: {4: 0}}
212 *
213 * getIndent(' \tfoo');
214 * // {indent: 4, stops: {1: 0, 2: 1, 4: 2}}
215 *
216 * getIndent('\t foo')
217 * // {indent: 6, stops: {4: 0, 5: 1, 6: 2}}
218 *
219 * @param {string} value - Indented line.
220 * @return {Object}
221 */
222function getIndent(value) {
223 var index = 0;
224 var indent = 0;
225 var character = value.charAt(index);
226 var stops = {};
227 var size;
228
229 while (character in INDENTATION_CHARACTERS) {
230 size = INDENTATION_CHARACTERS[character];
231
232 indent += size;
233
234 if (size > 1) {
235 indent = Math.floor(indent / size) * size;
236 }
237
238 stops[indent] = index;
239
240 character = value.charAt(++index);
241 }
242
243 return {
244 'indent': indent,
245 'stops': stops
246 };
247}
248
249/**
250 * Remove the minimum indent from every line in `value`.
251 * Supports both tab, spaced, and mixed indentation (as
252 * well as possible).
253 *
254 * @example
255 * removeIndentation(' foo'); // 'foo'
256 * removeIndentation(' foo', 2); // ' foo'
257 * removeIndentation('\tfoo', 2); // ' foo'
258 * removeIndentation(' foo\n bar'); // ' foo\n bar'
259 *
260 * @param {string} value
261 * @param {number?} [maximum] - Maximum indentation
262 * to remove.
263 * @return {string} - Unindented `value`.
264 */
265function removeIndentation(value, maximum) {
266 var values = value.split(NEW_LINE);
267 var position = values.length + 1;
268 var minIndent = Infinity;
269 var matrix = [];
270 var index;
271 var indentation;
272 var stops;
273 var padding;
274
275 values.unshift(repeat(SPACE, maximum) + EXCLAMATION_MARK);
276
277 while (position--) {
278 indentation = getIndent(values[position]);
279
280 matrix[position] = indentation.stops;
281
282 if (trim(values[position]).length === 0) {
283 continue;
284 }
285
286 if (indentation.indent) {
287 if (indentation.indent > 0 && indentation.indent < minIndent) {
288 minIndent = indentation.indent;
289 }
290 } else {
291 minIndent = Infinity;
292
293 break;
294 }
295 }
296
297 if (minIndent !== Infinity) {
298 position = values.length;
299
300 while (position--) {
301 stops = matrix[position];
302 index = minIndent;
303
304 while (index && !(index in stops)) {
305 index--;
306 }
307
308 if (
309 trim(values[position]).length !== 0 &&
310 minIndent &&
311 index !== minIndent
312 ) {
313 padding = TAB;
314 } else {
315 padding = EMPTY;
316 }
317
318 values[position] = padding + values[position].slice(
319 index in stops ? stops[index] + 1 : 0
320 );
321 }
322 }
323
324 values.shift();
325
326 return values.join(NEW_LINE);
327}
328
329/**
330 * Ensure that `value` is at least indented with
331 * `indent` spaces. Does not support tabs. Does support
332 * multiple lines.
333 *
334 * @example
335 * ensureIndentation('foo', 2); // ' foo'
336 * ensureIndentation(' foo', 4); // ' foo'
337 *
338 * @param {string} value
339 * @param {number} indent - The maximum amount of
340 * spacing to insert.
341 * @return {string} - indented `value`.
342 */
343function ensureIndentation(value, indent) {
344 var values = value.split(NEW_LINE);
345 var length = values.length;
346 var index = -1;
347 var line;
348 var position;
349
350 while (++index < length) {
351 line = values[index];
352
353 position = -1;
354
355 while (++position < indent) {
356 if (line.charAt(position) !== SPACE) {
357 values[index] = repeat(SPACE, indent - position) + line;
358 break;
359 }
360 }
361 }
362
363 return values.join(NEW_LINE);
364}
365
366/**
367 * Get the alignment from a table rule.
368 *
369 * @example
370 * getAlignment([':-', ':-:', '-:', '--']);
371 * // ['left', 'center', 'right', null];
372 *
373 * @param {Array.<string>} cells
374 * @return {Array.<string?>}
375 */
376function getAlignment(cells) {
377 var results = [];
378 var index = -1;
379 var length = cells.length;
380 var alignment;
381
382 while (++index < length) {
383 alignment = cells[index];
384
385 if (EXPRESSION_RIGHT_ALIGNMENT.test(alignment)) {
386 results[index] = 'right';
387 } else if (EXPRESSION_CENTER_ALIGNMENT.test(alignment)) {
388 results[index] = 'center';
389 } else if (EXPRESSION_LEFT_ALIGNMENT.test(alignment)) {
390 results[index] = 'left';
391 } else {
392 results[index] = null;
393 }
394 }
395
396 return results;
397}
398
399/**
400 * Construct a state `toggler`: a function which inverses
401 * `property` in context based on its current value.
402 * The by `toggler` returned function restores that value.
403 *
404 * @example
405 * var context = {};
406 * var key = 'foo';
407 * var val = true;
408 * context[key] = val;
409 * context.enter = stateToggler(key, val);
410 * context[key]; // true
411 * var exit = context.enter();
412 * context[key]; // false
413 * var nested = context.enter();
414 * context[key]; // false
415 * nested();
416 * context[key]; // false
417 * exit();
418 * context[key]; // true
419 *
420 * @param {string} key - Property to toggle.
421 * @param {boolean} state - It's default state.
422 * @return {function(): function()} - Enter.
423 */
424function stateToggler(key, state) {
425 /**
426 * Construct a toggler for the bound `key`.
427 *
428 * @return {Function} - Exit state.
429 */
430 function enter() {
431 var self = this;
432 var current = self[key];
433
434 self[key] = !state;
435
436 /**
437 * State canceler, cancels the state, if allowed.
438 */
439 function exit() {
440 self[key] = current;
441 }
442
443 return exit;
444 }
445
446 return enter;
447}
448
449/**
450 * Construct a state toggler which doesn't toggle.
451 *
452 * @example
453 * var context = {};
454 * var key = 'foo';
455 * var val = true;
456 * context[key] = val;
457 * context.enter = noopToggler();
458 * context[key]; // true
459 * var exit = context.enter();
460 * context[key]; // true
461 * exit();
462 * context[key]; // true
463 *
464 * @return {function(): function()} - Enter.
465 */
466function noopToggler() {
467 /**
468 * No-operation.
469 */
470 function exit() {}
471
472 /**
473 * @return {Function}
474 */
475 function enter() {
476 return exit;
477 }
478
479 return enter;
480}
481
482/*
483 * Define nodes of a type which can be merged.
484 */
485
486var MERGEABLE_NODES = {};
487
488/**
489 * Merge two text nodes: `node` into `prev`.
490 *
491 * @param {Object} prev - Preceding sibling.
492 * @param {Object} node - Following sibling.
493 * @return {Object} - `prev`.
494 */
495MERGEABLE_NODES.text = function (prev, node) {
496 prev.value += node.value;
497
498 return prev;
499};
500
501/**
502 * Merge two blockquotes: `node` into `prev`, unless in
503 * CommonMark mode.
504 *
505 * @param {Object} prev - Preceding sibling.
506 * @param {Object} node - Following sibling.
507 * @return {Object} - `prev`, or `node` in CommonMark mode.
508 */
509MERGEABLE_NODES.blockquote = function (prev, node) {
510 if (this.options.commonmark) {
511 return node;
512 }
513
514 prev.children = prev.children.concat(node.children);
515
516 return prev;
517};
518
519/**
520 * Merge two lists: `node` into `prev`. Knows, about
521 * which bullets were used.
522 *
523 * @param {Object} prev - Preceding sibling.
524 * @param {Object} node - Following sibling.
525 * @return {Object} - `prev`, or `node` when the lists are
526 * of different types (a different bullet is used).
527 */
528MERGEABLE_NODES.list = function (prev, node) {
529 if (
530 !this.currentBullet ||
531 this.currentBullet !== this.previousBullet ||
532 this.currentBullet.length !== 1
533 ) {
534 return node;
535 }
536
537 prev.children = prev.children.concat(node.children);
538
539 return prev;
540};
541
542/**
543 * Tokenise a line. Unsets `currentBullet` and
544 * `previousBullet` if more than one lines are found, thus
545 * preventing lists from merging when they use different
546 * bullets.
547 *
548 * @example
549 * tokenizeNewline(eat, '\n\n');
550 *
551 * @param {function(string)} eat
552 * @param {string} $0 - Lines.
553 */
554function tokenizeNewline(eat, $0) {
555 if ($0.length > 1) {
556 this.currentBullet = null;
557 this.previousBullet = null;
558 }
559
560 eat($0);
561}
562
563/**
564 * Tokenise an indented code block.
565 *
566 * @example
567 * tokenizeCode(eat, '\tfoo');
568 *
569 * @param {function(string)} eat
570 * @param {string} $0 - Whole code.
571 * @return {Node} - `code` node.
572 */
573function tokenizeCode(eat, $0) {
574 $0 = trimTrailingLines($0);
575
576 return eat($0)(this.renderCodeBlock(
577 removeIndentation($0, TAB_SIZE), null, eat)
578 );
579}
580
581/**
582 * Tokenise a fenced code block.
583 *
584 * @example
585 * var $0 = '```js\nfoo()\n```';
586 * tokenizeFences(eat, $0, '', '```', '`', 'js', 'foo()\n');
587 *
588 * @param {function(string)} eat
589 * @param {string} $0 - Whole code.
590 * @param {string} $1 - Initial spacing.
591 * @param {string} $2 - Initial fence.
592 * @param {string} $3 - Fence marker.
593 * @param {string} $4 - Programming language flag.
594 * @param {string} $5 - Content.
595 * @return {Node} - `code` node.
596 */
597function tokenizeFences(eat, $0, $1, $2, $3, $4, $5) {
598 $0 = trimTrailingLines($0);
599
600 /*
601 * If the initial fence was preceded by spaces,
602 * exdent that amount of white space from the code
603 * block. Because it's possible that the code block
604 * is exdented, we first have to ensure at least
605 * those spaces are available.
606 */
607
608 if ($1) {
609 $5 = removeIndentation(ensureIndentation($5, $1.length), $1.length);
610 }
611
612 return eat($0)(this.renderCodeBlock($5, $4, eat));
613}
614
615/**
616 * Tokenise an ATX-style heading.
617 *
618 * @example
619 * tokenizeHeading(eat, ' # foo', ' ', '#', ' ', 'foo');
620 *
621 * @param {function(string)} eat
622 * @param {string} $0 - Whole heading.
623 * @param {string} $1 - Initial spacing.
624 * @param {string} $2 - Hashes.
625 * @param {string} $3 - Internal spacing.
626 * @param {string} $4 - Content.
627 * @return {Node} - `heading` node.
628 */
629function tokenizeHeading(eat, $0, $1, $2, $3, $4) {
630 var now = eat.now();
631
632 now.column += ($1 + $2 + ($3 || '')).length;
633
634 return eat($0)(this.renderHeading($4, $2.length, now));
635}
636
637/**
638 * Tokenise a Setext-style heading.
639 *
640 * @example
641 * tokenizeLineHeading(eat, 'foo\n===', '', 'foo', '=');
642 *
643 * @param {function(string)} eat
644 * @param {string} $0 - Whole heading.
645 * @param {string} $1 - Initial spacing.
646 * @param {string} $2 - Content.
647 * @param {string} $3 - Underline marker.
648 * @return {Node} - `heading` node.
649 */
650function tokenizeLineHeading(eat, $0, $1, $2, $3) {
651 var now = eat.now();
652
653 now.column += $1.length;
654
655 return eat($0)(this.renderHeading($2, $3 === EQUALS ? 1 : 2, now));
656}
657
658/**
659 * Tokenise a horizontal rule.
660 *
661 * @example
662 * tokenizeHorizontalRule(eat, '***');
663 *
664 * @param {function(string)} eat
665 * @param {string} $0 - Whole rule.
666 * @return {Node} - `horizontalRule` node.
667 */
668function tokenizeHorizontalRule(eat, $0) {
669 return eat($0)(this.renderVoid(HORIZONTAL_RULE));
670}
671
672/**
673 * Tokenise a blockquote.
674 *
675 * @example
676 * tokenizeBlockquote(eat, '> Foo');
677 *
678 * @param {function(string)} eat
679 * @param {string} $0 - Whole blockquote.
680 * @return {Node} - `blockquote` node.
681 */
682function tokenizeBlockquote(eat, $0) {
683 var now = eat.now();
684 var indent = this.indent(now.line);
685 var value = trimTrailingLines($0);
686 var add = eat(value);
687
688 value = value.replace(EXPRESSION_BLOCK_QUOTE, function (prefix) {
689 indent(prefix.length);
690
691 return '';
692 });
693
694 return add(this.renderBlockquote(value, now));
695}
696
697/**
698 * Tokenise a list.
699 *
700 * @example
701 * tokenizeList(eat, '- Foo', '', '-');
702 *
703 * @param {function(string)} eat
704 * @param {string} $0 - Whole list.
705 * @param {string} $1 - Indent.
706 * @param {string} $2 - Bullet.
707 * @return {Node} - `list` node.
708 */
709function tokenizeList(eat, $0, $1, $2) {
710 var self = this;
711 var firstBullet = $2;
712 var value = trimTrailingLines($0);
713 var matches = value.match(self.rules.item);
714 var length = matches.length;
715 var index = 0;
716 var isLoose = false;
717 var now;
718 var bullet;
719 var item;
720 var enterTop;
721 var exitBlockquote;
722 var node;
723 var indent;
724 var size;
725 var position;
726 var end;
727
728 /*
729 * Determine if all list-items belong to the
730 * same list.
731 */
732
733 if (!self.options.pedantic) {
734 while (++index < length) {
735 bullet = self.rules.bullet.exec(matches[index])[0];
736
737 if (
738 firstBullet !== bullet &&
739 (
740 firstBullet.length === 1 && bullet.length === 1 ||
741 bullet.charAt(bullet.length - 1) !==
742 firstBullet.charAt(firstBullet.length - 1)
743 )
744 ) {
745 matches = matches.slice(0, index);
746 matches[index - 1] = trimTrailingLines(matches[index - 1]);
747
748 length = matches.length;
749
750 break;
751 }
752 }
753 }
754
755 if (self.options.commonmark) {
756 index = -1;
757
758 while (++index < length) {
759 item = matches[index];
760 indent = self.rules.indent.exec(item);
761 indent = indent[1] + repeat(SPACE, indent[2].length) + indent[3];
762 size = getIndent(indent).indent;
763 position = indent.length;
764 end = item.length;
765
766 while (++position < end) {
767 if (
768 item.charAt(position) === NEW_LINE &&
769 item.charAt(position - 1) === NEW_LINE &&
770 getIndent(item.slice(position + 1)).indent < size
771 ) {
772 matches[index] = item.slice(0, position - 1);
773
774 matches = matches.slice(0, index + 1);
775 length = matches.length;
776
777 break;
778 }
779 }
780 }
781 }
782
783 self.previousBullet = self.currentBullet;
784 self.currentBullet = firstBullet;
785
786 index = -1;
787
788 node = eat(matches.join(NEW_LINE)).reset(
789 self.renderList([], firstBullet)
790 );
791
792 enterTop = self.exitTop();
793 exitBlockquote = self.enterBlockquote();
794
795 while (++index < length) {
796 item = matches[index];
797 now = eat.now();
798
799 item = eat(item)(self.renderListItem(item, now), node);
800
801 if (item.loose) {
802 isLoose = true;
803 }
804
805 if (index !== length - 1) {
806 eat(NEW_LINE);
807 }
808 }
809
810 node.loose = isLoose;
811
812 enterTop();
813 exitBlockquote();
814
815 return node;
816}
817
818/**
819 * Tokenise HTML.
820 *
821 * @example
822 * tokenizeHtml(eat, '<span>foo</span>');
823 *
824 * @param {function(string)} eat
825 * @param {string} $0 - Whole HTML.
826 * @return {Node} - `html` node.
827 */
828function tokenizeHtml(eat, $0) {
829 $0 = trimTrailingLines($0);
830
831 return eat($0)(this.renderRaw(HTML, $0));
832}
833
834/**
835 * Tokenise a definition.
836 *
837 * @example
838 * var $0 = '[foo]: http://example.com "Example Domain"';
839 * var $1 = 'foo';
840 * var $2 = 'http://example.com';
841 * var $3 = 'Example Domain';
842 * tokenizeDefinition(eat, $0, $1, $2, $3);
843 *
844 * @property {boolean} onlyAtTop
845 * @property {boolean} notInBlockquote
846 * @param {function(string)} eat
847 * @param {string} $0 - Whole definition.
848 * @param {string} $1 - Key.
849 * @param {string} $2 - URL.
850 * @param {string} $3 - Title.
851 * @return {Node} - `definition` node.
852 */
853function tokenizeDefinition(eat, $0, $1, $2, $3) {
854 var link = $2;
855
856 /*
857 * Remove angle-brackets from `link`.
858 */
859
860 if (link.charAt(0) === LT && link.charAt(link.length - 1) === GT) {
861 link = link.slice(1, -1);
862 }
863
864 return eat($0)({
865 'type': 'definition',
866 'identifier': normalize($1),
867 'title': $3 ? decode(this.descape($3), eat) : null,
868 'link': decode(this.descape(link), eat)
869 });
870}
871
872tokenizeDefinition.onlyAtTop = true;
873tokenizeDefinition.notInBlockquote = true;
874
875/**
876 * Tokenise YAML front matter.
877 *
878 * @example
879 * var $0 = '---\nfoo: bar\n---';
880 * var $1 = 'foo: bar';
881 * tokenizeYAMLFrontMatter(eat, $0, $1);
882 *
883 * @property {boolean} onlyAtStart
884 * @param {function(string)} eat
885 * @param {string} $0 - Whole front matter.
886 * @param {string} $1 - Content.
887 * @return {Node} - `yaml` node.
888 */
889function tokenizeYAMLFrontMatter(eat, $0, $1) {
890 return eat($0)(this.renderRaw(YAML, $1 ? trimTrailingLines($1) : EMPTY));
891}
892
893tokenizeYAMLFrontMatter.onlyAtStart = true;
894
895/**
896 * Tokenise a footnote definition.
897 *
898 * @example
899 * var $0 = '[foo]: Bar.';
900 * var $1 = '[foo]';
901 * var $2 = 'foo';
902 * var $3 = 'Bar.';
903 * tokenizeFootnoteDefinition(eat, $0, $1, $2, $3);
904 *
905 * @property {boolean} onlyAtTop
906 * @property {boolean} notInBlockquote
907 * @param {function(string)} eat
908 * @param {string} $0 - Whole definition.
909 * @param {string} $1 - Whole key.
910 * @param {string} $2 - Key.
911 * @param {string} $3 - Whole value.
912 * @return {Node} - `footnoteDefinition` node.
913 */
914function tokenizeFootnoteDefinition(eat, $0, $1, $2, $3) {
915 var self = this;
916 var now = eat.now();
917 var indent = self.indent(now.line);
918
919 $3 = $3.replace(EXPRESSION_INITIAL_TAB, function (value) {
920 indent(value.length);
921
922 return EMPTY;
923 });
924
925 now.column += $1.length;
926
927 return eat($0)(self.renderFootnoteDefinition(normalize($2), $3, now));
928}
929
930tokenizeFootnoteDefinition.onlyAtTop = true;
931tokenizeFootnoteDefinition.notInBlockquote = true;
932
933/**
934 * Tokenise a table.
935 *
936 * @example
937 * var $0 = ' | foo |\n | --- |\n | bar |';
938 * var $1 = ' | foo |';
939 * var $2 = '| foo |';
940 * var $3 = ' | --- |';
941 * var $4 = '| --- |';
942 * var $5 = ' | bar |';
943 * tokenizeTable(eat, $0, $1, $2, $3, $4, $5);
944 *
945 * @property {boolean} onlyAtTop
946 * @param {function(string)} eat
947 * @param {string} $0 - Whole table.
948 * @param {string} $1 - Whole heading.
949 * @param {string} $2 - Trimmed heading.
950 * @param {string} $3 - Whole alignment.
951 * @param {string} $4 - Trimmed alignment.
952 * @param {string} $5 - Rows.
953 * @return {Node} - `table` node.
954 */
955function tokenizeTable(eat, $0, $1, $2, $3, $4, $5) {
956 var self = this;
957 var length;
958 var index;
959 var node;
960
961 $0 = trimTrailingLines($0);
962
963 node = eat($0).reset({
964 'type': TABLE,
965 'align': [],
966 'children': []
967 });
968
969 /**
970 * Eat a row of type `type`.
971 *
972 * @param {string} type - Type of the returned node,
973 * such as `tableHeader` or `tableRow`.
974 * @param {string} value - Row, including initial and
975 * final fences.
976 */
977 function renderRow(type, value) {
978 var row = eat(value).reset(self.renderParent(type, []), node);
979 var length = value.length + 1;
980 var index = -1;
981 var queue = '';
982 var cell = '';
983 var preamble = true;
984 var count;
985 var opening;
986 var character;
987 var subvalue;
988 var now;
989
990 while (++index < length) {
991 character = value.charAt(index);
992
993 if (character === '\t' || character === ' ') {
994 if (cell) {
995 queue += character;
996 } else {
997 eat(character);
998 }
999
1000 continue;
1001 }
1002
1003 if (character === '|' || character === '') {
1004 if (preamble) {
1005 eat(character);
1006 } else {
1007 if (character && opening) {
1008 // cell += queue + character;
1009 queue += character;
1010 continue;
1011 }
1012
1013 if ((cell || character) && !preamble) {
1014 subvalue = cell;
1015
1016 if (queue.length > 1) {
1017 if (character) {
1018 subvalue += queue.slice(0, queue.length - 1);
1019 queue = queue.charAt(queue.length - 1);
1020 } else {
1021 subvalue += queue;
1022 queue = '';
1023 }
1024 }
1025
1026 now = eat.now();
1027
1028 eat(subvalue)(
1029 self.renderInline(TABLE_CELL, cell, now), row
1030 );
1031 }
1032
1033 eat(queue + character);
1034
1035 queue = '';
1036 cell = '';
1037 }
1038 } else {
1039 if (queue) {
1040 cell += queue;
1041 queue = '';
1042 }
1043
1044 cell += character;
1045
1046 if (character === '\\' && index !== length - 2) {
1047 cell += value.charAt(index + 1);
1048 index++;
1049 }
1050
1051 if (character === '`') {
1052 count = 1;
1053
1054 while (value.charAt(index + 1) === character) {
1055 cell += character;
1056 index++;
1057 count++;
1058 }
1059
1060 if (!opening) {
1061 opening = count;
1062 } else if (count >= opening) {
1063 opening = 0;
1064 }
1065 }
1066 }
1067
1068 preamble = false;
1069 }
1070 }
1071
1072 /*
1073 * Add the table's header.
1074 */
1075
1076 renderRow(TABLE_HEADER, $1);
1077
1078 eat(NEW_LINE);
1079
1080 /*
1081 * Add the table's alignment.
1082 */
1083
1084 eat($3);
1085
1086 $4 = $4
1087 .replace(EXPRESSION_TABLE_FENCE, EMPTY)
1088 .split(EXPRESSION_TABLE_BORDER);
1089
1090 node.align = getAlignment($4);
1091
1092 /*
1093 * Add the table rows to table's children.
1094 */
1095
1096 $5 = trimTrailingLines($5).split(NEW_LINE);
1097
1098 index = -1;
1099 length = $5.length;
1100
1101 while (++index < length) {
1102 renderRow(TABLE_ROW, $5[index]);
1103
1104 if (index !== length - 1) {
1105 eat(NEW_LINE);
1106 }
1107 }
1108
1109 return node;
1110}
1111
1112tokenizeTable.onlyAtTop = true;
1113
1114/**
1115 * Tokenise a paragraph node.
1116 *
1117 * @example
1118 * tokenizeParagraph(eat, 'Foo.');
1119 *
1120 * @param {function(string)} eat
1121 * @param {string} $0 - Whole paragraph.
1122 * @return {Node?} - `paragraph` node, when the node does
1123 * not just contain white space.
1124 */
1125function tokenizeParagraph(eat, $0) {
1126 var now = eat.now();
1127
1128 if (trim($0) === EMPTY) {
1129 eat($0);
1130
1131 return null;
1132 }
1133
1134 $0 = trimTrailingLines($0);
1135
1136 return eat($0)(this.renderInline(PARAGRAPH, $0, now));
1137}
1138
1139/**
1140 * Tokenise a text node.
1141 *
1142 * @example
1143 * tokenizeText(eat, 'foo');
1144 *
1145 * @param {function(string)} eat
1146 * @param {string} $0 - Whole text.
1147 * @return {Node} - `text` node.
1148 */
1149function tokenizeText(eat, $0) {
1150 return eat($0)(this.renderRaw(TEXT, $0));
1151}
1152
1153/**
1154 * Create a code-block node.
1155 *
1156 * @example
1157 * renderCodeBlock('foo()', 'js', now());
1158 *
1159 * @param {string?} [value] - Code.
1160 * @param {string?} [language] - Optional language flag.
1161 * @param {Function} eat
1162 * @return {Object} - `code` node.
1163 */
1164function renderCodeBlock(value, language, eat) {
1165 return {
1166 'type': CODE,
1167 'lang': language ? decode(this.descape(language), eat) : null,
1168 'value': trimTrailingLines(value || EMPTY)
1169 };
1170}
1171
1172/**
1173 * Create a list node.
1174 *
1175 * @example
1176 * var children = [renderListItem('- foo')];
1177 * renderList(children, '-');
1178 *
1179 * @param {string} children - Children.
1180 * @param {string} bullet - First bullet.
1181 * @return {Object} - `list` node.
1182 */
1183function renderList(children, bullet) {
1184 var start = parseInt(bullet, 10);
1185
1186 if (start !== start) {
1187 start = null;
1188 }
1189
1190 /*
1191 * `loose` should be added later.
1192 */
1193
1194 return {
1195 'type': LIST,
1196 'ordered': bullet.length > 1,
1197 'start': start,
1198 'loose': null,
1199 'children': children
1200 };
1201}
1202
1203/**
1204 * Create a list-item using overly simple mechanics.
1205 *
1206 * @example
1207 * renderPedanticListItem('- _foo_', now());
1208 *
1209 * @param {string} value - List-item.
1210 * @param {Object} position - List-item location.
1211 * @return {string} - Cleaned `value`.
1212 */
1213function renderPedanticListItem(value, position) {
1214 var self = this;
1215 var indent = self.indent(position.line);
1216
1217 /**
1218 * A simple replacer which removed all matches,
1219 * and adds their length to `offset`.
1220 *
1221 * @param {string} $0
1222 * @return {string}
1223 */
1224 function replacer($0) {
1225 indent($0.length);
1226
1227 return EMPTY;
1228 }
1229
1230 /*
1231 * Remove the list-item's bullet.
1232 */
1233
1234 value = value.replace(EXPRESSION_PEDANTIC_BULLET, replacer);
1235
1236 /*
1237 * The initial line was also matched by the below, so
1238 * we reset the `line`.
1239 */
1240
1241 indent = self.indent(position.line);
1242
1243 return value.replace(EXPRESSION_INITIAL_INDENT, replacer);
1244}
1245
1246/**
1247 * Create a list-item using sane mechanics.
1248 *
1249 * @example
1250 * renderNormalListItem('- _foo_', now());
1251 *
1252 * @param {string} value - List-item.
1253 * @param {Object} position - List-item location.
1254 * @return {string} - Cleaned `value`.
1255 */
1256function renderNormalListItem(value, position) {
1257 var self = this;
1258 var indent = self.indent(position.line);
1259 var bullet;
1260 var rest;
1261 var lines;
1262 var trimmedLines;
1263 var index;
1264 var length;
1265 var max;
1266
1267 /*
1268 * Remove the list-item's bullet.
1269 */
1270
1271 value = value.replace(EXPRESSION_BULLET, function ($0, $1, $2, $3, $4) {
1272 bullet = $1 + $2 + $3;
1273 rest = $4;
1274
1275 /*
1276 * Make sure that the first nine numbered list items
1277 * can indent with an extra space. That is, when
1278 * the bullet did not receive an extra final space.
1279 */
1280
1281 if (Number($2) < 10 && bullet.length % 2 === 1) {
1282 $2 = SPACE + $2;
1283 }
1284
1285 max = $1 + repeat(SPACE, $2.length) + $3;
1286
1287 return max + rest;
1288 });
1289
1290 lines = value.split(NEW_LINE);
1291
1292 trimmedLines = removeIndentation(
1293 value, getIndent(max).indent
1294 ).split(NEW_LINE);
1295
1296 /*
1297 * We replaced the initial bullet with something
1298 * else above, which was used to trick
1299 * `removeIndentation` into removing some more
1300 * characters when possible. However, that could
1301 * result in the initial line to be stripped more
1302 * than it should be.
1303 */
1304
1305 trimmedLines[0] = rest;
1306
1307 indent(bullet.length);
1308
1309 index = 0;
1310 length = lines.length;
1311
1312 while (++index < length) {
1313 indent(lines[index].length - trimmedLines[index].length);
1314 }
1315
1316 return trimmedLines.join(NEW_LINE);
1317}
1318
1319/*
1320 * A map of two functions which can create list items.
1321 */
1322
1323var LIST_ITEM_MAP = {};
1324
1325LIST_ITEM_MAP.true = renderPedanticListItem;
1326LIST_ITEM_MAP.false = renderNormalListItem;
1327
1328/**
1329 * Create a list-item node.
1330 *
1331 * @example
1332 * renderListItem('- _foo_', now());
1333 *
1334 * @param {Object} value - List-item.
1335 * @param {Object} position - List-item location.
1336 * @return {Object} - `listItem` node.
1337 */
1338function renderListItem(value, position) {
1339 var self = this;
1340 var checked = null;
1341 var node;
1342 var task;
1343 var indent;
1344
1345 value = LIST_ITEM_MAP[self.options.pedantic].apply(self, arguments);
1346
1347 if (self.options.gfm) {
1348 task = value.match(EXPRESSION_TASK_ITEM);
1349
1350 if (task) {
1351 indent = task[0].length;
1352 checked = task[1].toLowerCase() === 'x';
1353
1354 self.indent(position.line)(indent);
1355 value = value.slice(indent);
1356 }
1357 }
1358
1359 node = {
1360 'type': LIST_ITEM,
1361 'loose': EXPRESSION_LOOSE_LIST_ITEM.test(value) ||
1362 value.charAt(value.length - 1) === NEW_LINE
1363 };
1364
1365 if (self.options.gfm) {
1366 node.checked = checked;
1367 }
1368
1369 node.children = self.tokenizeBlock(value, position);
1370
1371 return node;
1372}
1373
1374/**
1375 * Create a footnote-definition node.
1376 *
1377 * @example
1378 * renderFootnoteDefinition('1', '_foo_', now());
1379 *
1380 * @param {string} identifier - Unique reference.
1381 * @param {string} value - Contents
1382 * @param {Object} position - Definition location.
1383 * @return {Object} - `footnoteDefinition` node.
1384 */
1385function renderFootnoteDefinition(identifier, value, position) {
1386 var self = this;
1387 var exitBlockquote = self.enterBlockquote();
1388 var node;
1389
1390 node = {
1391 'type': FOOTNOTE_DEFINITION,
1392 'identifier': identifier,
1393 'children': self.tokenizeBlock(value, position)
1394 };
1395
1396 exitBlockquote();
1397
1398 return node;
1399}
1400
1401/**
1402 * Create a heading node.
1403 *
1404 * @example
1405 * renderHeading('_foo_', 1, now());
1406 *
1407 * @param {string} value - Content.
1408 * @param {number} depth - Heading depth.
1409 * @param {Object} position - Heading content location.
1410 * @return {Object} - `heading` node
1411 */
1412function renderHeading(value, depth, position) {
1413 return {
1414 'type': HEADING,
1415 'depth': depth,
1416 'children': this.tokenizeInline(value, position)
1417 };
1418}
1419
1420/**
1421 * Create a blockquote node.
1422 *
1423 * @example
1424 * renderBlockquote('_foo_', eat);
1425 *
1426 * @param {string} value - Content.
1427 * @param {Object} now - Position.
1428 * @return {Object} - `blockquote` node.
1429 */
1430function renderBlockquote(value, now) {
1431 var self = this;
1432 var exitBlockquote = self.enterBlockquote();
1433 var node = {
1434 'type': BLOCKQUOTE,
1435 'children': this.tokenizeBlock(value, now)
1436 };
1437
1438 exitBlockquote();
1439
1440 return node;
1441}
1442
1443/**
1444 * Create a void node.
1445 *
1446 * @example
1447 * renderVoid('horizontalRule');
1448 *
1449 * @param {string} type - Node type.
1450 * @return {Object} - Node of type `type`.
1451 */
1452function renderVoid(type) {
1453 return {
1454 'type': type
1455 };
1456}
1457
1458/**
1459 * Create a parent.
1460 *
1461 * @example
1462 * renderParent('paragraph', '_foo_');
1463 *
1464 * @param {string} type - Node type.
1465 * @param {Array.<Object>} children - Child nodes.
1466 * @return {Object} - Node of type `type`.
1467 */
1468function renderParent(type, children) {
1469 return {
1470 'type': type,
1471 'children': children
1472 };
1473}
1474
1475/**
1476 * Create a raw node.
1477 *
1478 * @example
1479 * renderRaw('inlineCode', 'foo()');
1480 *
1481 * @param {string} type - Node type.
1482 * @param {string} value - Contents.
1483 * @return {Object} - Node of type `type`.
1484 */
1485function renderRaw(type, value) {
1486 return {
1487 'type': type,
1488 'value': value
1489 };
1490}
1491
1492/**
1493 * Create a link node.
1494 *
1495 * @example
1496 * renderLink(true, 'example.com', 'example', 'Example Domain', now(), eat);
1497 * renderLink(false, 'fav.ico', 'example', 'Example Domain', now(), eat);
1498 *
1499 * @param {boolean} isLink - Whether linking to a document
1500 * or an image.
1501 * @param {string} href - URI reference.
1502 * @param {string} text - Content.
1503 * @param {string?} title - Title.
1504 * @param {Object} position - Location of link.
1505 * @param {function(string)} eat
1506 * @return {Object} - `link` or `image` node.
1507 */
1508function renderLink(isLink, href, text, title, position, eat) {
1509 var self = this;
1510 var exitLink = self.enterLink();
1511 var node;
1512
1513 node = {
1514 'type': isLink ? LINK : IMAGE,
1515 'title': title ? decode(self.descape(title), eat) : null
1516 };
1517
1518 href = decode(href, eat);
1519
1520 if (isLink) {
1521 node.href = href;
1522 node.children = self.tokenizeInline(text, position);
1523 } else {
1524 node.src = href;
1525 node.alt = text ? decode(self.descape(text), eat) : null;
1526 }
1527
1528 exitLink();
1529
1530 return node;
1531}
1532
1533/**
1534 * Create a footnote node.
1535 *
1536 * @example
1537 * renderFootnote('_foo_', now());
1538 *
1539 * @param {string} value - Contents.
1540 * @param {Object} position - Location of footnote.
1541 * @return {Object} - `footnote` node.
1542 */
1543function renderFootnote(value, position) {
1544 return this.renderInline(FOOTNOTE, value, position);
1545}
1546
1547/**
1548 * Add a node with inline content.
1549 *
1550 * @example
1551 * renderInline('strong', '_foo_', now());
1552 *
1553 * @param {string} type - Node type.
1554 * @param {string} value - Contents.
1555 * @param {Object} position - Location of node.
1556 * @return {Object} - Node of type `type`.
1557 */
1558function renderInline(type, value, position) {
1559 return this.renderParent(type, this.tokenizeInline(value, position));
1560}
1561
1562/**
1563 * Add a node with block content.
1564 *
1565 * @example
1566 * renderBlock('blockquote', 'Foo.', now());
1567 *
1568 * @param {string} type - Node type.
1569 * @param {string} value - Contents.
1570 * @param {Object} position - Location of node.
1571 * @return {Object} - Node of type `type`.
1572 */
1573function renderBlock(type, value, position) {
1574 return this.renderParent(type, this.tokenizeBlock(value, position));
1575}
1576
1577/**
1578 * Tokenise an escape sequence.
1579 *
1580 * @example
1581 * tokenizeEscape(eat, '\\a', 'a');
1582 *
1583 * @param {function(string)} eat
1584 * @param {string} $0 - Whole escape.
1585 * @param {string} $1 - Escaped character.
1586 * @return {Node} - `escape` node.
1587 */
1588function tokenizeEscape(eat, $0, $1) {
1589 return eat($0)(this.renderRaw(ESCAPE, $1));
1590}
1591
1592/**
1593 * Tokenise a URL in carets.
1594 *
1595 * @example
1596 * tokenizeAutoLink(eat, '<http://foo.bar>', 'http://foo.bar', '');
1597 *
1598 * @property {boolean} notInLink
1599 * @param {function(string)} eat
1600 * @param {string} $0 - Whole link.
1601 * @param {string} $1 - URL.
1602 * @param {string?} [$2] - Protocol or at.
1603 * @return {Node} - `link` node.
1604 */
1605function tokenizeAutoLink(eat, $0, $1, $2) {
1606 var self = this;
1607 var href = $1;
1608 var text = $1;
1609 var now = eat.now();
1610 var offset = 1;
1611 var tokenize;
1612 var node;
1613
1614 if ($2 === AT_SIGN) {
1615 if (
1616 text.substr(0, MAILTO_PROTOCOL.length).toLowerCase() !==
1617 MAILTO_PROTOCOL
1618 ) {
1619 href = MAILTO_PROTOCOL + text;
1620 } else {
1621 text = text.substr(MAILTO_PROTOCOL.length);
1622 offset += MAILTO_PROTOCOL.length;
1623 }
1624 }
1625
1626 now.column += offset;
1627
1628 /*
1629 * Temporarily remove support for escapes in autolinks.
1630 */
1631
1632 tokenize = self.inlineTokenizers.escape;
1633 self.inlineTokenizers.escape = null;
1634
1635 node = eat($0)(self.renderLink(true, href, text, null, now, eat));
1636
1637 self.inlineTokenizers.escape = tokenize;
1638
1639 return node;
1640}
1641
1642tokenizeAutoLink.notInLink = true;
1643
1644/**
1645 * Tokenise a URL in text.
1646 *
1647 * @example
1648 * tokenizeURL(eat, 'http://foo.bar');
1649 *
1650 * @property {boolean} notInLink
1651 * @param {function(string)} eat
1652 * @param {string} $0 - Whole link.
1653 * @return {Node} - `link` node.
1654 */
1655function tokenizeURL(eat, $0) {
1656 var now = eat.now();
1657
1658 return eat($0)(this.renderLink(true, $0, $0, null, now, eat));
1659}
1660
1661tokenizeURL.notInLink = true;
1662
1663/**
1664 * Tokenise an HTML tag.
1665 *
1666 * @example
1667 * tokenizeTag(eat, '<span foo="bar">');
1668 *
1669 * @param {function(string)} eat
1670 * @param {string} $0 - Content.
1671 * @return {Node} - `html` node.
1672 */
1673function tokenizeTag(eat, $0) {
1674 var self = this;
1675
1676 if (!self.inLink && EXPRESSION_HTML_LINK_OPEN.test($0)) {
1677 self.inLink = true;
1678 } else if (self.inLink && EXPRESSION_HTML_LINK_CLOSE.test($0)) {
1679 self.inLink = false;
1680 }
1681
1682 return eat($0)(self.renderRaw(HTML, $0));
1683}
1684
1685/**
1686 * Tokenise a link.
1687 *
1688 * @example
1689 * tokenizeLink(
1690 * eat, '![foo](fav.ico "Favicon")', '![', 'foo', null,
1691 * 'fav.ico', 'Foo Domain'
1692 * );
1693 *
1694 * @param {function(string)} eat
1695 * @param {string} $0 - Whole link.
1696 * @param {string} $1 - Prefix.
1697 * @param {string} $2 - Text.
1698 * @param {string?} $3 - URL wrapped in angle braces.
1699 * @param {string?} $4 - Literal URL.
1700 * @param {string?} $5 - Title wrapped in single or double
1701 * quotes.
1702 * @param {string?} [$6] - Title wrapped in double quotes.
1703 * @param {string?} [$7] - Title wrapped in parentheses.
1704 * @return {Node?} - `link` node, `image` node, or `null`.
1705 */
1706function tokenizeLink(eat, $0, $1, $2, $3, $4, $5, $6, $7) {
1707 var isLink = $1 === BRACKET_OPEN;
1708 var href = $4 || $3 || '';
1709 var title = $7 || $6 || $5;
1710 var now;
1711
1712 if (!isLink || !this.inLink) {
1713 now = eat.now();
1714
1715 now.column += $1.length;
1716
1717 return eat($0)(this.renderLink(
1718 isLink, this.descape(href), $2, title, now, eat
1719 ));
1720 }
1721
1722 return null;
1723}
1724
1725/**
1726 * Tokenise a reference link, image, or footnote;
1727 * shortcut reference link, or footnote.
1728 *
1729 * @example
1730 * tokenizeReference(eat, '[foo]', '[', 'foo');
1731 * tokenizeReference(eat, '[foo][]', '[', 'foo', '');
1732 * tokenizeReference(eat, '[foo][bar]', '[', 'foo', 'bar');
1733 *
1734 * @param {function(string)} eat
1735 * @param {string} $0 - Whole link.
1736 * @param {string} $1 - Prefix.
1737 * @param {string} $2 - identifier.
1738 * @param {string} $3 - Content.
1739 * @return {Node?} - `linkReference`, `imageReference`, or
1740 * `footnoteReference`. Returns null when this is a link
1741 * reference, but we're already in a link.
1742 */
1743function tokenizeReference(eat, $0, $1, $2, $3) {
1744 var self = this;
1745 var text = $2;
1746 var identifier = $3 || $2;
1747 var type = $1 === BRACKET_OPEN ? 'link' : 'image';
1748 var isFootnote = self.options.footnotes && identifier.charAt(0) === CARET;
1749 var now = eat.now();
1750 var referenceType;
1751 var node;
1752 var exitLink;
1753
1754 if ($3 === undefined) {
1755 referenceType = 'shortcut';
1756 } else if ($3 === '') {
1757 referenceType = 'collapsed';
1758 } else {
1759 referenceType = 'full';
1760 }
1761
1762 if (referenceType !== 'shortcut') {
1763 isFootnote = false;
1764 }
1765
1766 if (isFootnote) {
1767 identifier = identifier.substr(1);
1768 }
1769
1770 if (isFootnote) {
1771 if (identifier.indexOf(SPACE) !== -1) {
1772 return eat($0)(self.renderFootnote(identifier, eat.now()));
1773 } else {
1774 type = 'footnote';
1775 }
1776 }
1777
1778 if (self.inLink && type === 'link') {
1779 return null;
1780 }
1781
1782 now.column += $1.length;
1783
1784 node = {
1785 'type': type + 'Reference',
1786 'identifier': normalize(identifier)
1787 };
1788
1789 if (type === 'link' || type === 'image') {
1790 node.referenceType = referenceType;
1791 }
1792
1793 if (type === 'link') {
1794 exitLink = self.enterLink();
1795 node.children = self.tokenizeInline(text, now);
1796 exitLink();
1797 } else if (type === 'image') {
1798 node.alt = decode(self.descape(text), eat);
1799 }
1800
1801 return eat($0)(node);
1802}
1803
1804/**
1805 * Tokenise strong emphasis.
1806 *
1807 * @example
1808 * tokenizeStrong(eat, '**foo**', '**', 'foo');
1809 * tokenizeStrong(eat, '__foo__', null, null, '__', 'foo');
1810 *
1811 * @param {function(string)} eat
1812 * @param {string} $0 - Whole emphasis.
1813 * @param {string?} $1 - Marker.
1814 * @param {string?} $2 - Content.
1815 * @param {string?} [$3] - Marker.
1816 * @param {string?} [$4] - Content.
1817 * @return {Node?} - `strong` node, when not empty.
1818 */
1819function tokenizeStrong(eat, $0, $1, $2, $3, $4) {
1820 var now = eat.now();
1821 var value = $2 || $4;
1822
1823 if (trim(value) === EMPTY) {
1824 return null;
1825 }
1826
1827 now.column += 2;
1828
1829 return eat($0)(this.renderInline(STRONG, value, now));
1830}
1831
1832/**
1833 * Tokenise slight emphasis.
1834 *
1835 * @example
1836 * tokenizeEmphasis(eat, '*foo*', '*', 'foo');
1837 * tokenizeEmphasis(eat, '_foo_', null, null, '_', 'foo');
1838 *
1839 * @param {function(string)} eat
1840 * @param {string} $0 - Whole emphasis.
1841 * @param {string?} $1 - Marker.
1842 * @param {string?} $2 - Content.
1843 * @param {string?} [$3] - Marker.
1844 * @param {string?} [$4] - Content.
1845 * @return {Node?} - `emphasis` node, when not empty.
1846 */
1847function tokenizeEmphasis(eat, $0, $1, $2, $3, $4) {
1848 var now = eat.now();
1849 var marker = $1 || $3;
1850 var value = $2 || $4;
1851
1852 if (
1853 trim(value) === EMPTY ||
1854 value.charAt(0) === marker ||
1855 value.charAt(value.length - 1) === marker
1856 ) {
1857 return null;
1858 }
1859
1860 now.column += 1;
1861
1862 return eat($0)(this.renderInline(EMPHASIS, value, now));
1863}
1864
1865/**
1866 * Tokenise a deletion.
1867 *
1868 * @example
1869 * tokenizeDeletion(eat, '~~foo~~', '~~', 'foo');
1870 *
1871 * @param {function(string)} eat
1872 * @param {string} $0 - Whole deletion.
1873 * @param {string} $1 - Content.
1874 * @return {Node} - `delete` node.
1875 */
1876function tokenizeDeletion(eat, $0, $1) {
1877 var now = eat.now();
1878
1879 now.column += 2;
1880
1881 return eat($0)(this.renderInline(DELETE, $1, now));
1882}
1883
1884/**
1885 * Tokenise inline code.
1886 *
1887 * @example
1888 * tokenizeInlineCode(eat, '`foo()`', '`', 'foo()');
1889 *
1890 * @param {function(string)} eat
1891 * @param {string} $0 - Whole code.
1892 * @param {string} $1 - Initial markers.
1893 * @param {string} $2 - Content.
1894 * @return {Node} - `inlineCode` node.
1895 */
1896function tokenizeInlineCode(eat, $0, $1, $2) {
1897 return eat($0)(this.renderRaw(INLINE_CODE, trim($2 || '')));
1898}
1899
1900/**
1901 * Tokenise a break.
1902 *
1903 * @example
1904 * tokenizeBreak(eat, ' \n');
1905 *
1906 * @param {function(string)} eat
1907 * @param {string} $0
1908 * @return {Node} - `break` node.
1909 */
1910function tokenizeBreak(eat, $0) {
1911 return eat($0)(this.renderVoid(BREAK));
1912}
1913
1914/**
1915 * Construct a new parser.
1916 *
1917 * @example
1918 * var parser = new Parser(new VFile('Foo'));
1919 *
1920 * @constructor
1921 * @class {Parser}
1922 * @param {VFile} file - File to parse.
1923 * @param {Object?} [options] - Passed to
1924 * `Parser#setOptions()`.
1925 */
1926function Parser(file, options) {
1927 var self = this;
1928 var rules = extend({}, self.expressions.rules);
1929
1930 self.file = file;
1931 self.inLink = false;
1932 self.atTop = true;
1933 self.atStart = true;
1934 self.inBlockquote = false;
1935
1936 self.rules = rules;
1937 self.descape = descapeFactory(rules, 'escape');
1938
1939 self.options = extend({}, self.options);
1940
1941 self.setOptions(options);
1942}
1943
1944/**
1945 * Set options. Does not overwrite previously set
1946 * options.
1947 *
1948 * @example
1949 * var parser = new Parser();
1950 * parser.setOptions({gfm: true});
1951 *
1952 * @this {Parser}
1953 * @throws {Error} - When an option is invalid.
1954 * @param {Object?} [options] - Parse settings.
1955 * @return {Parser} - `self`.
1956 */
1957Parser.prototype.setOptions = function (options) {
1958 var self = this;
1959 var expressions = self.expressions;
1960 var rules = self.rules;
1961 var current = self.options;
1962 var key;
1963
1964 if (options === null || options === undefined) {
1965 options = {};
1966 } else if (typeof options === 'object') {
1967 options = extend({}, options);
1968 } else {
1969 raise(options, 'options');
1970 }
1971
1972 self.options = options;
1973
1974 for (key in defaultOptions) {
1975 validate.boolean(options, key, current[key]);
1976
1977 if (options[key]) {
1978 extend(rules, expressions[key]);
1979 }
1980 }
1981
1982 if (options.gfm && options.breaks) {
1983 extend(rules, expressions.breaksGFM);
1984 }
1985
1986 if (options.gfm && options.commonmark) {
1987 extend(rules, expressions.commonmarkGFM);
1988 }
1989
1990 if (options.commonmark) {
1991 self.enterBlockquote = noopToggler();
1992 }
1993
1994 return self;
1995};
1996
1997/*
1998 * Expose `defaults`.
1999 */
2000
2001Parser.prototype.options = defaultOptions;
2002
2003/*
2004 * Expose `expressions`.
2005 */
2006
2007Parser.prototype.expressions = defaultExpressions;
2008
2009/**
2010 * Factory to track indentation for each line corresponding
2011 * to the given `start` and the number of invocations.
2012 *
2013 * @param {number} start - Starting line.
2014 * @return {function(offset)} - Indenter.
2015 */
2016Parser.prototype.indent = function (start) {
2017 var self = this;
2018 var line = start;
2019
2020 /**
2021 * Intender which increments the global offset,
2022 * starting at the bound line, and further incrementing
2023 * each line for each invocation.
2024 *
2025 * @example
2026 * indenter(2)
2027 *
2028 * @param {number} offset - Number to increment the
2029 * offset.
2030 */
2031 function indenter(offset) {
2032 self.offset[line] = (self.offset[line] || 0) + offset;
2033
2034 line++;
2035 }
2036
2037 return indenter;
2038};
2039
2040/**
2041 * Parse the bound file.
2042 *
2043 * @example
2044 * new Parser(new File('_Foo_.')).parse();
2045 *
2046 * @this {Parser}
2047 * @return {Object} - `root` node.
2048 */
2049Parser.prototype.parse = function () {
2050 var self = this;
2051 var value = clean(String(self.file));
2052 var node;
2053
2054 /*
2055 * Add an `offset` matrix, used to keep track of
2056 * syntax and white space indentation per line.
2057 */
2058
2059 self.offset = {};
2060
2061 node = self.renderBlock(ROOT, value);
2062
2063 if (self.options.position) {
2064 node.position = {
2065 'start': {
2066 'line': 1,
2067 'column': 1
2068 }
2069 };
2070
2071 node.position.end = self.eof || node.position.start;
2072 }
2073
2074 return node;
2075};
2076
2077/*
2078 * Enter and exit helpers.
2079 */
2080
2081Parser.prototype.enterLink = stateToggler('inLink', false);
2082Parser.prototype.exitTop = stateToggler('atTop', true);
2083Parser.prototype.exitStart = stateToggler('atStart', true);
2084Parser.prototype.enterBlockquote = stateToggler('inBlockquote', false);
2085
2086/*
2087 * Expose helpers
2088 */
2089
2090Parser.prototype.renderRaw = renderRaw;
2091Parser.prototype.renderVoid = renderVoid;
2092Parser.prototype.renderParent = renderParent;
2093Parser.prototype.renderInline = renderInline;
2094Parser.prototype.renderBlock = renderBlock;
2095
2096Parser.prototype.renderLink = renderLink;
2097Parser.prototype.renderCodeBlock = renderCodeBlock;
2098Parser.prototype.renderBlockquote = renderBlockquote;
2099Parser.prototype.renderList = renderList;
2100Parser.prototype.renderListItem = renderListItem;
2101Parser.prototype.renderFootnoteDefinition = renderFootnoteDefinition;
2102Parser.prototype.renderHeading = renderHeading;
2103Parser.prototype.renderFootnote = renderFootnote;
2104
2105/**
2106 * Construct a tokenizer. This creates both
2107 * `tokenizeInline` and `tokenizeBlock`.
2108 *
2109 * @example
2110 * Parser.prototype.tokenizeInline = tokenizeFactory('inline');
2111 *
2112 * @param {string} type - Name of parser, used to find
2113 * its expressions (`%sMethods`) and tokenizers
2114 * (`%Tokenizers`).
2115 * @return {function(string, Object?): Array.<Object>}
2116 */
2117function tokenizeFactory(type) {
2118 /**
2119 * Tokenizer for a bound `type`
2120 *
2121 * @example
2122 * parser = new Parser();
2123 * parser.tokenizeInline('_foo_');
2124 *
2125 * @param {string} value - Content.
2126 * @param {Object?} [location] - Offset at which `value`
2127 * starts.
2128 * @return {Array.<Object>} - Nodes.
2129 */
2130 function tokenize(value, location) {
2131 var self = this;
2132 var offset = self.offset;
2133 var tokens = [];
2134 var rules = self.rules;
2135 var methods = self[type + 'Methods'];
2136 var tokenizers = self[type + 'Tokenizers'];
2137 var line = location ? location.line : 1;
2138 var column = location ? location.column : 1;
2139 var patchPosition = self.options.position;
2140 var add;
2141 var index;
2142 var length;
2143 var method;
2144 var name;
2145 var match;
2146 var matched;
2147 var valueLength;
2148 var eater;
2149
2150 /*
2151 * Trim white space only lines.
2152 */
2153
2154 if (!value) {
2155 return tokens;
2156 }
2157
2158 /**
2159 * Update line and column based on `value`.
2160 *
2161 * @example
2162 * updatePosition('foo');
2163 *
2164 * @param {string} subvalue
2165 */
2166 function updatePosition(subvalue) {
2167 var character = -1;
2168 var subvalueLength = subvalue.length;
2169 var lastIndex = -1;
2170
2171 while (++character < subvalueLength) {
2172 if (subvalue.charAt(character) === NEW_LINE) {
2173 lastIndex = character;
2174 line++;
2175 }
2176 }
2177
2178 if (lastIndex === -1) {
2179 column = column + subvalue.length;
2180 } else {
2181 column = subvalue.length - lastIndex;
2182 }
2183
2184 if (line in offset) {
2185 if (lastIndex !== -1) {
2186 column += offset[line];
2187 } else if (column <= offset[line]) {
2188 column = offset[line] + 1;
2189 }
2190 }
2191 }
2192
2193 /**
2194 * Get offset. Called before the fisrt character is
2195 * eaten to retrieve the range's offsets.
2196 *
2197 * @return {Function} - `done`, to be called when
2198 * the last character is eaten.
2199 */
2200 function getOffset() {
2201 var indentation = [];
2202 var pos = line + 1;
2203
2204 /**
2205 * Done. Called when the last character is
2206 * eaten to retrieve the range's offsets.
2207 *
2208 * @return {Array.<number>} - Offset.
2209 */
2210 function done() {
2211 var last = line + 1;
2212
2213 while (pos < last) {
2214 indentation.push((offset[pos] || 0) + 1);
2215
2216 pos++;
2217 }
2218
2219 return indentation;
2220 }
2221
2222 return done;
2223 }
2224
2225 /**
2226 * Get the current position.
2227 *
2228 * @example
2229 * position = now(); // {line: 1, column: 1}
2230 *
2231 * @return {Object}
2232 */
2233 function now() {
2234 return {
2235 'line': line,
2236 'column': column
2237 };
2238 }
2239
2240 /**
2241 * Store position information for a node.
2242 *
2243 * @example
2244 * start = now();
2245 * updatePosition('foo');
2246 * location = new Position(start);
2247 * // {start: {line: 1, column: 1}, end: {line: 1, column: 3}}
2248 *
2249 * @param {Object} start
2250 */
2251 function Position(start) {
2252 this.start = start;
2253 this.end = now();
2254 }
2255
2256 /**
2257 * Throw when a value is incorrectly eaten.
2258 * This shouldn’t happen but will throw on new,
2259 * incorrect rules.
2260 *
2261 * @example
2262 * // When the current value is set to `foo bar`.
2263 * validateEat('foo');
2264 * eat('foo');
2265 *
2266 * validateEat('bar');
2267 * // throws, because the space is not eaten.
2268 *
2269 * @param {string} subvalue - Value to be eaten.
2270 * @throws {Error} - When `subvalue` cannot be eaten.
2271 */
2272 function validateEat(subvalue) {
2273 /* istanbul ignore if */
2274 if (value.substring(0, subvalue.length) !== subvalue) {
2275 self.file.fail(
2276 'Incorrectly eaten value: please report this ' +
2277 'warning on http://git.io/vUYWz', now()
2278 );
2279 }
2280 }
2281
2282 /**
2283 * Mark position and patch `node.position`.
2284 *
2285 * @example
2286 * var update = position();
2287 * updatePosition('foo');
2288 * update({});
2289 * // {
2290 * // position: {
2291 * // start: {line: 1, column: 1}
2292 * // end: {line: 1, column: 3}
2293 * // }
2294 * // }
2295 *
2296 * @returns {function(Node): Node}
2297 */
2298 function position() {
2299 var before = now();
2300
2301 /**
2302 * Add the position to a node.
2303 *
2304 * @example
2305 * update({type: 'text', value: 'foo'});
2306 *
2307 * @param {Node} node - Node to attach position
2308 * on.
2309 * @return {Node} - `node`.
2310 */
2311 function update(node, indent) {
2312 var prev = node.position;
2313 var start = prev ? prev.start : before;
2314 var combined = [];
2315 var n = prev && prev.end.line;
2316 var l = before.line;
2317
2318 node.position = new Position(start);
2319
2320 /*
2321 * If there was already a `position`, this
2322 * node was merged. Fixing `start` wasn't
2323 * hard, but the indent is different.
2324 * Especially because some information, the
2325 * indent between `n` and `l` wasn't
2326 * tracked. Luckily, that space is
2327 * (should be?) empty, so we can safely
2328 * check for it now.
2329 */
2330
2331 if (prev) {
2332 combined = prev.indent;
2333
2334 if (n < l) {
2335 while (++n < l) {
2336 combined.push((offset[n] || 0) + 1);
2337 }
2338
2339 combined.push(before.column);
2340 }
2341
2342 indent = combined.concat(indent);
2343 }
2344
2345 node.position.indent = indent;
2346
2347 return node;
2348 }
2349
2350 return update;
2351 }
2352
2353 /**
2354 * Add `node` to `parent`s children or to `tokens`.
2355 * Performs merges where possible.
2356 *
2357 * @example
2358 * add({});
2359 *
2360 * add({}, {children: []});
2361 *
2362 * @param {Object} node - Node to add.
2363 * @param {Object} [parent] - Parent to insert into.
2364 * @return {Object} - Added or merged into node.
2365 */
2366 add = function (node, parent) {
2367 var isMultiple = 'length' in node;
2368 var prev;
2369 var children;
2370
2371 if (!parent) {
2372 children = tokens;
2373 } else {
2374 children = parent.children;
2375 }
2376
2377 if (isMultiple) {
2378 arrayPush.apply(children, node);
2379 } else {
2380 if (type === INLINE && node.type === TEXT) {
2381 node.value = decode(node.value, eater);
2382 }
2383
2384 prev = children[children.length - 1];
2385
2386 if (
2387 prev &&
2388 node.type === prev.type &&
2389 node.type in MERGEABLE_NODES
2390 ) {
2391 node = MERGEABLE_NODES[node.type].call(
2392 self, prev, node
2393 );
2394 }
2395
2396 if (node !== prev) {
2397 children.push(node);
2398 }
2399
2400 if (self.atStart && tokens.length) {
2401 self.exitStart();
2402 }
2403 }
2404
2405 return node;
2406 };
2407
2408 /**
2409 * Remove `subvalue` from `value`.
2410 * Expects `subvalue` to be at the start from
2411 * `value`, and applies no validation.
2412 *
2413 * @example
2414 * eat('foo')({type: 'text', value: 'foo'});
2415 *
2416 * @param {string} subvalue - Removed from `value`,
2417 * and passed to `updatePosition`.
2418 * @return {Function} - Wrapper around `add`, which
2419 * also adds `position` to node.
2420 */
2421 function eat(subvalue) {
2422 var indent = getOffset();
2423 var pos = position();
2424 var current = now();
2425
2426 validateEat(subvalue);
2427
2428 /**
2429 * Add the given arguments, add `position` to
2430 * the returned node, and return the node.
2431 *
2432 * @return {Node}
2433 */
2434 function apply() {
2435 return pos(add.apply(null, arguments), indent);
2436 }
2437
2438 /**
2439 * Functions just like apply, but resets the
2440 * content: the line and column are reversed,
2441 * and the eaten value is re-added.
2442 *
2443 * This is useful for nodes with a single
2444 * type of content, such as lists and tables.
2445 *
2446 * See `apply` above for what parameters are
2447 * expected.
2448 *
2449 * @return {Node}
2450 */
2451 function reset() {
2452 var node = apply.apply(null, arguments);
2453
2454 line = current.line;
2455 column = current.column;
2456 value = subvalue + value;
2457
2458 return node;
2459 }
2460
2461 apply.reset = reset;
2462
2463 value = value.substring(subvalue.length);
2464
2465 updatePosition(subvalue);
2466
2467 indent = indent();
2468
2469 return apply;
2470 }
2471
2472 /**
2473 * Same as `eat` above, but will not add positional
2474 * information to nodes.
2475 *
2476 * @example
2477 * noEat('foo')({type: 'text', value: 'foo'});
2478 *
2479 * @param {string} subvalue - Removed from `value`.
2480 * @return {Function} - Wrapper around `add`.
2481 */
2482 function noEat(subvalue) {
2483 validateEat(subvalue);
2484
2485 /**
2486 * Add the given arguments, and return the
2487 * node.
2488 *
2489 * @return {Node}
2490 */
2491 function apply() {
2492 return add.apply(null, arguments);
2493 }
2494
2495 /**
2496 * Functions just like apply, but resets the
2497 * content: the eaten value is re-added.
2498 *
2499 * @return {Node}
2500 */
2501 function reset() {
2502 var node = apply.apply(null, arguments);
2503
2504 value = subvalue + value;
2505
2506 return node;
2507 }
2508
2509 apply.reset = reset;
2510
2511 value = value.substring(subvalue.length);
2512
2513 return apply;
2514 }
2515
2516 /*
2517 * Expose the eater, depending on if `position`s
2518 * should be patched on nodes.
2519 */
2520
2521 eater = patchPosition ? eat : noEat;
2522
2523 /*
2524 * Expose `now` on `eater`.
2525 */
2526
2527 eater.now = now;
2528
2529 /*
2530 * Expose `file` on `eater`.
2531 */
2532
2533 eater.file = self.file;
2534
2535 /*
2536 * Sync initial offset.
2537 */
2538
2539 updatePosition(EMPTY);
2540
2541 /*
2542 * Iterate over `value`, and iterate over all
2543 * block-expressions. When one matches, invoke
2544 * its companion function. If no expression
2545 * matches, something failed (should not happen)
2546 * and an exception is thrown.
2547 */
2548
2549 while (value) {
2550 index = -1;
2551 length = methods.length;
2552 matched = false;
2553
2554 while (++index < length) {
2555 name = methods[index];
2556 method = tokenizers[name];
2557
2558 if (
2559 method &&
2560 rules[name] &&
2561 (!method.onlyAtStart || self.atStart) &&
2562 (!method.onlyAtTop || self.atTop) &&
2563 (!method.notInBlockquote || !self.inBlockquote) &&
2564 (!method.notInLink || !self.inLink)
2565 ) {
2566 match = rules[name].exec(value);
2567
2568 if (match) {
2569 valueLength = value.length;
2570
2571 method.apply(self, [eater].concat(match));
2572
2573 matched = valueLength !== value.length;
2574
2575 if (matched) {
2576 break;
2577 }
2578 }
2579 }
2580 }
2581
2582 /* istanbul ignore if */
2583 if (!matched) {
2584 self.file.fail('Infinite loop', eater.now());
2585
2586 /*
2587 * Errors are not thrown on `File#fail`
2588 * when `quiet: true`.
2589 */
2590
2591 break;
2592 }
2593 }
2594
2595 self.eof = now();
2596
2597 return tokens;
2598 }
2599
2600 return tokenize;
2601}
2602
2603/*
2604 * Expose tokenizers for block-level nodes.
2605 */
2606
2607Parser.prototype.blockTokenizers = {
2608 'yamlFrontMatter': tokenizeYAMLFrontMatter,
2609 'newline': tokenizeNewline,
2610 'code': tokenizeCode,
2611 'fences': tokenizeFences,
2612 'heading': tokenizeHeading,
2613 'lineHeading': tokenizeLineHeading,
2614 'horizontalRule': tokenizeHorizontalRule,
2615 'blockquote': tokenizeBlockquote,
2616 'list': tokenizeList,
2617 'html': tokenizeHtml,
2618 'definition': tokenizeDefinition,
2619 'footnoteDefinition': tokenizeFootnoteDefinition,
2620 'looseTable': tokenizeTable,
2621 'table': tokenizeTable,
2622 'paragraph': tokenizeParagraph
2623};
2624
2625/*
2626 * Expose order in which to parse block-level nodes.
2627 */
2628
2629Parser.prototype.blockMethods = [
2630 'yamlFrontMatter',
2631 'newline',
2632 'code',
2633 'fences',
2634 'blockquote',
2635 'heading',
2636 'horizontalRule',
2637 'list',
2638 'lineHeading',
2639 'html',
2640 'definition',
2641 'footnoteDefinition',
2642 'looseTable',
2643 'table',
2644 'paragraph'
2645];
2646
2647/**
2648 * Block tokenizer.
2649 *
2650 * @example
2651 * var parser = new Parser();
2652 * parser.tokenizeBlock('> foo.');
2653 *
2654 * @param {string} value - Content.
2655 * @return {Array.<Object>} - Nodes.
2656 */
2657
2658Parser.prototype.tokenizeBlock = tokenizeFactory(BLOCK);
2659
2660/*
2661 * Expose tokenizers for inline-level nodes.
2662 */
2663
2664Parser.prototype.inlineTokenizers = {
2665 'escape': tokenizeEscape,
2666 'autoLink': tokenizeAutoLink,
2667 'url': tokenizeURL,
2668 'tag': tokenizeTag,
2669 'link': tokenizeLink,
2670 'reference': tokenizeReference,
2671 'shortcutReference': tokenizeReference,
2672 'strong': tokenizeStrong,
2673 'emphasis': tokenizeEmphasis,
2674 'deletion': tokenizeDeletion,
2675 'inlineCode': tokenizeInlineCode,
2676 'break': tokenizeBreak,
2677 'inlineText': tokenizeText
2678};
2679
2680/*
2681 * Expose order in which to parse inline-level nodes.
2682 */
2683
2684Parser.prototype.inlineMethods = [
2685 'escape',
2686 'autoLink',
2687 'url',
2688 'tag',
2689 'link',
2690 'reference',
2691 'shortcutReference',
2692 'strong',
2693 'emphasis',
2694 'deletion',
2695 'inlineCode',
2696 'break',
2697 'inlineText'
2698];
2699
2700/**
2701 * Inline tokenizer.
2702 *
2703 * @example
2704 * var parser = new Parser();
2705 * parser.tokenizeInline('_foo_');
2706 *
2707 * @param {string} value - Content.
2708 * @return {Array.<Object>} - Nodes.
2709 */
2710
2711Parser.prototype.tokenizeInline = tokenizeFactory(INLINE);
2712
2713/*
2714 * Expose `tokenizeFactory` so dependencies could create
2715 * their own tokenizers.
2716 */
2717
2718Parser.prototype.tokenizeFactory = tokenizeFactory;
2719
2720/*
2721 * Expose `parse` on `module.exports`.
2722 */
2723
2724module.exports = Parser;