Markdown parser, done right. 100% CommonMark support, extensions, syntax plugins & high speed https://markdown-it.github.io/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

175 lines
4.4 KiB

// Process [links](<to> "stuff")
'use strict';
var parseLinkLabel = require('../helpers/parse_link_label');
var parseLinkDestination = require('../helpers/parse_link_destination');
var parseLinkTitle = require('../helpers/parse_link_title');
var normalizeReference = require('../helpers/normalize_reference');
var StateInline = require('../rules_inline/state_inline');
module.exports = function links(state, silent) {
var code,
href,
label,
labelEnd,
labelStart,
pos,
ref,
title,
tokens,
isImage = false,
oldPos = state.pos,
max = state.posMax,
start = state.pos,
marker = state.src.charCodeAt(start);
if (marker === 0x21/* ! */) {
isImage = true;
marker = state.src.charCodeAt(++start);
}
if (marker !== 0x5B/* [ */) { return false; }
if (state.level >= state.options.maxNesting) { return false; }
labelStart = start + 1;
labelEnd = parseLinkLabel(state, start, !isImage);
// parser failed to find ']', so it's not a valid link
if (labelEnd < 0) { return false; }
pos = labelEnd + 1;
if (pos < max && state.src.charCodeAt(pos) === 0x28/* ( */) {
//
// Inline link
//
// [link]( <href> "title" )
// ^^ skipping these spaces
pos++;
for (; pos < max; pos++) {
code = state.src.charCodeAt(pos);
if (code !== 0x20 && code !== 0x0A) { break; }
}
if (pos >= max) { return false; }
// [link]( <href> "title" )
// ^^^^^^ parsing link destination
start = pos;
if (parseLinkDestination(state, pos)) {
href = state.linkContent;
pos = state.pos;
} else {
href = '';
}
// [link]( <href> "title" )
// ^^ skipping these spaces
start = pos;
for (; pos < max; pos++) {
code = state.src.charCodeAt(pos);
if (code !== 0x20 && code !== 0x0A) { break; }
}
// [link]( <href> "title" )
// ^^^^^^^ parsing link title
if (pos < max && start !== pos && parseLinkTitle(state, pos)) {
title = state.linkContent;
pos = state.pos;
// [link]( <href> "title" )
// ^^ skipping these spaces
for (; pos < max; pos++) {
code = state.src.charCodeAt(pos);
if (code !== 0x20 && code !== 0x0A) { break; }
}
} else {
title = '';
}
if (pos >= max || state.src.charCodeAt(pos) !== 0x29/* ) */) {
state.pos = oldPos;
return false;
}
pos++;
} else {
//
// Link reference
//
if (typeof state.env.references === 'undefined') { return false; }
// [foo] [bar]
// ^^ optional whitespace (can include newlines)
for (; pos < max; pos++) {
code = state.src.charCodeAt(pos);
if (code !== 0x20 && code !== 0x0A) { break; }
}
if (pos < max && state.src.charCodeAt(pos) === 0x5B/* [ */) {
start = pos + 1;
pos = parseLinkLabel(state, pos);
if (pos >= 0) {
label = state.src.slice(start, pos++);
} else {
pos = labelEnd + 1;
}
} else {
pos = labelEnd + 1;
}
// covers label === '' and label === undefined
// (collapsed reference link and shortcut reference link respectively)
if (!label) { label = state.src.slice(labelStart, labelEnd); }
ref = state.env.references[normalizeReference(label)];
if (!ref) {
state.pos = oldPos;
return false;
}
href = ref.href;
title = ref.title;
}
//
// We found the end of the link, and know for a fact it's a valid link;
// so all that's left to do is to call tokenizer.
//
if (!silent) {
state.pos = labelStart;
state.posMax = labelEnd;
if (isImage) {
var newState = new StateInline(
state.src.slice(labelStart, labelEnd),
state.parser,
state.options,
state.env,
tokens = []
);
newState.parser.tokenize(newState);
state.push({
type: 'image',
src: href,
title: title,
tokens: tokens,
level: state.level
});
} else {
state.push({
type: 'link_open',
href: href,
target: '',
title: title,
level: state.level++
});
state.parser.tokenize(state);
state.push({ type: 'link_close', level: --state.level });
}
}
state.pos = pos;
state.posMax = max;
return true;
};