'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 T_LINK = 'link'; var T_IMAGE = 'image'; var T_FOOTNOTE = 'footnote'; var REFERENCE_TYPE_SHORTCUT = 'shortcut'; var REFERENCE_TYPE_COLLAPSED = 'collapsed'; var REFERENCE_TYPE_FULL = 'full'; var C_CARET = '^'; var C_BACKSLASH = '\\'; var C_BRACKET_OPEN = '['; var C_BRACKET_CLOSE = ']'; function reference(eat, value, silent) { var self = this; var character = value.charAt(0); var index = 0; var length = value.length; var subvalue = ''; var intro = ''; var type = T_LINK; var referenceType = REFERENCE_TYPE_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 === '!') { type = T_IMAGE; intro = character; character = value.charAt(++index); } if (character !== C_BRACKET_OPEN) { return; } index++; intro += character; queue = ''; /* Check whether we’re eating a footnote. */ if (self.options.footnotes && value.charAt(index) === C_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 === T_IMAGE) { return; } intro += C_CARET; index++; type = T_FOOTNOTE; } /* Eat the text. */ depth = 0; while (index < length) { character = value.charAt(index); if (character === C_BRACKET_OPEN) { bracketed = true; depth++; } else if (character === C_BRACKET_CLOSE) { if (!depth) { break; } depth--; } if (character === C_BACKSLASH) { queue += C_BACKSLASH; character = value.charAt(++index); } queue += character; index++; } subvalue = queue; content = queue; character = value.charAt(index); if (character !== C_BRACKET_CLOSE) { return; } index++; subvalue += character; queue = ''; 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 !== T_FOOTNOTE && character === C_BRACKET_OPEN) { identifier = ''; queue += character; index++; while (index < length) { character = value.charAt(index); if (character === C_BRACKET_OPEN || character === C_BRACKET_CLOSE) { break; } if (character === C_BACKSLASH) { identifier += C_BACKSLASH; character = value.charAt(++index); } identifier += character; index++; } character = value.charAt(index); if (character === C_BRACKET_CLOSE) { referenceType = identifier ? REFERENCE_TYPE_FULL : REFERENCE_TYPE_COLLAPSED; queue += identifier + character; index++; } else { identifier = ''; } subvalue += queue; queue = ''; } else { if (!content) { return; } identifier = content; } /* Brackets cannot be inside the identifier. */ if (referenceType !== REFERENCE_TYPE_FULL && bracketed) { return; } subvalue = intro + subvalue; if (type === T_LINK && self.inLink) { return null; } /* istanbul ignore if - never used (yet) */ if (silent) { return true; } if (type === T_FOOTNOTE && content.indexOf(' ') !== -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 === REFERENCE_TYPE_FULL ? identifier : content; node = { type: type + 'Reference', identifier: normalize(identifier) }; if (type === T_LINK || type === T_IMAGE) { node.referenceType = referenceType; } if (type === T_LINK) { exit = self.enterLink(); node.children = self.tokenizeInline(content, now); exit(); } else if (type === T_IMAGE) { node.alt = self.decode.raw(self.unescape(content), now) || null; } return eat(subvalue)(node); }