let logging = false; let SKIP_FORCE_THEMING_KEY = "skipForceThemingList"; new (class ExtensionPopup { BROWSER_STORAGE_KEY = "transparentZenSettings"; globalSettings = {}; siteSettings = {}; enableStylingSwitch = document.getElementById("enable-styling"); refetchCSSButton = document.getElementById("refetch-css"); websitesList = document.getElementById("websites-list"); currentSiteFeatures = document.getElementById("current-site-toggles"); currentSiteHostname = ""; autoUpdateSwitch = document.getElementById("auto-update"); lastFetchedTime = document.getElementById("last-fetched-time"); forceStylingSwitch = document.getElementById("force-styling"); whitelistModeSwitch = document.getElementById("whitelist-mode"); whitelistModeLabel = document.getElementById("whitelist-mode-label"); skipForceThemingSwitch = document.getElementById("skip-force-theming"); siteToggleLabel = document.getElementById("site-toggle-label"); skipForceThemingList = []; reloadButton = document.getElementById("reload"); modeIndicator = document.getElementById("mode-indicator"); whatsNewButton = document.getElementById("whats-new"); constructor() { if (logging) console.log("Initializing ExtensionPopup"); // Load settings and initialize the popup this.loadSettings().then(() => { this.loadSkipForceThemingList().then(() => { this.getCurrentTabInfo().then(() => { this.restoreSettings(); this.bindEvents(); }); }); }); // Bind event listeners this.refetchCSSButton.addEventListener("click", this.refetchCSS.bind(this)); this.refetchCSSButton.addEventListener( "auxclick", this.handleMiddleClick.bind(this) ); this.autoUpdateSwitch.addEventListener( "change", this.saveSettings.bind(this) ); this.forceStylingSwitch.addEventListener( "change", this.saveSettings.bind(this) ); this.reloadButton.addEventListener("click", this.reloadPage.bind(this)); // Add toggle features button event listener document .getElementById("toggle-features") ?.addEventListener("click", this.toggleFeatures.bind(this)); this.whitelistModeSwitch.addEventListener( "change", this.handleWhitelistModeChange.bind(this) ); // Add event listener for the "What's New" button this.whatsNewButton.addEventListener("click", this.openWhatsNew.bind(this)); // Add event listener for the data viewer button document.getElementById("view-data")?.addEventListener("click", () => { browser.tabs.create({ url: browser.runtime.getURL("data-viewer/data-viewer.html"), }); }); // Setup auto-update and display last fetched time this.setupAutoUpdate(); this.displayLastFetchedTime(); this.displayAddonVersion(); } async getCurrentTabInfo() { if (logging) console.log("getCurrentTabInfo called"); try { const tabs = await browser.tabs.query({ active: true, currentWindow: true, }); if (tabs.length > 0) { const url = new URL(tabs[0].url); this.currentSiteHostname = url.hostname; console.info("Current site hostname:", this.currentSiteHostname); } } catch (error) { console.error("Error getting current tab info:", error); } } bindEvents() { if (logging) console.log("bindEvents called"); // Bind event listeners for settings changes this.enableStylingSwitch.addEventListener("change", () => { this.saveSettings(); this.updateActiveTabStyling(); }); this.currentSiteFeatures.addEventListener("change", (event) => { if (event.target.type === "checkbox") { this.saveSettings(); this.updateActiveTabStyling(); } }); this.skipForceThemingSwitch.addEventListener("change", () => { this.saveSkipForceThemingList(); }); this.reloadButton.addEventListener("click", this.reloadPage.bind(this)); } restoreSettings() { if (logging) console.log("restoreSettings called"); // Restore global settings this.enableStylingSwitch.checked = this.globalSettings.enableStyling ?? true; this.autoUpdateSwitch.checked = this.globalSettings.autoUpdate ?? false; this.forceStylingSwitch.checked = this.globalSettings.forceStyling ?? false; this.whitelistModeSwitch.checked = this.globalSettings.whitelistMode ?? false; this.updateModeLabels(); // In whitelist mode, checked means "include this site" // In blacklist mode, checked means "skip this site" const isInList = this.skipForceThemingList.includes( this.currentSiteHostname ); this.skipForceThemingSwitch.checked = isInList; this.loadCurrentSiteFeatures(); } async loadSettings() { if (logging) console.log("loadSettings called"); // Load global settings const globalData = await browser.storage.local.get( this.BROWSER_STORAGE_KEY ); this.globalSettings = globalData[this.BROWSER_STORAGE_KEY] || { enableStyling: true, autoUpdate: false, lastFetchedTime: null, forceStyling: false, }; // Load site-specific settings if on a specific site if (this.currentSiteHostname) { const siteKey = `${this.BROWSER_STORAGE_KEY}.${this.currentSiteHostname}`; const siteData = await browser.storage.local.get(siteKey); this.siteSettings = siteData[siteKey] || {}; await this.loadCurrentSiteFeatures(); } } saveSettings() { if (logging) console.log("saveSettings called"); // Save global settings this.globalSettings.enableStyling = this.enableStylingSwitch.checked; this.globalSettings.autoUpdate = this.autoUpdateSwitch.checked; this.globalSettings.forceStyling = this.forceStylingSwitch.checked; this.globalSettings.whitelistMode = this.whitelistModeSwitch.checked; browser.storage.local .set({ [this.BROWSER_STORAGE_KEY]: this.globalSettings, }) .then(() => { if (logging) console.log("Global settings saved"); this.updateActiveTabStyling(); }); // Save site-specific settings if (this.currentSiteHostname) { const siteKey = `${this.BROWSER_STORAGE_KEY}.${this.currentSiteHostname}`; const featureSettings = {}; this.currentSiteFeatures .querySelectorAll("input[type=checkbox]") .forEach((checkbox) => { const [, feature] = checkbox.name.split("|"); featureSettings[feature] = checkbox.checked; }); this.siteSettings = featureSettings; browser.storage.local .set({ [siteKey]: featureSettings, }) .then(() => { if (logging) console.log("Site settings saved"); this.updateActiveTabStyling(); }); } console.info("Settings saved", { global: this.globalSettings, site: this.siteSettings, }); } async loadSkipForceThemingList() { const data = await browser.storage.local.get(SKIP_FORCE_THEMING_KEY); this.skipForceThemingList = data[SKIP_FORCE_THEMING_KEY] || []; } saveSkipForceThemingList() { const isChecked = this.skipForceThemingSwitch.checked; const index = this.skipForceThemingList.indexOf(this.currentSiteHostname); if (isChecked && index === -1) { // Add to the list (whitelist: include, blacklist: skip) this.skipForceThemingList.push(this.currentSiteHostname); } else if (!isChecked && index !== -1) { // Remove from the list (whitelist: exclude, blacklist: include) this.skipForceThemingList.splice(index, 1); } browser.storage.local .set({ [SKIP_FORCE_THEMING_KEY]: this.skipForceThemingList, }) .then(() => { this.updateActiveTabStyling(); }); } async loadCurrentSiteFeatures() { if (logging) console.log("loadCurrentSiteFeatures called"); try { const stylesData = await browser.storage.local.get("styles"); const styles = stylesData.styles?.website || {}; this.currentSiteFeatures.innerHTML = ""; let currentSiteKey = Object.keys(styles).find((site) => this.isCurrentSite(site.replace(".css", "")) ); // Check if we have any styles at all, including example.com const hasExampleSite = "example.com.css" in styles; const hasNoStyles = Object.keys(styles).length === 0; // Only collapse if we found a specific theme for this site // Otherwise keep it expanded to show the request theme button const hasSpecificTheme = currentSiteKey && currentSiteKey !== "example.com.css"; // Apply collapsed class based on whether we have a theme const featuresList = document.getElementById("current-site-toggles"); const actionsContainer = document.getElementById("current-site-actions"); if (hasSpecificTheme) { featuresList.classList.add("collapsed"); if (actionsContainer) actionsContainer.classList.add("collapsed"); // Update the icon to show collapsed state const toggleButton = document.getElementById("toggle-features"); if (toggleButton) { const icon = toggleButton.querySelector("i"); if (icon) icon.className = "fas fa-chevron-down"; } } else { // Keep expanded when no theme was found or using default featuresList.classList.remove("collapsed"); if (actionsContainer) actionsContainer.classList.remove("collapsed"); // Update the icon to show expanded state const toggleButton = document.getElementById("toggle-features"); if (toggleButton) { const icon = toggleButton.querySelector("i"); if (icon) icon.className = "fas fa-chevron-up"; } } // Disable the force styling toggle if we found a theme for this site if (hasSpecificTheme) { // We found a specific theme for this site, no need for force styling // Disable the skip/enable toggle this.skipForceThemingSwitch.disabled = true; this.siteToggleLabel.innerHTML = `${ this.whitelistModeSwitch.checked ? "Enable" : "Skip Forcing" } for this Site ×`; } else { // No specific theme found, enable the toggle this.skipForceThemingSwitch.disabled = false; this.siteToggleLabel.innerHTML = this.whitelistModeSwitch.checked ? "Enable for this Site" : "Skip Forcing for this Site"; } if (!currentSiteKey && this.globalSettings.forceStyling) { currentSiteKey = Object.keys(styles).find( (site) => site === "example.com.css" ); } // Only show the request theme button if we have at least the example.com style // but no specific theme for this site if ( (!currentSiteKey || currentSiteKey === "example.com.css") && hasExampleSite ) { const requestThemeButton = document.createElement("button"); requestThemeButton.className = "action-button primary"; requestThemeButton.innerHTML = `Request Theme for ${this.currentSiteHostname}`; requestThemeButton.addEventListener("click", () => { const issueUrl = `https://github.com/sameerasw/my-internet/issues/new?template=website-theme-request.md&title=[THEME] ${this.currentSiteHostname}&body=Please add a theme for ${this.currentSiteHostname}`; window.open(issueUrl, "_blank"); }); this.currentSiteFeatures.appendChild(requestThemeButton); } else if (hasNoStyles) { // No styles at all, suggest to fetch first const fetchFirstMessage = document.createElement("div"); fetchFirstMessage.className = "toggle-container"; fetchFirstMessage.innerHTML = `