| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- 'use strict';
- var whitespace = require('is-whitespace-character');
- var locate = require('../locate/link');
- module.exports = link;
- link.locator = locate;
- var own = {}.hasOwnProperty;
- var C_BACKSLASH = '\\';
- var C_BRACKET_OPEN = '[';
- var C_BRACKET_CLOSE = ']';
- var C_PAREN_OPEN = '(';
- var C_PAREN_CLOSE = ')';
- var C_LT = '<';
- var C_GT = '>';
- var C_TICK = '`';
- var C_DOUBLE_QUOTE = '"';
- var C_SINGLE_QUOTE = '\'';
- /* Map of characters, which can be used to mark link
- * and image titles. */
- var LINK_MARKERS = {};
- LINK_MARKERS[C_DOUBLE_QUOTE] = C_DOUBLE_QUOTE;
- LINK_MARKERS[C_SINGLE_QUOTE] = C_SINGLE_QUOTE;
- /* Map of characters, which can be used to mark link
- * and image titles in commonmark-mode. */
- var COMMONMARK_LINK_MARKERS = {};
- COMMONMARK_LINK_MARKERS[C_DOUBLE_QUOTE] = C_DOUBLE_QUOTE;
- COMMONMARK_LINK_MARKERS[C_SINGLE_QUOTE] = C_SINGLE_QUOTE;
- COMMONMARK_LINK_MARKERS[C_PAREN_OPEN] = C_PAREN_CLOSE;
- function link(eat, value, silent) {
- var self = this;
- var subvalue = '';
- var index = 0;
- var character = value.charAt(0);
- var pedantic = self.options.pedantic;
- var commonmark = self.options.commonmark;
- var gfm = self.options.gfm;
- var closed;
- var count;
- var opening;
- var beforeURL;
- var beforeTitle;
- var subqueue;
- var hasMarker;
- var markers;
- var isImage;
- var content;
- var marker;
- var length;
- var title;
- var depth;
- var queue;
- var url;
- var now;
- var exit;
- var node;
- /* Detect whether this is an image. */
- if (character === '!') {
- isImage = true;
- subvalue = character;
- character = value.charAt(++index);
- }
- /* Eat the opening. */
- if (character !== C_BRACKET_OPEN) {
- return;
- }
- /* Exit when this is a link and we’re already inside
- * a link. */
- if (!isImage && self.inLink) {
- return;
- }
- subvalue += character;
- queue = '';
- index++;
- /* Eat the content. */
- length = value.length;
- now = eat.now();
- depth = 0;
- now.column += index;
- now.offset += index;
- while (index < length) {
- character = value.charAt(index);
- subqueue = character;
- if (character === C_TICK) {
- /* Inline-code in link content. */
- count = 1;
- while (value.charAt(index + 1) === C_TICK) {
- subqueue += character;
- index++;
- count++;
- }
- if (!opening) {
- opening = count;
- } else if (count >= opening) {
- opening = 0;
- }
- } else if (character === C_BACKSLASH) {
- /* Allow brackets to be escaped. */
- index++;
- subqueue += value.charAt(index);
- /* In GFM mode, brackets in code still count.
- * In all other modes, they don’t. This empty
- * block prevents the next statements are
- * entered. */
- } else if ((!opening || gfm) && character === C_BRACKET_OPEN) {
- depth++;
- } else if ((!opening || gfm) && character === C_BRACKET_CLOSE) {
- if (depth) {
- depth--;
- } else {
- /* Allow white-space between content and
- * url in GFM mode. */
- if (!pedantic) {
- while (index < length) {
- character = value.charAt(index + 1);
- if (!whitespace(character)) {
- break;
- }
- subqueue += character;
- index++;
- }
- }
- if (value.charAt(index + 1) !== C_PAREN_OPEN) {
- return;
- }
- subqueue += C_PAREN_OPEN;
- closed = true;
- index++;
- break;
- }
- }
- queue += subqueue;
- subqueue = '';
- index++;
- }
- /* Eat the content closing. */
- if (!closed) {
- return;
- }
- content = queue;
- subvalue += queue + subqueue;
- index++;
- /* Eat white-space. */
- while (index < length) {
- character = value.charAt(index);
- if (!whitespace(character)) {
- break;
- }
- subvalue += character;
- index++;
- }
- /* Eat the URL. */
- character = value.charAt(index);
- markers = commonmark ? COMMONMARK_LINK_MARKERS : LINK_MARKERS;
- queue = '';
- beforeURL = subvalue;
- if (character === C_LT) {
- index++;
- beforeURL += C_LT;
- while (index < length) {
- character = value.charAt(index);
- if (character === C_GT) {
- break;
- }
- if (commonmark && character === '\n') {
- return;
- }
- queue += character;
- index++;
- }
- if (value.charAt(index) !== C_GT) {
- return;
- }
- subvalue += C_LT + queue + C_GT;
- url = queue;
- index++;
- } else {
- character = null;
- subqueue = '';
- while (index < length) {
- character = value.charAt(index);
- if (subqueue && own.call(markers, character)) {
- break;
- }
- if (whitespace(character)) {
- if (!pedantic) {
- break;
- }
- subqueue += character;
- } else {
- if (character === C_PAREN_OPEN) {
- depth++;
- } else if (character === C_PAREN_CLOSE) {
- if (depth === 0) {
- break;
- }
- depth--;
- }
- queue += subqueue;
- subqueue = '';
- if (character === C_BACKSLASH) {
- queue += C_BACKSLASH;
- character = value.charAt(++index);
- }
- queue += character;
- }
- index++;
- }
- subvalue += queue;
- url = queue;
- index = subvalue.length;
- }
- /* Eat white-space. */
- queue = '';
- while (index < length) {
- character = value.charAt(index);
- if (!whitespace(character)) {
- break;
- }
- queue += character;
- index++;
- }
- character = value.charAt(index);
- subvalue += queue;
- /* Eat the title. */
- if (queue && own.call(markers, character)) {
- index++;
- subvalue += character;
- queue = '';
- marker = markers[character];
- beforeTitle = subvalue;
- /* In commonmark-mode, things are pretty easy: the
- * marker cannot occur inside the title.
- *
- * Non-commonmark does, however, support nested
- * delimiters. */
- if (commonmark) {
- while (index < length) {
- character = value.charAt(index);
- if (character === marker) {
- break;
- }
- if (character === C_BACKSLASH) {
- queue += C_BACKSLASH;
- character = value.charAt(++index);
- }
- index++;
- queue += character;
- }
- character = value.charAt(index);
- if (character !== marker) {
- return;
- }
- title = queue;
- subvalue += queue + character;
- index++;
- while (index < length) {
- character = value.charAt(index);
- if (!whitespace(character)) {
- break;
- }
- subvalue += character;
- index++;
- }
- } else {
- subqueue = '';
- while (index < length) {
- character = value.charAt(index);
- if (character === marker) {
- if (hasMarker) {
- queue += marker + subqueue;
- subqueue = '';
- }
- hasMarker = true;
- } else if (!hasMarker) {
- queue += character;
- } else if (character === C_PAREN_CLOSE) {
- subvalue += queue + marker + subqueue;
- title = queue;
- break;
- } else if (whitespace(character)) {
- subqueue += character;
- } else {
- queue += marker + subqueue + character;
- subqueue = '';
- hasMarker = false;
- }
- index++;
- }
- }
- }
- if (value.charAt(index) !== C_PAREN_CLOSE) {
- return;
- }
- /* istanbul ignore if - never used (yet) */
- if (silent) {
- return true;
- }
- subvalue += C_PAREN_CLOSE;
- url = self.decode.raw(self.unescape(url), eat(beforeURL).test().end, {nonTerminated: false});
- if (title) {
- beforeTitle = eat(beforeTitle).test().end;
- title = self.decode.raw(self.unescape(title), beforeTitle);
- }
- node = {
- type: isImage ? 'image' : 'link',
- title: title || null,
- url: url
- };
- if (isImage) {
- node.alt = self.decode.raw(self.unescape(content), now) || null;
- } else {
- exit = self.enterLink();
- node.children = self.tokenizeInline(content, now);
- exit();
- }
- return eat(subvalue)(node);
- }
|