(function () { // ============================================================================================================ // Hibernate Background Tabs with Status Bar Button or Keyboard Shortcut Mod // URL: https://forum.vivaldi.net/post/396130 and https://forum.vivaldi.net/topic/60172/hibernate-background-tabs-keyboard-shortcut-hotkey // Description: Adds a status bar button and keyboard shortcut that will hibernate all tabs except the active one // Author(s): @nomadic [ keyboard shortcut based on a mod by @LonM "Custom Keyboard Shortcuts Mod" ] // CopyRight: No Copyright Reserved // Tested on: Stable 3.7.2218.58 and Snapshot 3.8.2259.32 // ============================================================================================================ function hibernateBackgroundTabsCombined() { // Config ------------ // 0 is farthest left on the status bar's right grouping of buttons // keep adding 1 to the variable to move it over to the right. const HIBERNATE_BTN_POSITION = 1; // Keyboard shortcut combo to hibernate all background tabs // written in the form "Ctrl+Shift+Alt+KEY" const HIBERNATE_KEY_COMBO = "Alt+H"; const SKIP_SELECTED_TABS = true; const SKIP_ACTIVE_TAB_STACK_TABS = true; // ------------------- // for finding tab stack id in tab data const regex = /(?<="group":")[a-z0-9]+(?=")/i; function hibernateBackgroundTabs() { vivaldi.windowPrivate.getCurrentId((currentWindowId) => { chrome.tabs.query({ active: true, windowId: currentWindowId }, (activeTab) => { activeTab = activeTab[0]; let activeTabStackID = (regex.exec(activeTab.extData) || [false])[0]; chrome.tabs.query({ windowType: "normal" }, (tabs) => { tabs.forEach((tab) => { if (tab.discarded) return; // already discarded, so can skip it if (tab.windowId === currentWindowId && tab.active) return; // skip active tab if (SKIP_SELECTED_TABS && tab.highlighted) return; // skip selected tabs if configured to do so // skip tabs in the same tab stack as the active tab if configured to do so if (SKIP_ACTIVE_TAB_STACK_TABS && activeTabStackID) { let stackID = (regex.exec(tab.extData) || [""])[0]; if (stackID === activeTabStackID) return; } chrome.tabs.discard(tab.id); }); }); }); }); } // shamelessly modified from @luetage's "Random Theme Button Mod" because I am lazy function addHibernateButton() { // Check if button already exists before adding if (!document.getElementById("hibernate")) { let div = document.createElement("div"); div.id = "hibernate"; div.classList.add("button-toolbar"); div.innerHTML = ''; // positon the button based on the config let elBefore = document.querySelector(".StatusInfo").nextSibling; for (let i = 0; i < HIBERNATE_BTN_POSITION; i++) { elBefore = elBefore.nextSibling; } document.querySelector(".toolbar-statusbar").insertBefore(div, elBefore); document.querySelector("#hibernate button svg").style = "width: 14px; height: 14px;"; document.getElementById("hibernate").addEventListener("click", hibernateBackgroundTabs); } } function areStringsEqualIgnoringCase(string1, string2) { return string1.localeCompare(string2, undefined, { sensitivity: "base" }) === 0; } function handleKeyboardShortcut(combo) { if (areStringsEqualIgnoringCase(HIBERNATE_KEY_COMBO, combo)) { hibernateBackgroundTabs(); } } // CHANGE #1: Re-add the button after exiting fullscreen let browser = document.getElementById("browser"); let oldState = browser.classList.contains("fullscreen") || browser.classList.contains("minimal-ui"); let browserObserver = new MutationObserver(function (mutations) { mutations.forEach(function (mutation) { if (mutation.attributeName == "class") { let isFullscreen = mutation.target.classList.contains("fullscreen") || mutation.target.classList.contains("minimal-ui"); if (oldState !== isFullscreen) { oldState = isFullscreen; if (!isFullscreen) { addHibernateButton(); } } // CHANGE #2: Re-add the button after toggling status bar } else if (Array.from(mutation.addedNodes).find((element) => element.classList.contains("toolbar-statusbar"))) { addHibernateButton(); } }); }); browserObserver.observe(browser, { attributes: true, childList: true }); addHibernateButton(); vivaldi.tabsPrivate.onKeyboardShortcut.addListener(handleKeyboardShortcut); } let intervalID = setInterval(() => { const browser = document.getElementById("browser"); if (browser) { clearInterval(intervalID); hibernateBackgroundTabsCombined(); } }, 300); })();