From 3c60e9f735fddca8abf2519dc860eaa2dd762039 Mon Sep 17 00:00:00 2001 From: Ling Zheng Date: Wed, 5 Nov 2025 16:34:19 -0500 Subject: [PATCH] fix css for navbar and tab behavior --- content/content.js | 1 + content/modules/links.js | 29 +++++++- content/modules/ui.js | 151 ++++++++++++++++++++++++++++----------- manifest.json | 4 +- 4 files changed, 138 insertions(+), 47 deletions(-) diff --git a/content/content.js b/content/content.js index a076dda..525d998 100644 --- a/content/content.js +++ b/content/content.js @@ -32,6 +32,7 @@ // Initialize the extension async function init() { + await injectOtherStyles(document.documentElement) await changeTitle() settings("apply") setCssFilters() // Set CSS filters after settings are applied (matches original userscript order) diff --git a/content/modules/links.js b/content/modules/links.js index c1d6e20..9c82cf0 100644 --- a/content/modules/links.js +++ b/content/modules/links.js @@ -38,8 +38,31 @@ function handleLink(source, link) { case 'Update': case 'TicketTaskNew': case 'TicketAddTaskTemplate': { - link.onclick = (event) => { + const datasetKey = `tdx${source}Processed` + if (link.dataset && link.dataset[datasetKey]) { + break + } + + if (link.dataset) { + link.dataset[datasetKey] = 'true' + } + + if (link.target) { + try { + link.target = '' + } catch (_) { + link.removeAttribute('target') + } + } + + link.addEventListener('click', (event) => { + if (event.button && event.button !== 0) { + return + } + event.preventDefault() + event.stopPropagation() + let w = (window.screen.width / 1.75) let h = (window.screen.height / 1.25) let l = ((window.screen.width / 2) - (w/2)) + relL @@ -51,8 +74,8 @@ function handleLink(source, link) { let href = `${baseHref}/${source}${window.location.search}` let params = `width=${w}px,height=${h}px,left=${l}px,top=${t}px` - return window.open(href, '_blank', params) - } + window.open(href, '_blank', params) + }, true) break } } diff --git a/content/modules/ui.js b/content/modules/ui.js index ce0223e..ddc5c2e 100644 --- a/content/modules/ui.js +++ b/content/modules/ui.js @@ -220,54 +220,123 @@ async function changeTitle() { await injectToolbar() } -function injectOtherStyles(element) { - // Load CSS from the stylesheet - // For shadow DOM and iframes, we need to inject the CSS manually - let s = document.createElement("style") - s.id = "customStyles" - - // CSS is injected via manifest, so we need to fetch it from extension resources - // Check if we already have it cached +async function loadCustomStyles() { if (window._tdxCustomStyles) { - s.innerText = window._tdxCustomStyles - injectStyleElement(s, element) - } else { - // Fetch from extension resources - const cssUrl = chrome.runtime.getURL("content/styles.css") - fetch(cssUrl) - .then(response => response.text()) - .then(css => { - // Cache the CSS for future use - window._tdxCustomStyles = css - s.innerText = css - injectStyleElement(s, element) - }) - .catch(e => { - console.warn("Could not load CSS for injection:", e) - // Still try to inject an empty style tag to avoid errors - injectStyleElement(s, element) - }) + return window._tdxCustomStyles + } + + if (!window._tdxCustomStylesPromise) { + try { + const runtimeApi = (typeof chrome !== "undefined" && chrome?.runtime) + ? chrome.runtime + : (typeof browser !== "undefined" && browser?.runtime ? browser.runtime : null) + if (!runtimeApi) { + throw new Error("Runtime API unavailable") + } + + const cssUrl = runtimeApi.getURL("content/styles.css") + window._tdxCustomStylesPromise = fetch(cssUrl) + .then(response => { + if (!response.ok) { + throw new Error(`Failed to load styles (${response.status})`) + } + return response.text() + }) + .then(css => { + window._tdxCustomStyles = css + return css + }) + .catch(error => { + console.warn("Could not load CSS for injection:", error) + window._tdxCustomStyles = "" + return "" + }) + } catch (error) { + console.warn("Could not resolve CSS URL for injection:", error) + window._tdxCustomStyles = "" + window._tdxCustomStylesPromise = Promise.resolve("") + } } + + return window._tdxCustomStylesPromise } -function injectStyleElement(s, element) { - //find iframes - likely text editors - if (element.tagName == "IFRAME") { - let frame = element.contentWindow.document; - - //check for existing style block - if (!frame.querySelector("#customStyles")) { - let head = frame.querySelector("head") - let html = frame.querySelector("html") - html.classList.add(settings("get", "colorMode")) - head.appendChild(s.cloneNode(true)) +async function injectOtherStyles(element) { + if (!element) { + return + } + + const isIFrame = (typeof HTMLIFrameElement !== "undefined") && element instanceof HTMLIFrameElement + const isShadowRoot = (typeof ShadowRoot !== "undefined") && element instanceof ShadowRoot + + // Avoid duplicate injections where possible + if (!isIFrame && !isShadowRoot) { + if (element.querySelector && element.querySelector("#customStyles")) { + return } - } else { - if (!element.querySelector("#customStyles")) { - setCssFilters(s) - element.appendChild(s) + } + + const cssText = await loadCustomStyles() + + injectStyleElement(cssText, element) +} + +function injectStyleElement(cssText, element) { + if (!element) { + return + } + + const isIFrame = (typeof HTMLIFrameElement !== "undefined") && element instanceof HTMLIFrameElement + if (isIFrame) { + try { + const frameDoc = element.contentDocument || element.contentWindow?.document + if (!frameDoc) { + return + } + if (frameDoc.querySelector("#customStyles")) { + return + } + + const styleNode = frameDoc.createElement("style") + styleNode.id = "customStyles" + styleNode.textContent = cssText + + const html = frameDoc.documentElement + const currentMode = settings("get", "colorMode") + if (html && currentMode) { + html.classList.add(currentMode) + } + + ;(frameDoc.head || html || frameDoc).appendChild(styleNode) + return + } catch (error) { + console.warn("Could not inject styles into iframe:", error) + return + } + } + + const isShadowRoot = (typeof ShadowRoot !== "undefined") && element instanceof ShadowRoot + if (isShadowRoot) { + if (element.querySelector("#customStyles")) { + return } + const styleNode = document.createElement("style") + styleNode.id = "customStyles" + styleNode.textContent = cssText + setCssFilters(styleNode) + element.appendChild(styleNode) + return } + + if (element.querySelector && element.querySelector("#customStyles")) { + return + } + + const styleNode = document.createElement("style") + styleNode.id = "customStyles" + styleNode.textContent = cssText + setCssFilters(styleNode) + element.appendChild(styleNode) } function checkPath() { diff --git a/manifest.json b/manifest.json index 01ca255..b0c555d 100644 --- a/manifest.json +++ b/manifest.json @@ -34,6 +34,7 @@ "https://purdue.teamdynamixpreview.com/TDWorkManagement*", "https://purdue.teamdynamixpreview.com/TDNext*" ], + "all_frames": true, "js": [ "lib/moment.min.js", "lib/tinycolor.min.js", @@ -48,9 +49,6 @@ "content/modules/observer.js", "content/content.js" ], - "css": [ - "content/styles.css" - ], "run_at": "document_end" } ]