From 1dc9bd9058912cf58a46219688096c13c74e950b Mon Sep 17 00:00:00 2001 From: coomdev Date: Sun, 2 Jan 2022 06:00:28 +0100 Subject: [PATCH] Fix embeddings in inlined replies --- main.user.js | 37 ++++++++++++++++++++++++++----------- src/main.ts | 48 +++++++++++++++++++++++++++++++++++------------- 2 files changed, 61 insertions(+), 24 deletions(-) diff --git a/main.user.js b/main.user.js index 215de55..a9569db 100644 --- a/main.user.js +++ b/main.user.js @@ -1,7 +1,7 @@ // ==UserScript== // @name PNGExtraEmbed // @namespace https://coom.tech/ -// @version 0.36 +// @version 0.37 // @description uhh // @author You // @match https://boards.4channel.org/*/thread/* @@ -12605,9 +12605,6 @@ return await proc[1](reader.getReader()); }; var processPost = async (post) => { - if (post.hasAttribute("data-processed")) - return; - post.setAttribute("data-processed", "true"); const thumb = post.querySelector(".fileThumb"); if (!thumb) return; @@ -12620,7 +12617,13 @@ const cf = ` `; - const a = document.createRange().createContextualFragment(cf).children[0]; + let a; + a = fi.querySelector(".fa.fa-eye"); + let inlining = true; + if (!a) { + inlining = false; + a = document.createRange().createContextualFragment(cf).children[0]; + } const type = await fileTypeFromBuffer(res.data); let cont; let w, h; @@ -12636,7 +12639,11 @@ cont.autoplay = true; } else return; - cont.src = URL.createObjectURL(new Blob([res.data], { type: type.mime })); + let src; + src = post.getAttribute("data-processed"); + if (!src) + src = URL.createObjectURL(new Blob([res.data], { type: type.mime })); + cont.src = src; await new Promise((res2) => { if (cont instanceof HTMLImageElement) cont.onload = res2; @@ -12673,6 +12680,7 @@ thumb.style.gap = "5px"; thumb.style.flexDirection = "column"; a.classList.toggle("disabled"); + a.classList.toggle("pee-button"); let contracted = true; contract(); cont.onclick = (e) => { @@ -12696,14 +12704,21 @@ } a.classList.toggle("disabled"); }; - fi.children[1].insertAdjacentElement("afterend", a); + if (!inlining) + fi.children[1].insertAdjacentElement("afterend", a); + post.setAttribute("data-processed", cont.src); }; var startup = async () => { - document.addEventListener("PostsInserted", async (e) => { - const threadelement = e.target; - const posts = [...threadelement.querySelectorAll("postContainer")].filter((e2) => e2.hasAttribute("data-processed")); - posts.map((e2) => processPost(e2)); + const mo = new MutationObserver((reco) => { + for (const rec of reco) + if (rec.type == "childList") + rec.addedNodes.forEach((e) => { + const el = e.querySelector(".postContainer"); + if (el) + processPost(el); + }); }); + mo.observe(document.querySelector(".thread"), { childList: true, subtree: true }); const getSelectedFile = () => { return new Promise((res) => { document.addEventListener("QRFile", (e) => res(e.detail), { once: true }); diff --git a/src/main.ts b/src/main.ts index 2aa84dc..7fe93f6 100644 --- a/src/main.ts +++ b/src/main.ts @@ -69,9 +69,6 @@ const processImage = async (src: string) => { }; const processPost = async (post: HTMLDivElement) => { - if (post.hasAttribute('data-processed')) - return; - post.setAttribute('data-processed', "true"); const thumb = post.querySelector(".fileThumb") as HTMLAnchorElement; if (!thumb) return; @@ -85,8 +82,13 @@ const processPost = async (post: HTMLDivElement) => { const cf = ` `; - const a = document.createRange().createContextualFragment(cf).children[0] as HTMLAnchorElement; - const type = await fileTypeFromBuffer(res.data); + let a: HTMLAnchorElement | null; + a = fi.querySelector('.fa.fa-eye'); + let inlining = true; + if (!a) { + inlining = false; + a = document.createRange().createContextualFragment(cf).children[0] as HTMLAnchorElement; + } const type = await fileTypeFromBuffer(res.data); let cont: HTMLImageElement | HTMLVideoElement | HTMLAudioElement; let w: number, h: number; if (type?.mime.startsWith("image")) { @@ -102,7 +104,11 @@ const processPost = async (post: HTMLDivElement) => { } else return; // TODO: handle new file types??? Or direct "download"? - cont.src = URL.createObjectURL(new Blob([res.data], { type: type.mime })); + let src: string | null; + src = post.getAttribute('data-processed'); + if (!src) + src = URL.createObjectURL(new Blob([res.data], { type: type.mime })); + cont.src = src; await new Promise(res => { if (cont instanceof HTMLImageElement) @@ -148,6 +154,7 @@ const processPost = async (post: HTMLDivElement) => { thumb.style.gap = "5px"; thumb.style.flexDirection = "column"; a.classList.toggle("disabled"); + a.classList.toggle("pee-button"); let contracted = true; contract(); cont.onclick = (e) => { @@ -170,20 +177,35 @@ const processPost = async (post: HTMLDivElement) => { } imgcont.removeChild(cont); } - a.classList.toggle("disabled"); + a!.classList.toggle("disabled"); }; - fi.children[1].insertAdjacentElement('afterend', a); + if (!inlining) + fi.children[1].insertAdjacentElement('afterend', a); + post.setAttribute('data-processed', cont.src); }; const startup = async () => { //await Promise.all([...document.querySelectorAll('.postContainer')].filter(e => e.textContent?.includes("191 KB")).map(e => processPost(e as any))); - document.addEventListener('PostsInserted', (async (e: CustomEvent) => { - const threadelement = e.target as HTMLDivElement; - const posts = [...threadelement.querySelectorAll("postContainer")].filter(e => e.hasAttribute('data-processed')); - posts.map(e => processPost(e as any)); - })); + // Basically this is a misnommer: fires even when inlining existings posts, also posts are inlined through some kind of dom projection + // document.addEventListener('PostsInserted', (async (e: CustomEvent) => { + // const threadelement = e.target as HTMLDivElement; + // const posts = [...threadelement.querySelectorAll(".postContainer")].filter(e => !e.hasAttribute('data-processed')); + // posts.map(e => processPost(e as any)); + // }));threadelement + + const mo = new MutationObserver(reco => { + for (const rec of reco) + if (rec.type == "childList") + rec.addedNodes.forEach(e => { + const el = (e as any).querySelector(".postContainer"); + if (el) + processPost(el as any); + }); + }); + + mo.observe(document.querySelector('.thread')!, { childList: true, subtree: true }); const getSelectedFile = () => { return new Promise(res => {