tildefriends/apps/ssb/commonmark-linkify.js

92 lines
1.8 KiB
JavaScript
Raw Permalink Normal View History

function textNode(text) {
const node = new commonmark.Node("text", undefined);
node.literal = text;
return node;
}
function linkNode(text, url) {
const urlNode = new commonmark.Node("link", undefined);
urlNode.destination = url;
urlNode.appendChild(textNode(text));
return urlNode;
}
function splitMatches(text, regexp) {
// Regexp must be sticky.
regexp = new RegExp(regexp, "gm");
let i = 0;
const result = [];
let match = regexp.exec(text);
while (match) {
const matchText = match[0];
if (match.index > i) {
result.push([text.substring(i, match.index), false]);
}
result.push([matchText, true]);
i = match.index + matchText.length;
match = regexp.exec(text);
}
if (i < text.length) {
result.push([text.substring(i, text.length), false]);
}
return result;
}
const urlRegexp = new RegExp("https?://[^ ]+[^ .,]");
function splitURLs(textNodes) {
const text = textNodes.map(n => n.literal).join("");
const parts = splitMatches(text, urlRegexp);
return parts.map(part => {
if (part[1]) {
return linkNode(part[0], part[0]);
} else {
return textNode(part[0]);
}
});
}
export function transform(parsed) {
const walker = parsed.walker();
let event;
let nodes = [];
while ((event = walker.next())) {
const node = event.node;
if (event.entering && node.type === "text") {
nodes.push(node);
} else {
if (nodes.length > 0) {
splitURLs(nodes)
.reverse()
.forEach(newNode => {
nodes[0].insertAfter(newNode);
});
nodes.forEach(n => n.unlink());
nodes = [];
}
}
}
if (nodes.length > 0) {
splitURLs(nodes)
.reverse()
.forEach(newNode => {
nodes[0].insertAfter(newNode);
});
nodes.forEach(n => n.unlink());
}
return parsed;
}