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