TeaWeb/shared/js/text/bbcode/image.tsx

92 lines
3.4 KiB
TypeScript

import {ElementRenderer} from "vendor/xbbcode/renderer/base";
import {BBCodeTagElement} from "vendor/xbbcode/elements";
import * as React from "react";
import * as loader from "tc-loader";
import {rendererReact, rendererText} from "tc-shared/text/bbcode/renderer";
import * as contextmenu from "tc-shared/ui/elements/ContextMenu";
import {copyToClipboard} from "tc-shared/utils/helpers";
import * as image_preview from "tc-shared/ui/frames/ImagePreview";
export const regexImage = /^(?:https?):(?:\/{1,3}|\\)[-a-zA-Z0-9:;,@#%&()~_?+=\/\\.]*$/g;
function loadImageForElement(element: HTMLImageElement) {
if(!element.hasAttribute("x-image-url")) {
return;
}
const url = decodeURIComponent(element.getAttribute("x-image-url") || "");
element.removeAttribute("x-image-url");
let proxiedURL;
try {
const parsedURL = new URL(url);
if(parsedURL.hostname === "cdn.discordapp.com") {
proxiedURL = url;
}
} catch (e) { }
if(!proxiedURL) {
proxiedURL = "https://images.weserv.nl/?url=" + encodeURIComponent(url);
}
element.onload = undefined;
element.src = proxiedURL;
const parent = $(element.parentElement);
parent.on('contextmenu', event => {
contextmenu.spawn_context_menu(event.pageX, event.pageY, {
callback: () => {
const win = window.open(url, '_blank');
win.focus();
},
name: tr("Open image in browser"),
type: contextmenu.MenuEntryType.ENTRY,
icon_class: "client-browse-addon-online"
}, contextmenu.Entry.HR(), {
callback: () => copyToClipboard(url),
name: tr("Copy image URL to clipboard"),
type: contextmenu.MenuEntryType.ENTRY,
icon_class: "client-copy"
})
});
parent.css("cursor", "pointer").on('click', () => image_preview.showImagePreview(proxiedURL, url));
}
loader.register_task(loader.Stage.JAVASCRIPT_INITIALIZING, {
name: "XBBCode emoji init",
function: async () => {
let reactId = 0;
rendererReact.registerCustomRenderer(new class extends ElementRenderer<BBCodeTagElement, React.ReactNode> {
tags(): string | string[] {
return ["img", "image"];
}
render(element: BBCodeTagElement): React.ReactNode {
let target;
let content = rendererText.render(element);
if (!element.options) {
target = content?.trim();
} else {
target = element.options?.trim();
}
regexImage.lastIndex = 0;
if (!regexImage.test(target)) {
return <React.Fragment key={"er-" + ++reactId}>{"[img]" + content + "[/img]"}</React.Fragment>;
}
return (
<div key={"irc-" + ++reactId} className={"xbbcode-tag xbbcode-tag-img"}>
<img src={"img/loading_image.svg"} onLoad={event => loadImageForElement(event.currentTarget)} x-image-url={encodeURIComponent(target)} title={target} alt={target} />
</div>
);
}
});
},
priority: 10
});
export function fixupJQueryImageTags(container: JQuery) {
container.find("img").on('load', event => loadImageForElement(event.target as HTMLImageElement));
}