diff --git a/assets/js/main.js b/assets/js/main.js index f2c4c75..f65e304 100644 --- a/assets/js/main.js +++ b/assets/js/main.js @@ -187,6 +187,33 @@ const paperDiv = document.createElement("div"); paperDiv.className = "featured-paper"; paperDiv.style.cursor = "pointer"; + let sectionHasStaticImage = false; + + const hasNonBadgeImage = (element) => { + const images = element.matches("img") + ? [element] + : Array.from(element.querySelectorAll("img")); + + return images.some((image) => { + const src = image.getAttribute("src") || ""; + return !src.includes("img.shields.io"); + }); + }; + + for ( + let cursor = section.nextElementSibling; + cursor; + cursor = cursor.nextElementSibling + ) { + if (cursor.matches("h3")) { + break; + } + + if (hasNonBadgeImage(cursor)) { + sectionHasStaticImage = true; + break; + } + } // Get all content until the next h3 or end let content = [section.cloneNode(true)]; @@ -223,8 +250,14 @@ continue; } - // Keep homepage cards lightweight: skip embedded media. + // Skip embedded media when the paper already has a static image. + // Otherwise keep the iframe so cards do not lose their only media. if (nextEl.matches("iframe")) { + if (!sectionHasStaticImage) { + const iframeClone = nextEl.cloneNode(true); + iframeClone.setAttribute("loading", "lazy"); + content.push(iframeClone); + } nextEl = nextEl.nextElementSibling; continue; } diff --git a/tests/featured-papers-regression.test.js b/tests/featured-papers-regression.test.js new file mode 100644 index 0000000..a2f2655 --- /dev/null +++ b/tests/featured-papers-regression.test.js @@ -0,0 +1,72 @@ +describe("featured papers homepage media handling", () => { + const researchHtml = ` +

[1] Iframe-only featured paper

+
Featured
+ + +

[2] Featured paper with static image

+
Featured
+ +
cover
+ `; + + beforeEach(() => { + jest.resetModules(); + document.body.innerHTML = ` +
+ +
+
+ `; + window.history.pushState({}, "", "/"); + + global.marked = { + parse: jest.fn((value) => value), + }; + global.DOMPurify = { + sanitize: jest.fn((value) => value), + }; + + fetch.mockReset(); + fetch.mockImplementation((url) => { + if (url === "/research/") { + return Promise.resolve({ + ok: true, + text: () => Promise.resolve(researchHtml), + }); + } + + return Promise.resolve({ + ok: true, + text: () => Promise.resolve(""), + }); + }); + }); + + afterEach(() => { + document.body.innerHTML = ""; + delete global.marked; + delete global.DOMPurify; + jest.clearAllMocks(); + }); + + it( + "keeps iframe-only featured cards from losing their only media", + async () => { + require("../assets/js/main.js"); + + window.dispatchEvent(new Event("load")); + await Promise.resolve(); + await Promise.resolve(); + + const cards = document.querySelectorAll(".featured-paper"); + const firstIframe = cards[0].querySelector("iframe"); + + expect(cards).toHaveLength(2); + expect(firstIframe).not.toBeNull(); + expect(firstIframe.getAttribute("loading")).toBe("lazy"); + expect(cards[1].querySelector("iframe")).toBeNull(); + expect(cards[1].querySelector("img")).not.toBeNull(); + } + ); +});