'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) }