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.
111 lines
3.1 KiB
111 lines
3.1 KiB
// Replace link-like texts with link nodes.
|
|
//
|
|
// Currently restricted by `md.validateLink()` to http/https/ftp
|
|
//
|
|
'use strict';
|
|
|
|
|
|
var arrayReplaceAt = require('../common/utils').arrayReplaceAt;
|
|
var normalizeLink = require('../common/utils').normalizeLink;
|
|
var Token = require('../token');
|
|
|
|
|
|
function isLinkOpen(str) {
|
|
return /^<a[>\s]/i.test(str);
|
|
}
|
|
function isLinkClose(str) {
|
|
return /^<\/a\s*>/i.test(str);
|
|
}
|
|
|
|
|
|
module.exports = function linkify(state) {
|
|
var i, j, l, tokens, token, currentToken, nodes, ln, text, pos, lastPos, level, htmlLinkLevel,
|
|
blockTokens = state.tokens,
|
|
links;
|
|
|
|
if (!state.md.options.linkify) { return; }
|
|
|
|
for (j = 0, l = blockTokens.length; j < l; j++) {
|
|
if (blockTokens[j].type !== 'inline') { continue; }
|
|
|
|
tokens = blockTokens[j].children;
|
|
|
|
htmlLinkLevel = 0;
|
|
|
|
// We scan from the end, to keep position when new tags added.
|
|
// Use reversed logic in links start/end match
|
|
for (i = tokens.length - 1; i >= 0; i--) {
|
|
currentToken = tokens[i];
|
|
|
|
// Skip content of markdown links
|
|
if (currentToken.type === 'link_close') {
|
|
i--;
|
|
while (tokens[i].level !== currentToken.level && tokens[i].type !== 'link_open') {
|
|
i--;
|
|
}
|
|
continue;
|
|
}
|
|
|
|
// Skip content of html tag links
|
|
if (currentToken.type === 'html_inline') {
|
|
if (isLinkOpen(currentToken.content) && htmlLinkLevel > 0) {
|
|
htmlLinkLevel--;
|
|
}
|
|
if (isLinkClose(currentToken.content)) {
|
|
htmlLinkLevel++;
|
|
}
|
|
}
|
|
if (htmlLinkLevel > 0) { continue; }
|
|
|
|
if (currentToken.type === 'text' && state.md.linkify.test(currentToken.content)) {
|
|
|
|
text = currentToken.content;
|
|
links = state.md.linkify.match(text);
|
|
|
|
// Now split string to nodes
|
|
nodes = [];
|
|
level = currentToken.level;
|
|
lastPos = 0;
|
|
|
|
for (ln = 0; ln < links.length; ln++) {
|
|
|
|
if (!state.md.validateLink(links[ln].url)) { continue; }
|
|
|
|
pos = links[ln].index;
|
|
|
|
if (pos > lastPos) {
|
|
token = new Token('text', '', 0);
|
|
token.content = text.slice(lastPos, pos);
|
|
token.level = level;
|
|
nodes.push(token);
|
|
}
|
|
|
|
token = new Token('link_open', 'a', 1);
|
|
token.attrs = [ [ 'href', normalizeLink(links[ln].url) ] ];
|
|
token.level = level++;
|
|
nodes.push(token);
|
|
|
|
token = new Token('text', '', 0);
|
|
token.content = links[ln].text;
|
|
token.level = level;
|
|
nodes.push(token);
|
|
|
|
token = new Token('link_close', 'a', -1);
|
|
token.level = --level;
|
|
nodes.push(token);
|
|
|
|
lastPos = links[ln].lastIndex;
|
|
}
|
|
if (lastPos < text.length) {
|
|
token = new Token('text', '', 0);
|
|
token.content = text.slice(lastPos);
|
|
token.level = level;
|
|
nodes.push(token);
|
|
}
|
|
|
|
// replace current node
|
|
blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes);
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|