TeaWeb/shared/js/text/bbcode.tsx

122 lines
3.7 KiB
TypeScript
Raw Normal View History

2020-07-19 14:34:08 +00:00
import {XBBCodeRenderer} from "vendor/xbbcode/react";
import * as React from "react";
2020-12-21 18:13:25 +00:00
import {rendererHTML, rendererReact, rendererText, BBCodeHandlerContext} from "tc-shared/text/bbcode/renderer";
2020-07-19 14:34:08 +00:00
import {parse as parseBBCode} from "vendor/xbbcode/parser";
import {fixupJQueryUrlTags} from "tc-shared/text/bbcode/url";
import {fixupJQueryImageTags} from "tc-shared/text/bbcode/image";
2020-12-07 14:07:47 +00:00
import "./bbcode.scss";
2020-07-19 14:34:08 +00:00
2020-12-07 14:07:47 +00:00
export const escapeBBCode = (text: string) => text.replace(/(\[)/g, "\\$1");
2020-07-19 14:34:08 +00:00
export const allowedBBCodes = [
"b", "big",
"i", "italic",
"u", "underlined",
"s", "strikethrough",
2020-12-07 14:07:47 +00:00
"color", "bgcolor",
"url", "img",
2020-07-19 14:34:08 +00:00
"code",
"i-code", "icode",
"sub", "sup",
"size",
"hr", "br",
"left", "l", "center", "c", "right", "r",
"ul", "ol", "list",
2020-12-07 14:07:47 +00:00
"ulist", "olist",
2020-07-19 14:34:08 +00:00
"li",
2020-12-07 14:07:47 +00:00
"*",
2020-07-19 14:34:08 +00:00
"table",
"tr", "td", "th",
"yt", "youtube",
"quote"
];
export interface BBCodeRenderOptions {
convertSingleUrls: boolean;
}
2020-12-07 14:07:47 +00:00
const youtubeUrlRegex = /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/;
2020-07-19 14:34:08 +00:00
function preprocessMessage(message: string, settings: BBCodeRenderOptions) : string {
/* try if its only one url */
single_url_parse:
if(settings.convertSingleUrls) {
const raw_url = message.replace(/\[url(=\S+)?](\S+)\[\/url]/, "$2");
let url: URL;
try {
url = new URL(raw_url);
} catch(error) {
break single_url_parse;
}
single_url_yt:
{
2020-12-07 14:07:47 +00:00
const result = raw_url.match(youtubeUrlRegex);
2020-07-19 14:34:08 +00:00
if(!result) break single_url_yt;
if(result[5]?.length < 11) break single_url_yt;
2020-07-19 14:34:08 +00:00
return "[yt]https://www.youtube.com/watch?v=" + result[5] + "[/yt]";
}
single_url_image:
{
const ext_index = url.pathname.lastIndexOf(".");
if(ext_index == -1) break single_url_image;
const ext_name = url.pathname.substr(ext_index + 1).toLowerCase();
if([
"jpeg", "jpg",
"png", "bmp", "gif",
"tiff", "pdf", "svg"
].findIndex(e => e === ext_name) == -1) break single_url_image;
return "[img]" + message + "[/img]";
}
}
return message;
}
export const BBCodeRenderer = (props: { message: string, settings: BBCodeRenderOptions, handlerId?: string }) => {
if(props.handlerId) {
return (
<BBCodeHandlerContext.Provider value={props.handlerId} key={"handler-id"}>
<XBBCodeRenderer options={{ tag_whitelist: allowedBBCodes }} renderer={rendererReact}>
{preprocessMessage(props.message, props.settings)}
</XBBCodeRenderer>
</BBCodeHandlerContext.Provider>
);
} else {
return (
<XBBCodeRenderer options={{ tag_whitelist: allowedBBCodes }} renderer={rendererReact} key={"no-handler-id"}>
{preprocessMessage(props.message, props.settings)}
</XBBCodeRenderer>
);
}
}
2020-07-19 14:34:08 +00:00
export function renderBBCodeAsJQuery(message: string, settings: BBCodeRenderOptions) : JQuery[] {
const result = parseBBCode(preprocessMessage(message, settings), {
tag_whitelist: allowedBBCodes
});
let html = result.map(e => rendererHTML.render(e)).join("");
const container = $.spawn("div") as JQuery;
container[0].innerHTML = html;
/* fixup some listeners */
fixupJQueryUrlTags(container);
fixupJQueryImageTags(container);
return [container.contents() as JQuery];
}
export function renderBBCodeAsText(message: string) {
return parseBBCode(message, { tag_whitelist: allowedBBCodes }).map(e => rendererText.render(e)).join("");
2020-07-19 14:34:08 +00:00
}