| '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 { |
| // 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) !== 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) |
| } |