blob: 911e35b9fc8d0159187ace1bc4c53c672a791f9e [file] [log] [blame]
'use strict'
var whitespace = require('is-whitespace-character')
var locate = require('../locate/link')
var normalize = require('../util/normalize')
module.exports = reference
reference.locator = locate
var link = 'link'
var image = 'image'
var footnote = 'footnote'
var shortcut = 'shortcut'
var collapsed = 'collapsed'
var full = 'full'
var space = ' '
var exclamationMark = '!'
var leftSquareBracket = '['
var backslash = '\\'
var rightSquareBracket = ']'
var caret = '^'
function reference(eat, value, silent) {
var self = this
var commonmark = self.options.commonmark
var character = value.charAt(0)
var index = 0
var length = value.length
var subvalue = ''
var intro = ''
var type = link
var referenceType = shortcut
var content
var identifier
var now
var node
var exit
var queue
var bracketed
var depth
// Check whether we’re eating an image.
if (character === exclamationMark) {
type = image
intro = character
character = value.charAt(++index)
}
if (character !== leftSquareBracket) {
return
}
index++
intro += character
queue = ''
// Check whether we’re eating a footnote.
if (self.options.footnotes && value.charAt(index) === caret) {
// Exit if `![^` is found, so the `!` will be seen as text after this,
// and we’ll enter this function again when `[^` is found.
if (type === image) {
return
}
intro += caret
index++
type = footnote
}
// Eat the text.
depth = 0
while (index < length) {
character = value.charAt(index)
if (character === leftSquareBracket) {
bracketed = true
depth++
} else if (character === rightSquareBracket) {
if (!depth) {
break
}
depth--
}
if (character === backslash) {
queue += backslash
character = value.charAt(++index)
}
queue += character
index++
}
subvalue = queue
content = queue
character = value.charAt(index)
if (character !== rightSquareBracket) {
return
}
index++
subvalue += character
queue = ''
if (!commonmark) {
// The original markdown syntax definition explicitly allows for whitespace
// between the link text and link label; commonmark departs from this, in
// part to improve support for shortcut reference links
while (index < length) {
character = value.charAt(index)
if (!whitespace(character)) {
break
}
queue += character
index++
}
}
character = value.charAt(index)
// Inline footnotes cannot have an identifier.
if (type !== footnote && character === leftSquareBracket) {
identifier = ''
queue += character
index++
while (index < length) {
character = value.charAt(index)
if (character === leftSquareBracket || character === rightSquareBracket) {
break
}
if (character === backslash) {
identifier += backslash
character = value.charAt(++index)
}
identifier += character
index++
}
character = value.charAt(index)
if (character === rightSquareBracket) {
referenceType = identifier ? full : collapsed
queue += identifier + character
index++
} else {
identifier = ''
}
subvalue += queue
queue = ''
} else {
if (!content) {
return
}
identifier = content
}
// Brackets cannot be inside the identifier.
if (referenceType !== full && bracketed) {
return
}
subvalue = intro + subvalue
if (type === link && self.inLink) {
return null
}
/* istanbul ignore if - never used (yet) */
if (silent) {
return true
}
if (type === footnote && content.indexOf(space) !== -1) {
return eat(subvalue)({
type: footnote,
children: this.tokenizeInline(content, eat.now())
})
}
now = eat.now()
now.column += intro.length
now.offset += intro.length
identifier = referenceType === full ? identifier : content
node = {
type: type + 'Reference',
identifier: normalize(identifier),
label: identifier
}
if (type === link || type === image) {
node.referenceType = referenceType
}
if (type === link) {
exit = self.enterLink()
node.children = self.tokenizeInline(content, now)
exit()
} else if (type === image) {
node.alt = self.decode.raw(self.unescape(content), now) || null
}
return eat(subvalue)(node)
}