2023-01-29 15:58:33 +00:00
|
|
|
import LinkifyIt, {Match} from "linkify-it";
|
|
|
|
import tlds from "tlds";
|
|
|
|
|
|
|
|
export type LinkPart = {
|
|
|
|
start: number;
|
|
|
|
end: number;
|
|
|
|
link: string;
|
|
|
|
};
|
|
|
|
|
|
|
|
const linkify = LinkifyIt().tlds(tlds).tlds("onion", true);
|
|
|
|
|
|
|
|
// Known schemes to detect in text
|
|
|
|
const commonSchemes = [
|
|
|
|
"sftp",
|
|
|
|
"smb",
|
|
|
|
"file",
|
|
|
|
"irc",
|
|
|
|
"ircs",
|
|
|
|
"svn",
|
|
|
|
"git",
|
|
|
|
"steam",
|
|
|
|
"mumble",
|
|
|
|
"ts3server",
|
|
|
|
"svn+ssh",
|
|
|
|
"ssh",
|
|
|
|
"gopher",
|
|
|
|
"gemini",
|
|
|
|
];
|
|
|
|
|
|
|
|
for (const schema of commonSchemes) {
|
|
|
|
linkify.add(schema + ":", "http:");
|
|
|
|
}
|
|
|
|
|
2024-01-21 15:42:12 +00:00
|
|
|
linkify.add("web+", {
|
|
|
|
validate(text: string, pos: number, self: LinkifyIt.LinkifyIt) {
|
|
|
|
const webSchemaRe = /^[a-z]+:/gi;
|
|
|
|
|
|
|
|
if (!webSchemaRe.test(text.slice(pos))) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
const linkEnd = self.testSchemaAt(text, "http:", pos + webSchemaRe.lastIndex);
|
|
|
|
|
|
|
|
if (linkEnd === 0) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return webSchemaRe.lastIndex + linkEnd;
|
|
|
|
},
|
|
|
|
normalize(match) {
|
|
|
|
match.schema = match.text.slice(0, match.text.indexOf(":") + 1);
|
2024-01-21 17:04:18 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
|
|
|
// we must rewrite protocol less urls to http, else if TL is hosted
|
|
|
|
// on https, this would incorrectly use https for the remote link.
|
|
|
|
// See https://github.com/thelounge/thelounge/issues/2525
|
|
|
|
//
|
|
|
|
// We take the validation logic from linkify and just add our own
|
|
|
|
// normalizer.
|
|
|
|
linkify.add("//", {
|
|
|
|
validate: (linkify as any).__schemas__["//"].validate,
|
|
|
|
normalize(match) {
|
|
|
|
match.schema = ""; // this counts as not having a schema
|
|
|
|
match.url = "http:" + match.url;
|
2024-01-21 15:42:12 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
|
2023-01-29 15:58:33 +00:00
|
|
|
export function findLinks(text: string) {
|
2024-01-21 17:04:18 +00:00
|
|
|
const matches = linkify.match(text);
|
2023-01-29 15:58:33 +00:00
|
|
|
|
|
|
|
if (!matches) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
|
|
|
return matches.map(makeLinkPart);
|
|
|
|
}
|
|
|
|
|
|
|
|
export function findLinksWithSchema(text: string) {
|
2024-01-21 17:04:18 +00:00
|
|
|
const matches = linkify.match(text);
|
2023-01-29 15:58:33 +00:00
|
|
|
|
|
|
|
if (!matches) {
|
|
|
|
return [];
|
|
|
|
}
|
|
|
|
|
2024-01-21 17:04:18 +00:00
|
|
|
return matches.filter((url) => !!url.schema).map(makeLinkPart);
|
2023-01-29 15:58:33 +00:00
|
|
|
}
|
|
|
|
|
2024-01-21 17:04:18 +00:00
|
|
|
function makeLinkPart(url: Match): LinkPart {
|
2023-01-29 15:58:33 +00:00
|
|
|
return {
|
|
|
|
start: url.index,
|
|
|
|
end: url.lastIndex,
|
|
|
|
link: url.url,
|
|
|
|
};
|
|
|
|
}
|