368 lines
7.1 KiB
JavaScript
368 lines
7.1 KiB
JavaScript
|
'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)
|
|||
|
}
|