UNPKG

@mapbox/jsxtreme-markdown

Version:
368 lines (299 loc) 7.4 kB
'use strict'; var whitespace = require('is-whitespace-character'); var locate = require('../locate/link'); module.exports = link; link.locator = locate; var lineFeed = '\n'; var exclamationMark = '!'; var quotationMark = '"'; var apostrophe = "'"; var leftParenthesis = '('; var rightParenthesis = ')'; var lessThan = '<'; var greaterThan = '>'; var leftSquareBracket = '['; var backslash = '\\'; var rightSquareBracket = ']'; var graveAccent = '`'; 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 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 === exclamationMark) { isImage = true; subvalue = character; character = value.charAt(++index); } // Eat the opening. if (character !== leftSquareBracket) { 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 === graveAccent) { // Inline-code in link content. count = 1; while (value.charAt(index + 1) === graveAccent) { subqueue += character; index++; count++; } if (!opening) { opening = count; } else if (count >= opening) { opening = 0; } } else if (character === backslash) { // Allow brackets to be escaped. index++; subqueue += value.charAt(index); } else if ((!opening || gfm) && character === leftSquareBracket) { // In GFM mode, brackets in code still count. In all other modes, // they don’t. depth++; } else if ((!opening || gfm) && character === rightSquareBracket) { if (depth) { depth--; } else { if (value.charAt(index + 1) !== leftParenthesis) { return; } subqueue += leftParenthesis; 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); queue = ''; beforeURL = subvalue; if (character === lessThan) { index++; beforeURL += lessThan; while (index < length) { character = value.charAt(index); if (character === greaterThan) { break; } if (commonmark && character === lineFeed) { return; } queue += character; index++; } if (value.charAt(index) !== greaterThan) { return; } subvalue += lessThan + queue + greaterThan; url = queue; index++; } else { character = null; subqueue = ''; while (index < length) { character = value.charAt(index); if ( subqueue && (character === quotationMark || character === apostrophe || (commonmark && character === leftParenthesis)) ) { break; } if (whitespace(character)) { if (!pedantic) { break; } subqueue += character; } else { if (character === leftParenthesis) { depth++; } else if (character === rightParenthesis) { if (depth === 0) { break; } depth--; } queue += subqueue; subqueue = ''; if (character === backslash) { queue += 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 && (character === quotationMark || character === apostrophe || (commonmark && character === leftParenthesis)) ) { index++; subvalue += character; queue = ''; marker = character === leftParenthesis ? rightParenthesis : 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 === backslash) { queue += 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 === rightParenthesis) { 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) !== rightParenthesis) { return; } /* istanbul ignore if - never used (yet) */ if (silent) { return true; } subvalue += rightParenthesis; 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); }