Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /* ================================================================================================================================================================================================================================= */
- /*
- * Autosave Sessions (a mod for Vivaldi)
- * Written by LonM
- * v4 : Localise to current timezone, l10n
- * v3 : Has own settings section & support private windows again
- * v2 : Better handling of multiple windows
- */
- (function autoSaveSessionsMod(){
- "use strict";
- const LANGUAGE = 'en_gb';
- const l10n = {
- en_gb: {
- delay: 'Period (minutes)',
- restart: 'This setting requires a restart to take full effect.',
- maxoldsessions: 'Old Sessions Count',
- prefix: 'Prefix',
- prefixdesc: 'A unique prefix made up of the following characters: A-Z 0-9 _',
- saveprivate: 'Save Private Windows'
- },
- }[LANGUAGE];
- let CURRENT_SETTINGS = {};
- /**
- * Copied from bundle.js © Vivaldi - Check if a filename is valid
- * @param {string} s
- */
- function isValidName(e){
- return /^[^\\/:\*\?"<>\|]+$/.test(e) && !/^\./.test(e) && !/^(nul|prn|con|lpt[0-9]|com[0-9])(\.|$)/i.test(e);
- }
- /**
- * Turns a date into a string that can be used in a file name
- * Locale string seems to be the best at getting the correct time for any given timezone
- * @param {Date} date object
- */
- function dateToFileSafeString(date){
- const badChars = /[\\/:\*\?"<>\|]/gi;
- return date.toLocaleString().replace(badChars, '.');
- }
- /**
- * Enable Autosaving sessions
- */
- function autoSaveSession(isPrivate){
- vivaldi.sessionsPrivate.getAll(allSessions => {
- const priv = isPrivate ? "PRIV" : "";
- const prefix = CURRENT_SETTINGS["LONM_SESSION_AUTOSAVE_PREFIX"] + priv;
- const maxOld = CURRENT_SETTINGS["LONM_SESSION_AUTOSAVE_MAX_OLD_SESSIONS"];
- const now = new Date();
- const autosavesOnly = allSessions.filter(x => x.name.indexOf(prefix)===0);
- const oldestFirst = autosavesOnly.sort((a,b) => {return a.createDateJS - b.createDateJS;});
- /* create the new session */
- const name = prefix + dateToFileSafeString(now);
- /* final sanity check */
- if (!isValidName(name)){
- throw new Error("[Autosave Sessions] Cannot name a session as " + name);
- }
- const options = {
- saveOnlyWindowId: 0
- };
- vivaldi.sessionsPrivate.saveOpenTabs(name, options, () => {}); /* there is no way to tell if it failed */
- /* delete older sessions */
- let numberOfSessions = oldestFirst.length + 1; /* length + 1 as we have just added a new one */
- let oldestIndex = 0;
- while(numberOfSessions > maxOld){
- vivaldi.sessionsPrivate.delete(oldestFirst[oldestIndex].name,() => {});
- oldestIndex++;
- numberOfSessions--;
- }
- });
- }
- /**
- * Check if this is the most recent window, and if the most recent window is still open
- * if not, then stop saving the sessions
- */
- function triggerAutosave(){
- chrome.storage.local.get("LONM_SESSION_AUTOSAVE_LAST_WINDOW", data => {
- const lastOpenedWindow = data["LONM_SESSION_AUTOSAVE_LAST_WINDOW"];
- if(window.vivaldiWindowId===lastOpenedWindow){
- /* We know this window is correct, skip the checks */
- autoSaveSession();
- return;
- }
- chrome.windows.getAll(openWindows => {
- const foundLastOpen = openWindows.find(window => window.id===lastOpenedWindow);
- if(foundLastOpen){
- /*Most recent window still active, use that one instead*/
- } else {
- /*Most recent window was closed, revert to this one*/
- chrome.storage.local.set({
- "LONM_SESSION_AUTOSAVE_LAST_WINDOW": window.vivaldiWindowId
- }, () => {
- autoSaveSession();
- });
- }
- });
- });
- }
- function triggerAutosavePrivate(){
- chrome.storage.local.get("LONM_SESSION_AUTOSAVE_LAST_PRIV_WINDOW", data => {
- const lastOpenedWindow = data["LONM_SESSION_AUTOSAVE_LAST_PRIV_WINDOW"];
- if(window.vivaldiWindowId===lastOpenedWindow){
- /* We know this window is correct, skip the checks */
- autoSaveSession(true);
- return;
- }
- chrome.windows.getAll(openWindows => {
- const foundLastOpen = openWindows.find(window => window.id===lastOpenedWindow);
- if(foundLastOpen){
- /*Most recent window still active, use that one instead*/
- } else {
- /*Most recent window was closed, revert to this one*/
- chrome.storage.local.set({
- "LONM_SESSION_AUTOSAVE_LAST_PRIV_WINDOW": window.vivaldiWindowId
- }, () => {
- autoSaveSession(true);
- });
- }
- });
- });
- }
- /**
- * Mod the settings page to show settings there
- * Wait a little bit after a settings page has been opened and add settings in
- */
- const SETTINGSPAGE = "chrome-extension://mpognobbkildjkofajifpdfhcoklimli/components/settings/settings.html?path=general";
- function modSettingsPageListener(newTab){
- if(newTab.url === SETTINGSPAGE || newTab.pendingUrl === SETTINGSPAGE){
- setTimeout(modSettingsPage, 1000);
- }
- }
- function modSettingsPage(){
- const settingsHTML = document.createElement("section");
- settingsHTML.className = "setting-section";
- settingsHTML.id = "lonmAutosaveSessionsSettings";
- const settingsDiv = document.createElement("div");
- settingsDiv.insertAdjacentHTML("beforeend", "<h2>Autosave Sessions Mod</h2>");
- MOD_SETTINGS.forEach(setting => {
- settingsDiv.appendChild(makeSettingElement(setting));
- });
- settingsHTML.appendChild(settingsDiv);
- document.querySelector(".vivaldi-settings .settings-content section").insertAdjacentElement("afterbegin", settingsHTML);
- }
- /**
- * For a mod setting you need:
- *
- * A) Load it when the mod starts
- * B) Make an option for it when settings is opened
- * C) Change the saved and current state with new value when setting is changed
- *
- * Mod setting has:
- * Key: string
- * Default Value: string|int
- * Description: string
- */
- const MOD_SETTINGS = [
- {
- id: "LONM_SESSION_AUTOSAVE_DELAY_MINUTES",
- type: Number,
- min: 1,
- max: undefined,
- default: 5,
- title: l10n.delay,
- description: l10n.restart
- },
- {
- id: "LONM_SESSION_AUTOSAVE_MAX_OLD_SESSIONS",
- type: Number,
- min: 1,
- max: undefined,
- default: 5,
- title: l10n.maxoldsessions
- },
- {
- id: "LONM_SESSION_AUTOSAVE_PREFIX",
- type: String,
- pattern: "[\\w_]{0,20}",
- default: "VSESAUTOSAVE_",
- title: l10n.prefix,
- description: l10n.prefixdesc
- },
- {
- id: "LONM_SESSION_SAVE_PRIVATE_WINDOWS",
- type: Boolean,
- default: false,
- title: l10n.saveprivate,
- description: l10n.restart
- }
- ];
- /**
- * Handle a change to a setting input
- * Should be bound in a listener to the setting object
- * @param {InputEvent} input
- */
- function settingUpdated(input){
- if(input.target.type === "checkbox"){
- CURRENT_SETTINGS[this.id] = input.target.checked;
- } else {
- input.target.checkValidity();
- if(input.target.reportValidity() && input.target.value !== ""){
- CURRENT_SETTINGS[this.id] = input.target.value;
- }
- }
- chrome.storage.local.set(CURRENT_SETTINGS);
- }
- /**
- * Create an element for the current setting
- * @param modSetting
- */
- function makeSettingElement(modSetting) {
- const currentSettingValue = CURRENT_SETTINGS[modSetting.id];
- const div = document.createElement("div");
- div.className = "setting-single";
- const title = document.createElement("h3");
- title.innerText = modSetting.title;
- div.appendChild(title);
- if(modSetting.description){
- const info = document.createElement("p");
- info.className = "info";
- info.innerText = modSetting.description;
- div.appendChild(info);
- }
- const input = document.createElement("input");
- input.id = modSetting.id;
- input.value = currentSettingValue;
- input.autocomplete = "off";
- input.autocapitalize = "off";
- input.autocorrect = "off";
- input.spellcheck = "off";
- switch (modSetting.type) {
- case Number:
- input.type = "number";
- break;
- case String:
- input.type = "text";
- break;
- case Boolean:
- input.type = "checkbox";
- if(currentSettingValue){input.checked = "checked";}
- break;
- default:
- throw Error("Unknown setting type!");
- }
- if(modSetting.max){input.max = modSetting.max;}
- if(modSetting.min){input.min = modSetting.min;}
- if(modSetting.pattern){input.pattern = modSetting.pattern;}
- input.addEventListener("input", settingUpdated.bind(modSetting));
- div.appendChild(input);
- return div;
- }
- /**
- * Init the mod, but only if we are not incognito, to maintain privacy.
- * Save the window id in storage, and only use the most recent window to save sessions
- */
- function init(){
- if(window.vivaldiWindowId){
- chrome.windows.getCurrent(window => {
- if(!window.incognito){
- chrome.storage.local.set({
- "LONM_SESSION_AUTOSAVE_LAST_WINDOW": window.vivaldiWindowId
- }, () => {
- setInterval(triggerAutosave, CURRENT_SETTINGS["LONM_SESSION_AUTOSAVE_DELAY_MINUTES"]*60*1000);
- });
- }
- if(CURRENT_SETTINGS["LONM_SESSION_SAVE_PRIVATE_WINDOWS"] && window.incognito){
- chrome.storage.local.set({
- "LONM_SESSION_AUTOSAVE_LAST_PRIV_WINDOW": window.vivaldiWindowId
- }, () => {
- setInterval(triggerAutosavePrivate, CURRENT_SETTINGS["LONM_SESSION_AUTOSAVE_DELAY_MINUTES"]*60*1000);
- });
- }
- chrome.tabs.onCreated.addListener(modSettingsPageListener);
- });
- } else {
- setTimeout(init, 500);
- }
- }
- /**
- * Load the settings and call the initialiser function
- */
- function loadSettingsAndInit(){
- const keys = MOD_SETTINGS.reduce((prev, current) => {
- prev[current.id] = current.default;
- return prev;
- }, {});
- chrome.storage.local.get(keys, value => {
- CURRENT_SETTINGS = value;
- setTimeout(init, 500);
- });
- }
- loadSettingsAndInit();
- })();
- /* ================================================================================================================================================================================================================================= */
- // Backup Search Engines
- // version 2021.9.0
- // https://forum.vivaldi.net/topic/35443/backup-search-engines
- // Adds functionality to backup and restore search engines in
- // vivaldi://settings/search.
- (function () {
- function _msgSearch(pnt) {
- clearTimeout(_msgTimeout);
- if (pnt === "backup") {
- _infoSearch.innerText = "Search engines backup copied to clipboard.";
- } else if (pnt === "restore") {
- _infoSearch.innerText = "Search engines restored.";
- } else {
- _infoSearch.innerText = "Search engines code error.";
- }
- _msgTimeout = setTimeout(function () {
- _infoSearch.innerText = "";
- }, 5000);
- }
- function _restoreSearch() {
- event.preventDefault();
- event.stopPropagation();
- if (_eventSearch === "paste") {
- var clipboardData = event.clipboardData || window.clipboardData;
- var engineCode = clipboardData.getData("text");
- } else {
- var engineCode = event.dataTransfer.getData("text");
- }
- try {
- var engines = JSON.parse(engineCode);
- } catch (err) {
- _msgSearch("error");
- return;
- }
- if (
- "engines" in engines &&
- "default" in engines &&
- "defaultPrivate" in engines
- ) {
- chrome.storage.local.set(
- { SEARCH_ENGINE_COLLECTION: engines },
- function () {
- _msgSearch("restore");
- }
- );
- } else {
- _msgSearch("error");
- }
- }
- function _backupSearch() {
- chrome.storage.local.get({ SEARCH_ENGINE_COLLECTION: "" }, function (back) {
- const collection = back.SEARCH_ENGINE_COLLECTION;
- collection.engines = collection.engines.filter(
- (engine) => engine.removed === false
- );
- for (let i = 0; i < collection.engines.length; i++) {
- collection.engines[i].historyId = i + 1;
- }
- collection.nextHistoryId = collection.engines.length + 1;
- console.log(collection);
- const engineCode = JSON.stringify(collection);
- navigator.clipboard.writeText(engineCode);
- _msgSearch("backup");
- });
- }
- function searchEngines() {
- const styleCheck = document.getElementById("searchEngines");
- if (!styleCheck) {
- const style = document.createElement("style");
- style.type = "text/css";
- style.id = "searchEngines";
- style.innerHTML =
- "#backupSearch, #restoreSearch {margin-left: 6px;}#restoreSearch{width: 130px;margin-top: 6px;}#restoreSearch::-webkit-input-placeholder {opacity: 1;color: var(--colorHighlightBg);text-align: center;}#msgConfirm{margin-left: 12px}";
- document.getElementsByTagName("head")[0].appendChild(style);
- }
- const modCheck = document.getElementById("backupSearch");
- if (!modCheck) {
- const place = document.querySelector(
- ".setting-section > div > .setting-group.unlimited > .setting-single"
- );
- const backupBtn = document.createElement("input");
- backupBtn.setAttribute("type", "button");
- backupBtn.setAttribute("value", "Backup");
- backupBtn.id = "backupSearch";
- place.insertBefore(backupBtn, place.lastChild);
- const restoreInput = document.createElement("input");
- restoreInput.setAttribute("type", "text");
- restoreInput.setAttribute("placeholder", "Restore Backup");
- restoreInput.id = "restoreSearch";
- place.insertBefore(restoreInput, place.lastChild);
- _infoSearch = document.createElement("span");
- _infoSearch.id = "msgConfirm";
- place.insertBefore(_infoSearch, place.lastChild);
- document
- .getElementById("backupSearch")
- .addEventListener("click", _backupSearch);
- const restoreSearch = document.getElementById("restoreSearch");
- restoreSearch.addEventListener("paste", function () {
- _eventSearch = "paste";
- _restoreSearch(event);
- });
- restoreSearch.addEventListener("drop", function () {
- _eventSearch = "drop";
- _restoreSearch(event);
- });
- _msgTimeout = {};
- }
- }
- const settingsUrl =
- "chrome-extension://mpognobbkildjkofajifpdfhcoklimli/components/settings/settings.html?path=";
- chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
- if (changeInfo.url === `${settingsUrl}search`) {
- setTimeout(searchEngines, 100);
- }
- });
- })();
- /* ================================================================================================================================================================================================================================= */
- //Bookmarks button before AddressBar
- //Кнопка с выпадающими закладками перед строкой адреса
- {
- const position = { separate: "separate", addressfield: "addressfield" }
- vivaldi.jdhooks.hookModuleExport("vivaldiSettings", "default", exports => {
- let oldGetDefault = exports.getDefault
- exports.getDefault = name => {
- switch (name) {
- case "BOOKMARK_BUTTON_POSITION": return position.separate
- default: return oldGetDefault(name)
- }
- }
- return exports
- })
- function bookmarksOnClick(event) {
- const CommandManager = vivaldi.jdhooks.require("_CommandManager")
- const ShowMenu = vivaldi.jdhooks.require("_ShowMenu")
- const menu = CommandManager.getNamedMenu("vivaldi", true)
- .concat(CommandManager.getNamedMenu("menubar", true))
- const bookmarks = menu.find(i => i.commandName == "MENU_BOOKMARKS")
- if (bookmarks) {
- const rect = event.target.getBoundingClientRect()
- const props = {
- id: 0,
- rect: {
- x: parseInt(rect.left),
- y: parseInt(rect.top),
- width: parseInt(rect.width),
- height: parseInt(rect.height)
- },
- menu: {
- items: bookmarks.items,
- expandId: "",
- preferred: []
- }
- }
- ShowMenu.show(props.id, [props], "bottom")
- }
- }
- vivaldi.jdhooks.hookClass("createbookmark_createbookmark", origClass => {
- const ReactDom = vivaldi.jdhooks.require("ReactDOM")
- class newCreateBookmarkButton extends origClass {
- constructor(...e) {
- super(...e)
- this.bookmarksButtonJs = {
- pointerUp: (e => {
- if (e.button == 2 && this.state.jdVivaldiSettings.BOOKMARK_BUTTON_POSITION == position.addressfield) {
- bookmarksOnClick(e)
- }
- }).bind(this)
- }
- }
- componentDidMount() {
- if (super.componentDidMount) super.componentDidMount()
- const button = ReactDom.findDOMNode(this)
- if (button) button.addEventListener("pointerup", this.bookmarksButtonJs.pointerUp, true)
- }
- componentWillUnmount() {
- const button = ReactDom.findDOMNode(this)
- if (button) button.removeEventListener("pointerup", this.bookmarksButtonJs.pointerUp, true)
- if (super.componentWillUnmount) super.componentWillUnmount()
- }
- }
- return vivaldi.jdhooks.insertWatcher(newCreateBookmarkButton, { settings: ["BOOKMARK_BUTTON_POSITION"] })
- })
- vivaldi.jdhooks.hookClass("toolbars_Toolbar", origClass => {
- const React = vivaldi.jdhooks.require("React")
- const SettingsPaths = vivaldi.jdhooks.require("_PrefKeys")
- const ToolbarButton = vivaldi.jdhooks.require("toolbars_ToolbarButton")
- class newClass extends origClass {
- render() {
- let ret = super.render()
- if (this.props.name == SettingsPaths.kToolbarsNavigation) {
- ret.props.children.push(
- this.state.jdVivaldiSettings.BOOKMARK_BUTTON_POSITION == position.separate &&
- this.state.jdPrefs[SettingsPaths.kMenuDisplay] != "top"
- ? React.createElement(ToolbarButton, {
- tooltip: "Bookmarks",
- onClick: bookmarksOnClick,
- image: vivaldi.jdhooks.require("_svg_bookmarks_large")
- })
- : null
- )
- }
- return ret
- }
- }
- return vivaldi.jdhooks.insertWatcher(newClass, {
- settings: ["BOOKMARK_BUTTON_POSITION"],
- prefs: [SettingsPaths.kMenuDisplay]
- })
- })
- vivaldi.jdhooks.hookClass("settings_bookmarks_BookmarkSettings", origClass => {
- const React = vivaldi.jdhooks.require("React")
- const RadioGroup = vivaldi.jdhooks.require("common_RadioGroup")
- const VivaldiSettings = vivaldi.jdhooks.require("vivaldiSettings")
- const Settings_SettingsSearchCategoryChild = vivaldi.jdhooks.require("settings_SettingsSearchCategoryChild")
- function toArray(i) {
- if (Array.isArray(i)) return i;
- return [i];
- }
- const Setting = vivaldi.jdhooks.insertWatcher(class extends React.PureComponent {
- render() {
- return React.createElement(Settings_SettingsSearchCategoryChild, { filter: this.props.filter },
- React.createElement("div", { className: "setting-group" },
- React.createElement("h3", {}, "Bookmark Button"),
- React.createElement(RadioGroup,
- {
- name: "bookmark_button_position",
- value: this.state.jdVivaldiSettings.BOOKMARK_BUTTON_POSITION,
- onChange: (evt) => VivaldiSettings.set({ BOOKMARK_BUTTON_POSITION: evt.target.value })
- },
- React.createElement("div", { className: "setting-single" },
- React.createElement("label", {},
- React.createElement("input",
- {
- type: "radio",
- value: position.separate,
- }),
- React.createElement("span", {}, "Separate button")
- )
- ),
- React.createElement("div", { className: "setting-single" },
- React.createElement("label", {},
- React.createElement("input",
- {
- type: "radio",
- value: position.addressfield,
- }),
- React.createElement("span", {},
- 'Right click on "Add bookmark" button in the address field'
- )
- )
- )
- )
- )
- )
- }
- }, { settings: ["BOOKMARK_BUTTON_POSITION"] })
- return class extends origClass {
- render() {
- let r = super.render()
- r.props.children = toArray(r.props.children)
- r.props.children.push(React.createElement(Setting, this.props))
- return r
- }
- }
- })
- }
- /* ================================================================================================================================================================================================================================= */
- /* History Clock */
- function historyClock() {
- const clock = document.querySelector("#switch button.history");
- var time = new Date();
- var hours = time.getHours();
- var minutes = time.getMinutes();
- var seconds = time.getSeconds();
- if (clock) {
- clock.style =
- "--timeHourRotation: rotate(" +
- Math.floor(hours * 30 + minutes / 2) +
- "deg); --timeMinuteRotation: rotate(" +
- minutes * 6 +
- "deg); --timeSecondRotation: rotate(" +
- seconds * 6 +
- "deg)";
- clock.title =
- String(hours).padStart(2, 0) + ":" + String(minutes).padStart(2, 0) + ":" + String(seconds).padStart(2, 0);
- if (!document.getElementById("opClock")) {
- const cnp = document.querySelector("#switch button.history span svg");
- cnp.id = "opClock";
- cnp.innerHTML = `
- <rect x="12" y="8.5" width="1" height="5" rx="1" ry="1" style="transform-origin: center; transform: var(--timeHourRotation, rotate(130deg))"></rect>
- <rect x="12" y="8" width="1" height="6" rx="1" ry="1" style="transform-origin: center; transform: var(--timeMinuteRotation, rotate(0deg))"></rect>
- <rect x="12" y="8" width="0.75" height="6.5" rx="1" ry="1" style="transform-origin: center; transform: var(--timeSecondRotation, rotate(280deg))"></rect>
- <path fill-rule="evenodd" d="M5 13C5 11.1401 5 10.2101 5.20445 9.44709C5.75925 7.37653 7.37653 5.75925 9.44709 5.20445C10.2101 5 11.1401 5 13 5C14.8599 5 15.7899 5 16.5529 5.20445C18.6235 5.75925 20.2408 7.37653 20.7956 9.44709C21 10.2101 21 11.1401 21 13C21 14.8599 21 15.7899 20.7956 16.5529C20.2408 18.6235 18.6235 20.2408 16.5529 20.7956C15.7899 21 14.8599 21 13 21C11.1401 21 10.2101 21 9.44709 20.7956C7.37653 20.2408 5.75925 18.6235 5.20445 16.5529C5 15.7899 5 14.8599 5 13ZM13 19C11.0113 19 10.4093 18.9828 9.96472 18.8637C8.58436 18.4938 7.50617 17.4156 7.1363 16.0353C7.01718 15.5907 7 14.9887 7 13C7 11.0113 7.01718 10.4093 7.1363 9.96472C7.50617 8.58436 8.58436 7.50617 9.96472 7.1363C10.4093 7.01718 11.0113 7 13 7C14.9887 7 15.5907 7.01718 16.0353 7.1363C17.4156 7.50617 18.4938 8.58436 18.8637 9.96472C18.9828 10.4093 19 11.0113 19 13C19 14.9887 18.9828 15.5907 18.8637 16.0353C18.4938 17.4156 17.4156 18.4938 16.0353 18.8637C15.5907 18.9828 14.9887 19 13 19Z"></path>
- `;
- }
- }
- }
- setInterval(historyClock, 1000);
- /* ================================================================================================================================================================================================================================= */
- (function () {
- // ============================================================================================================
- // Hibernate Background Tabs Status Bar Button Mod and Random Theme Button by luetage
- // - made by nomadic on the Vivaldi Forums
- // ============================================================================================================
- function combinedStatusBarButtons() {
- // 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 left.
- const HIBERNATE_BTN_POSITION = -2;
- const RANDOMTHEME_BTN_POSITION = -3;
- // -------------------
- // if they are equal, defaults to random theme button first
- const RANDOMTHEME_BTN_IS_ADDED_FIRST = RANDOMTHEME_BTN_POSITION > HIBERNATE_BTN_POSITION ? false : true;
- // ################################ START Hibernate Button Functions ################################
- function hibernateBackgroundTabs() {
- vivaldi.windowPrivate.getCurrentId((currentWindowId) => {
- chrome.tabs.query({ windowType: "normal" }, (tabs) => {
- tabs.forEach((tab) => {
- if (tab.windowId === currentWindowId && tab.active) return;
- chrome.tabs.discard(tab.id);
- });
- });
- });
- }
- // shamelessly modified from @luetage's "Random Theme Button Mod" because I am lazy
- function addHibernateButton(callback) {
- let div = document.createElement("div");
- div.classList.add("button-toolbar", "hibernate");
- div.innerHTML =
- '<button draggable="true" tabindex="-1" title="Hibernate Background Tabs" type="button"><span><svg width="12" height="13" viewBox="0 0 26 26" xmlns="http://www.w3.org/2000/svg"><path d="M24.46.65c.1 0 .2.1.18.22l-.04.08-.06.2-.3.74-1.51 3.77-.34.85-.1.28-.04.06v.05h.16l.32-.05 2.17-.36c.15 0 .27-.04.35-.06h.26l.06.14c.11.55.2 1.12.27 1.7l-.02.02a1.36 1.36 0 01-.4.13l-3.48.6-3.6.59-.23.04h-.08a.24.24 0 01-.14-.06.16.16 0 01-.02-.08v-.16l.28-.71L19.4 5.5l.6-1.46.19-.45.04-.12v-.06h-.02V3.4h-.12c-.79.12-1.58.24-2.39.4l-.33.06h-.26l-.08-.12c-.1-.46-.2-.9-.25-1.36l-.04-.2v-.08l.02-.06a.4.4 0 01.06-.06l.15-.06.54-.11 3.11-.54 2.69-.43.83-.14.23-.04zm-7.61 4.06c.12-.01.21.06.25.2V5l-.04.22c0 .2-.06.5-.11.9l-.64 4.5-.15.99-.04.32v.11h.02v.04h.06l.07-.06.36-.13 1.1-.48 1.15-.5.37-.15.1-.04h.16c.04 0 .06.04.12.1.27.6.51 1.16.75 1.76 0 0 0 .04-.04.06a1.52 1.52 0 01-.4.23l-6.43 2.8-.88.38c-.1.04-.2.06-.24.1l-.08.02h-.02c-.06.02-.14 0-.16-.04a.16.16 0 01-.06-.08l.61-4.74c.1-.67.2-1.28.24-1.73l.08-.52.02-.13v-.1l-.02.04v-.04h-.02l-.02.02c-.02 0-.06 0-.1.04l-.31.14-2.17.9-.34.16-.12.04h-.16c-.03 0-.07-.04-.11-.1l-.4-.79-.33-.8-.02-.07v-.1l.06-.07.13-.1.56-.28a211.5 211.5 0 016.86-2.96l.26-.08.06-.04h.02zm-.71 7.32l-.02.02h.02zm-5.35 0c.14 0 .26.14.26.32l-.02.12-.08.25-2.57 8.15v.1l-.02.02v.04h.08l.14-.02.45-.12 3.63-.87h.2c.04.04.08.08.12.18l.6 2.25-.03.08-.06.06a1.93 1.93 0 01-.55.23c-.65.2-2.01.54-4.8 1.23l-4.92 1.2-.34.08H2.8l-.04.02a.34.34 0 01-.2-.08.2.2 0 01-.05-.12v-.24l.31-1.02 2.1-6.6.19-.66.06-.18v-.1H5.1l-.12.04-.43.1-1.42.36-1.44.35-.48.1-.13.02H.85l-.12-.16c-.2-.63-.35-1.24-.51-1.87 0-.12-.04-.2-.06-.28v-.2a.55.55 0 01.1-.1l.2-.1.74-.19a295.7 295.7 0 018-2.01l1.14-.28.31-.06.1-.02h.02z"/></svg><span></button>';
- // 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.querySelector(".hibernate").addEventListener("click", hibernateBackgroundTabs);
- callback();
- }
- // ################################ END Hibernate Button Functions ################################
- // ################################ START Random Theme Button Functions ################################
- function randomizeTheme() {
- chrome.storage.local.get(
- {
- THEMES_USER: "",
- THEME_CURRENT: "",
- },
- function (rd) {
- var userThemes = rd.THEMES_USER;
- var currentTheme = rd.THEME_CURRENT;
- if (userThemes.length > 1) {
- while (random === undefined || random.name === currentTheme) {
- var random = userThemes[Math.floor(Math.random() * userThemes.length)];
- }
- chrome.storage.local.set({
- THEME_CURRENT: random.name,
- BROWSER_COLOR_ACCENT_BG: random.colors.accentBg,
- BROWSER_COLOR_BG: random.colors.baseBg,
- BROWSER_COLOR_FG: random.colors.baseFg,
- BROWSER_COLOR_HIGHLIGHT_BG: random.colors.highlightBg,
- TABCOLOR_BEHIND_TABS: random.settings.accentOnWindow,
- USE_TABCOLOR: random.settings.accentFromPage,
- BORDER_RADIUS: random.settings.borderRadius,
- USE_TAB_TRANSPARENT_TABS: random.settings.tabsTransparent,
- });
- } else {
- console.log("Please create additional themes in vivaldi://settings/themes.");
- }
- }
- );
- }
- function addRandomThemeButton(callback) {
- let div = document.createElement("div");
- div.classList.add("button-toolbar", "random");
- div.innerHTML =
- '<button draggable="true" tabindex="-1" title="Random theme" type="button"><span><svg width="12" height="13" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M666 481q-60 92-137 273-22-45-37-72.5t-40.5-63.5-51-56.5-63-35-81.5-14.5h-224q-14 0-23-9t-9-23v-192q0-14 9-23t23-9h224q250 0 410 225zm1126 799q0 14-9 23l-320 320q-9 9-23 9-13 0-22.5-9.5t-9.5-22.5v-192q-32 0-85 .5t-81 1-73-1-71-5-64-10.5-63-18.5-58-28.5-59-40-55-53.5-56-69.5q59-93 136-273 22 45 37 72.5t40.5 63.5 51 56.5 63 35 81.5 14.5h256v-192q0-14 9-23t23-9q12 0 24 10l319 319q9 9 9 23zm0-896q0 14-9 23l-320 320q-9 9-23 9-13 0-22.5-9.5t-9.5-22.5v-192h-256q-48 0-87 15t-69 45-51 61.5-45 77.5q-32 62-78 171-29 66-49.5 111t-54 105-64 100-74 83-90 68.5-106.5 42-128 16.5h-224q-14 0-23-9t-9-23v-192q0-14 9-23t23-9h224q48 0 87-15t69-45 51-61.5 45-77.5q32-62 78-171 29-66 49.5-111t54-105 64-100 74-83 90-68.5 106.5-42 128-16.5h256v-192q0-14 9-23t23-9q12 0 24 10l319 319q9 9 9 23z"/></svg><span></button>';
- // positon the button based on the config
- let elBefore = document.querySelector(".StatusInfo").nextSibling;
- for (let i = 0; i < RANDOMTHEME_BTN_POSITION; i++) {
- elBefore = elBefore.nextSibling;
- }
- document.querySelector(".toolbar-statusbar").insertBefore(div, elBefore);
- document.querySelector(".random button svg").style = "width: 14px; height: 14px;";
- document.querySelector(".random").addEventListener("click", randomizeTheme);
- callback();
- }
- // ################################ END Random Theme Button Functions ################################
- // Re-add the buttons 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) {
- if (RANDOMTHEME_BTN_IS_ADDED_FIRST) addRandomThemeButton(addHibernateButton);
- else addHibernateButton(addRandomThemeButton);
- }
- }
- }
- });
- });
- browserObserver.observe(browser, { attributes: true });
- // initially add the buttons depending on orders given
- if (RANDOMTHEME_BTN_IS_ADDED_FIRST) addRandomThemeButton(addHibernateButton);
- else addHibernateButton(addRandomThemeButton);
- }
- let intervalID = setInterval(() => {
- const browser = document.getElementById("browser");
- if (browser) {
- clearInterval(intervalID);
- combinedStatusBarButtons();
- }
- }, 300);
- })();
- /* ================================================================================================================================================================================================================================= */
- /* Small pinned tabs in vertical tab bar */
- vivaldi.jdhooks.hookModuleExport("yoga-layout", "", exprt => {
- // If it didn't return the yogised layout, normally you'd hook
- // tabs_TabStrip.createFlexBoxLayout
- return function(layout, ...e) {
- // See https://yogalayout.com/playground/ (or just the root URL)
- // to get a better understanding of some of the properties
- // Yoga is used only for the tab bar, but anyway check it
- if (layout.children && layout.children.some(t => t.type === "tab")
- && layout.style.flexDirection === "column") { // It's vertical
- let lastPinned = -1, // They should come first but just in case
- thisRowWidth = 0,
- fromTop = 0
- layout.children = layout.children.reduce((ary, elm) => {
- let keep = false;
- if (elm.type !== "tab")
- keep = true;
- // Isn't pinned (nor is any other in its stack)
- if (!keep && !(elm.tab.page && elm.tab.page.pinned)
- && (elm.tab.get("type") !== "group" || elm.tab.pages.every(p => !p.pinned)))
- keep = true;
- // Might be pinned, but this is the "normal" display in 2nd tab bar
- if (!keep && elm.tab.page.getIn(["extData", "group"]) && elm.tab.get("type") === "tab")
- keep = true;
- if (keep) {
- ary.push(elm)
- return ary
- }
- // It's a pinned tab
- if ((thisRowWidth + 30) > layout.style.width) {
- fromTop += 30
- thisRowWidth = 0
- }
- elm.style = {
- ...elm.style,
- ...{
- position: "absolute",
- left: thisRowWidth,
- top: fromTop,
- flex: false,
- width: 30,
- minWidth: 30,
- maxWidth: 30,
- height: 30,
- minHeight: 30,
- maxHeight: 30
- }
- }
- thisRowWidth += 30
- // Make sure that it's before non-pinned tabs
- ary.splice(lastPinned + 1, 0, elm)
- lastPinned++
- return ary
- }, [])
- if (lastPinned > -1) {
- // Add a spacer so that normal tabs don't cover pinned
- layout.children.splice(lastPinned + 1, 0, {
- type: "blank",
- style: {
- flex: false,
- height: fromTop + 30,
- minHeight: fromTop + 30,
- maxHeight: fromTop + 30
- },
- tab: {} // Don't crash/error pls
- })
- const ret = exprt(layout, ...e)
- // Avoid actually rendering it
- layout.children[lastPinned + 1].layout = undefined
- return ret
- }
- }
- return exprt(layout, ...e)
- }
- })
- /* ================================================================================================================================================================================================================================= */
- /* Change the width of a tab */
- (function(){
- const defaultSettings = {
- horizontal: {
- unpinnedActiveWidthMax: 180,
- unpinnedInactiveWidthMax: 180,
- pinnedActiveWidthMax: 30,
- pinnedInactiveWidthMax: 30,
- pinnedAllowShrink: false
- },
- vertical: {
- unpinnedActiveHeightMax: 150,
- unpinnedInactiveHeightMax: 150,
- pinnedActiveHeightMax: 30,
- pinnedInactiveHeightMax: 30,
- unpinnedActiveHeightMin: 30,
- unpinnedInactiveHeightMin: 30,
- pinnedActiveHeightMin: 30,
- pinnedInactiveHeightMin: 30,
- }
- }
- const maxWidthLimit = 500
- const maxHeightLimit = 200
- function completeObject(obj, fallback = defaultSettings) {
- Object.keys(fallback).forEach(key => {
- if (!obj.hasOwnProperty(key))
- obj[key] = fallback[key]
- else if (typeof obj[key] == "object")
- obj[key] = completeObject(obj[key], fallback[key])
- })
- return obj
- }
- vivaldi.jdhooks.hookModuleExport("vivaldiSettings", "default", exports => {
- let oldGetDefault = exports.getDefault
- exports.getDefault = name => {
- switch (name) {
- case "TAB_SIZE": return defaultSettings
- default: return oldGetDefault(name)
- }
- }
- return exports
- })
- vivaldi.jdhooks.hookClass('tabs_TabStrip', cls => {
- return vivaldi.jdhooks.insertWatcher(class extends cls {
- getTabStyle(info, horz, focus, pin, stack, thumb) {
- let tabStyle = super.getTabStyle(...arguments)
- const tabSize = completeObject(this.state.jdVivaldiSettings.TAB_SIZE)
- if (horz) {
- const key = (pin ? 'pinned' : 'unpinned')
- + (focus ? 'Active' : 'Inactive') + 'WidthMax'
- tabStyle.maxWidth = tabSize.horizontal[key]
- if (pin) {
- if (tabSize.horizontal.pinnedAllowShrink) {
- tabStyle.flex = true
- delete tabStyle.minWidth
- } else tabStyle.minWidth = tabStyle.maxWidth
- }
- if (tabStyle.maxWidth > maxWidthLimit)
- delete tabStyle.maxWidth
- } else {
- const key = (pin ? 'pinned' : 'unpinned')
- + (focus ? 'Active' : 'Inactive')
- tabStyle.maxHeight = tabSize.vertical[key + 'HeightMax']
- tabStyle.minHeight = tabSize.vertical[key + 'HeightMin']
- if (tabStyle.maxHeight < tabStyle.minHeight)
- tabStyle.maxHeight = tabStyle.minHeight
- if (tabStyle.maxHeight > maxHeightLimit)
- delete tabStyle.maxHeight
- tabStyle.flex = !tabStyle.maxHeight || (tabStyle.minHeight != tabStyle.maxHeight)
- }
- return tabStyle
- }
- }, { settings: ['TAB_SIZE'] })
- })
- // Settings
- vivaldi.jdhooks.hookClass('settings_tabs_TabOptions', cls => {
- const React = vivaldi.jdhooks.require('React')
- const settings = vivaldi.jdhooks.require('vivaldiSettings')
- const PrefKeys = vivaldi.jdhooks.require("_PrefKeys")
- const TabWidthSettings = vivaldi.jdhooks.insertWatcher(class extends React.Component {
- onSliderChanged(dir, setting, event) {
- if (event.target && event.target.value) {
- let newState = completeObject(this.state.jdVivaldiSettings.TAB_SIZE)
- let newVal = parseInt([event.target.value])
- newState[dir] = {
- ...newState[dir],
- ...{ [setting]: newVal }
- }
- settings.set({ ['TAB_SIZE']: newState })
- }
- }
- onAllowShrinkChanged(event) {
- if (event.target && event.target.checked !== null) {
- let newState = completeObject(this.state.jdVivaldiSettings.TAB_SIZE)
- newState.horizontal = {
- ...newState.horizontal,
- ...{ ['pinnedAllowShrink']: !!event.target.checked }
- }
- settings.set({ ['TAB_SIZE']: newState })
- }
- }
- createSlider(dir, setting, label, canAuto = true) {
- let val = completeObject(this.state.jdVivaldiSettings.TAB_SIZE)[dir][setting]
- let displ = ''
- const maxLimit = (dir == 'horizontal') ? maxWidthLimit : maxHeightLimit
- if (canAuto) {
- if (val > maxLimit) val = maxLimit + 10
- displ = val > maxLimit ? 'Auto' : val + 'px'
- } else {
- val = Math.min(val, maxLimit)
- displ = val + 'px'
- }
- return React.createElement('div', { className: 'setting-single' },
- React.createElement('h3', null, label),
- React.createElement('input', {
- type: 'range',
- value: val,
- min: 30, // Less than 30 seems to have no effect
- max: canAuto ? maxLimit + 10 : maxLimit,
- step: 10,
- onChange: this.onSliderChanged.bind(this, dir, setting)
- }),
- React.createElement('span', null, displ))
- }
- render() {
- const tabSize = completeObject(this.state.jdVivaldiSettings.TAB_SIZE)
- const barPosition = this.state.jdPrefs[PrefKeys.kTabsBarPosition]
- if (barPosition == 'top' || barPosition == 'bottom') {
- return [
- this.createSlider('horizontal', 'unpinnedActiveWidthMax', 'Active Tab Max Width'),
- this.createSlider('horizontal', 'unpinnedInactiveWidthMax', 'Inactive Tab Max Width'),
- this.createSlider('horizontal', 'pinnedActiveWidthMax', 'Active Pinned Tab Max Width'),
- this.createSlider('horizontal', 'pinnedInactiveWidthMax', 'Pinned Tab Max Width'),
- React.createElement('div', { className: 'setting-single' },
- React.createElement('label', null,
- React.createElement('input', {
- type: 'checkbox',
- checked: tabSize.horizontal.pinnedAllowShrink,
- onChange: this.onAllowShrinkChanged.bind(this)
- }),
- React.createElement('span', null, 'Allow Shrinking of Pinned Tabs')))]
- } else {
- const ary = [];
- [true, false].forEach(isMax => {
- const side = isMax ? 'Max' : 'Min'
- ary.push(
- this.createSlider('vertical', `unpinnedActiveHeight${side}`, `Active Tab ${side} Height`, isMax),
- this.createSlider('vertical', `unpinnedInactiveHeight${side}`, `Inactive Tab ${side} Height`, isMax),
- this.createSlider('vertical', `pinnedActiveHeight${side}`, `Active Pinned Tab ${side} Height`, isMax),
- this.createSlider('vertical', `pinnedInactiveHeight${side}`, `Pinned Tab ${side} Height`, isMax))
- })
- return ary
- }
- }
- }, { settings: ['TAB_SIZE'], prefs: [PrefKeys.kTabsBarPosition] })
- return class extends cls {
- render() {
- let sup = super.render()
- if (sup.props && sup.props.children) {
- let section = sup.props.children.find(c =>
- c && c.props && c.props.className == 'setting-group tab-min-width')
- if (section && section.props && section.props.children) {
- let custom = React.createElement(TabWidthSettings, this.props)
- section.props.children.push(custom)
- }
- }
- return sup
- }
- }
- })
- })()
- // Change the width of a tab
- (function(){
- const defaultSettings = {
- horizontal: {
- unpinnedActiveWidthMax: 180,
- unpinnedInactiveWidthMax: 180,
- pinnedActiveWidthMax: 30,
- pinnedInactiveWidthMax: 30,
- pinnedAllowShrink: false
- },
- vertical: {
- unpinnedActiveHeightMax: 150,
- unpinnedInactiveHeightMax: 150,
- pinnedActiveHeightMax: 30,
- pinnedInactiveHeightMax: 30,
- unpinnedActiveHeightMin: 30,
- unpinnedInactiveHeightMin: 30,
- pinnedActiveHeightMin: 30,
- pinnedInactiveHeightMin: 30,
- }
- }
- const maxWidthLimit = 500
- const maxHeightLimit = 200
- function completeObject(obj, fallback = defaultSettings) {
- Object.keys(fallback).forEach(key => {
- if (!obj.hasOwnProperty(key))
- obj[key] = fallback[key]
- else if (typeof obj[key] == "object")
- obj[key] = completeObject(obj[key], fallback[key])
- })
- return obj
- }
- vivaldi.jdhooks.hookModuleExport("vivaldiSettings", "default", exports => {
- let oldGetDefault = exports.getDefault
- exports.getDefault = name => {
- switch (name) {
- case "TAB_SIZE": return defaultSettings
- default: return oldGetDefault(name)
- }
- }
- return exports
- })
- vivaldi.jdhooks.hookClass('tabs_TabStrip', cls => {
- return vivaldi.jdhooks.insertWatcher(class extends cls {
- getTabStyle(info, horz, focus, pin, stack, thumb) {
- let tabStyle = super.getTabStyle(...arguments)
- const tabSize = completeObject(this.state.jdVivaldiSettings.TAB_SIZE)
- if (horz) {
- const key = (pin ? 'pinned' : 'unpinned')
- + (focus ? 'Active' : 'Inactive') + 'WidthMax'
- tabStyle.maxWidth = tabSize.horizontal[key]
- if (pin) {
- if (tabSize.horizontal.pinnedAllowShrink) {
- tabStyle.flex = true
- delete tabStyle.minWidth
- } else tabStyle.minWidth = tabStyle.maxWidth
- }
- if (tabStyle.maxWidth > maxWidthLimit)
- delete tabStyle.maxWidth
- } else {
- const key = (pin ? 'pinned' : 'unpinned')
- + (focus ? 'Active' : 'Inactive')
- tabStyle.maxHeight = tabSize.vertical[key + 'HeightMax']
- tabStyle.minHeight = tabSize.vertical[key + 'HeightMin']
- if (tabStyle.maxHeight < tabStyle.minHeight)
- tabStyle.maxHeight = tabStyle.minHeight
- if (tabStyle.maxHeight > maxHeightLimit)
- delete tabStyle.maxHeight
- tabStyle.flex = !tabStyle.maxHeight || (tabStyle.minHeight != tabStyle.maxHeight)
- }
- return tabStyle
- }
- }, { settings: ['TAB_SIZE'] })
- })
- // Settings
- vivaldi.jdhooks.hookClass('settings_tabs_TabOptions', cls => {
- const React = vivaldi.jdhooks.require('React')
- const settings = vivaldi.jdhooks.require('vivaldiSettings')
- const PrefKeys = vivaldi.jdhooks.require("_PrefKeys")
- const TabWidthSettings = vivaldi.jdhooks.insertWatcher(class extends React.Component {
- onSliderChanged(dir, setting, event) {
- if (event.target && event.target.value) {
- let newState = completeObject(this.state.jdVivaldiSettings.TAB_SIZE)
- let newVal = parseInt([event.target.value])
- newState[dir] = {
- ...newState[dir],
- ...{ [setting]: newVal }
- }
- settings.set({ ['TAB_SIZE']: newState })
- }
- }
- onAllowShrinkChanged(event) {
- if (event.target && event.target.checked !== null) {
- let newState = completeObject(this.state.jdVivaldiSettings.TAB_SIZE)
- newState.horizontal = {
- ...newState.horizontal,
- ...{ ['pinnedAllowShrink']: !!event.target.checked }
- }
- settings.set({ ['TAB_SIZE']: newState })
- }
- }
- createSlider(dir, setting, label, canAuto = true) {
- let val = completeObject(this.state.jdVivaldiSettings.TAB_SIZE)[dir][setting]
- let displ = ''
- const maxLimit = (dir == 'horizontal') ? maxWidthLimit : maxHeightLimit
- if (canAuto) {
- if (val > maxLimit) val = maxLimit + 10
- displ = val > maxLimit ? 'Auto' : val + 'px'
- } else {
- val = Math.min(val, maxLimit)
- displ = val + 'px'
- }
- return React.createElement('div', { className: 'setting-single' },
- React.createElement('h3', null, label),
- React.createElement('input', {
- type: 'range',
- value: val,
- min: 30, // Less than 30 seems to have no effect
- max: canAuto ? maxLimit + 10 : maxLimit,
- step: 10,
- onChange: this.onSliderChanged.bind(this, dir, setting)
- }),
- React.createElement('span', null, displ))
- }
- render() {
- const tabSize = completeObject(this.state.jdVivaldiSettings.TAB_SIZE)
- const barPosition = this.state.jdPrefs[PrefKeys.kTabsBarPosition]
- if (barPosition == 'top' || barPosition == 'bottom') {
- return [
- this.createSlider('horizontal', 'unpinnedActiveWidthMax', 'Active Tab Max Width'),
- this.createSlider('horizontal', 'unpinnedInactiveWidthMax', 'Inactive Tab Max Width'),
- this.createSlider('horizontal', 'pinnedActiveWidthMax', 'Active Pinned Tab Max Width'),
- this.createSlider('horizontal', 'pinnedInactiveWidthMax', 'Pinned Tab Max Width'),
- React.createElement('div', { className: 'setting-single' },
- React.createElement('label', null,
- React.createElement('input', {
- type: 'checkbox',
- checked: tabSize.horizontal.pinnedAllowShrink,
- onChange: this.onAllowShrinkChanged.bind(this)
- }),
- React.createElement('span', null, 'Allow Shrinking of Pinned Tabs')))]
- } else {
- const ary = [];
- [true, false].forEach(isMax => {
- const side = isMax ? 'Max' : 'Min'
- ary.push(
- this.createSlider('vertical', `unpinnedActiveHeight${side}`, `Active Tab ${side} Height`, isMax),
- this.createSlider('vertical', `unpinnedInactiveHeight${side}`, `Inactive Tab ${side} Height`, isMax),
- this.createSlider('vertical', `pinnedActiveHeight${side}`, `Active Pinned Tab ${side} Height`, isMax),
- this.createSlider('vertical', `pinnedInactiveHeight${side}`, `Pinned Tab ${side} Height`, isMax))
- })
- return ary
- }
- }
- }, { settings: ['TAB_SIZE'], prefs: [PrefKeys.kTabsBarPosition] })
- return class extends cls {
- render() {
- let sup = super.render()
- if (sup.props && sup.props.children) {
- let section = sup.props.children.find(c =>
- c && c.props && c.props.className == 'setting-group tab-min-width')
- if (section && section.props && section.props.children) {
- let custom = React.createElement(TabWidthSettings, this.props)
- section.props.children.push(custom)
- }
- }
- return sup
- }
- }
- })
- })()
- /* ================================================================================================================================================================================================================================= */
- (function () {
- // ============================================================================================================
- // Gives Zoom Interface in the Address Bar
- // - made by nomadic on the Vivaldi Forums
- // ============================================================================================================
- function zoomControl() {
- // CONFIGURATION: ---------------------------------------------------------------
- // - in Vivaldi's settings you can set the default page zoom, this
- // will follow that if RESET_ZOOM_LEVEL is set to 100
- const RESET_ZOOM_LEVEL = 100; // 100 % -> the zoom that hitting the reset button will default to
- const ZOOM_INCREMENT_AMOUNT = 10; // 10 % -> the amount the zoom is either raised or lowered
- // MODES----------------
- // Mode 0: only clicking button opens and closes the panel
- // Mode 1: clicking the button opens the panel and the panel auto closes if not hovered over
- // Option for mode 1:
- // FADE_OUT_TIME -> the number of seconds the panel goes without hover before closing
- // Mode 2: just hovering over the button will open the panel and the panel auto closes if not hovered over
- // Options for mode 2:
- // FADE_OUT_TIME -> the number of seconds the panel goes without hover before closing
- // IS_AUTO_OPENED_ON_ADDRESSBAR -> instead of only the button being hovered, the whole address bar is used
- const MODE = 1;
- // ---------------------
- // Option for modes 1 and 2:
- const FADE_OUT_TIME = 3; // 3 seconds -> can be set to any positive half second increment (ex. 0, 0.5, 1, 1.5 ...)
- // Option for mode 2:
- const IS_AUTO_OPENED_ON_ADDRESSBAR = false;
- // ------------------------------------------------------------------------------
- // Creates the zoom button and panel initially, and then updates the icon depending on the zoom level
- function updateZoomIcon(zoomInfo) {
- let newZoom = zoomInfo.newZoomFactor;
- let zoomIconPath;
- // create the button if it isn't already there
- let alreadyExists = document.getElementById("zoomIcon-c");
- if (!alreadyExists) {
- // CHANGE: Added in Update #4
- // a guaranteed div to the left of the button
- let elementToTheLeft = document.createElement("div");
- elementToTheLeft.style.transition = "0.5s";
- elementToTheLeft.id = "el2left";
- let zoomBtn = document.createElement("div");
- zoomBtn.id = "zoom-hover-target";
- zoomBtn.innerHTML = `
- <div class="zoom-parent">
- <div class="zoom-panel">
- <div class="page-zoom-controls-c">
- <div class="button-toolbar button-toolbar-c reset-zoom-c" title="Reset Zoom">
- <button tabindex="-1" class="button-textonly-c" id="zoom-reset-c">
- <span class="button-title">Reset</span>
- </button>
- </div>
- <div class="button-toolbar button-toolbar-c" title="Zoom Out">
- <button tabindex="-1" id="zoom-out-c">
- <span>
- <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
- <path d="M4 8C4 8.55228 4.44772 9 5 9H11C11.5523 9 12 8.55228 12 8C12 7.44772 11.5523 7 11 7H5C4.44772 7 4 7.44772 4 8Z"></path>
- </svg>
- </span>
- </button>
- </div>
- <span id="zoom-percent-c"></span>
- <div class="button-toolbar button-toolbar-c" title="Zoom In">
- <button tabindex="-1" id="zoom-in-c">
- <span>
- <svg viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
- <path d="M7 7V5C7 4.44772 7.44772 4 8 4C8.55228 4 9 4.44772 9 5V7H11C11.5523 7 12 7.44772 12 8C12 8.55228 11.5523 9 11 9H9V11C9 11.5523 8.55228 12 8 12C7.44772 12 7 11.5523 7 11V9H5C4.44772 9 4 8.55228 4 8C4 7.44772 4.44772 7 5 7H7Z"></path>
- </svg>
- </span>
- </button>
- </div>
- </div>
- </div>
- </div>
- <div class="button-toolbar ZoomButton-Button">
- <button tabindex="-1" title="Adjust Zoom" id="zoom-panel-btn" type="button" class="ToolbarButton-Button">
- <span>
- <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewbox="0 0 16 16" id="zoomIcon-c">
- </svg>
- </span>
- </button>
- </div>
- `;
- // inserts the button to the left of the bookmark icon
- const addressBar = document.querySelector(".UrlBar-AddressField");
- const bookmarkBtn = addressBar.getElementsByClassName("BookmarkButton")[0];
- let before = bookmarkBtn;
- if (!bookmarkBtn) {
- const lastAddressBarElement = document.querySelector(
- ".toolbar-insideinput.toolbar-insideinput.toolbar-insideinput:last-of-type"
- );
- before = lastAddressBarElement;
- }
- addressBar.insertBefore(zoomBtn, before);
- // CHANGE:Added in Update #4
- // divs next to the button aren't static,so created my own div to push
- addressBar.insertBefore(elementToTheLeft, zoomBtn);
- // listener for the magnifying glass button to expand or collapse the control panel
- document.getElementById("zoom-panel-btn").addEventListener("click", function () {
- let nav = document.getElementsByClassName("zoom-panel")[0];
- navToggle(nav, elementToTheLeft);
- });
- // listener for the zoom in button in the zoom control panel
- document.getElementById("zoom-in-c").addEventListener("click", incrementPercent);
- // listener for the zoom out button in the zoom control panel
- document.getElementById("zoom-out-c").addEventListener("click", decrementPercent);
- // listener for the zoom reset button in the zoom control panel
- document.getElementById("zoom-reset-c").addEventListener("click", resetZoom);
- // starts esentially a hover listener that modes 1 and 2 need
- if (MODE === 1 || MODE === 2) {
- zoomPanelHoverTracker();
- }
- }
- // set the icon based on the new zoom level
- if (newZoom < RESET_ZOOM_LEVEL / 100) {
- // zoomed in
- zoomIconPath = `
- <path d="M5.83 9.65a.5.5 0 00-.29.13L1.32 14c-.46.47.23 1.17.7.7l4.22-4.22a.5.5 0 00-.42-.83zm3.6-8.5a5.41 5.41 0 00-5.4 5.4 5.4 5.4 0 105.4-5.4zm0 .99a4.4 4.4 0 11-4.41 4.41 4.4 4.4 0 014.42-4.42zM7.16 6.06c-.66 0-.66.98 0 .98h4.57c.65 0 .65-.98 0-.98z"/>
- `;
- } else if (newZoom > RESET_ZOOM_LEVEL / 100) {
- // zoomed out
- zoomIconPath = `
- <path d="M5.83 9.65a.5.5 0 00-.3.13L1.31 14c-.46.47.23 1.17.7.7l4.22-4.22a.5.5 0 00-.4-.84zm3.6-8.5a5.41 5.41 0 00-5.4 5.4 5.4 5.4 0 0010.81 0 5.4 5.4 0 00-5.4-5.4zm0 .98a4.4 4.4 0 014.42 4.41 4.41 4.41 0 11-4.41-4.4zm-.06 1.63a.5.5 0 00-.43.5v1.79h-1.8c-.65 0-.65.98 0 .98h1.8v1.81c0 .66.99.66.99 0v-1.8h1.79c.65 0 .65-.99 0-1h-1.8V4.27a.5.5 0 00-.55-.5z"/>
- `;
- } else {
- // default zoom icon
- zoomIconPath = `
- <path d="M5.87 9.71c-.11.01-.2.06-.29.14l-4.37 4.37c-.46.45.23 1.14.7.68l4.36-4.37a.48.48 0 00-.41-.82zm3.55-8.36A5.33 5.33 0 004.1 6.67a5.32 5.32 0 105.32-5.32zm0 .97a4.33 4.33 0 11-4.34 4.34 4.33 4.33 0 014.34-4.35z"/>
- `;
- }
- // insert the new icon
- let zoomSVG = document.getElementById("zoomIcon-c");
- zoomSVG.innerHTML = zoomIconPath;
- // make the percent in the controls match the current zoom level
- updatePercent(newZoom);
- }
- // Makes the zoom controls slide out
- function openNav(nav, elToLeft) {
- nav.classList.add("expanded-nav-c");
- nav.parentElement.parentElement.classList.add("zoom-hover-target--active");
- elToLeft.classList.add("expanded-left-c");
- }
- // Hides the zoom controls
- function closeNav(nav, elToLeft) {
- nav.classList.remove("expanded-nav-c");
- nav.parentElement.parentElement.classList.remove("zoom-hover-target--active");
- elToLeft.classList.remove("expanded-left-c");
- }
- // Toggles the zoom controls open or closed depending on the current state
- function navToggle(nav, elToLeft) {
- if (nav.offsetWidth === 0) {
- return openNav(nav, elToLeft);
- } else {
- return closeNav(nav, elToLeft);
- }
- }
- // Puts the zoom level percentage in the zoom controls panel
- function updatePercent(zoomLevel) {
- let zoomPercent = Math.round(zoomLevel * 100);
- let percentageSpan = document.getElementById("zoom-percent-c");
- percentageSpan.innerHTML = zoomPercent + " %";
- }
- // Zooms in the page by the specified increment
- function incrementPercent() {
- chrome.tabs.getZoom(function (zoomLevel) {
- let newZoomLevel = zoomLevel + ZOOM_INCREMENT_AMOUNT / 100;
- // Max zoom that Vivaldi allows is 500 %
- if (newZoomLevel <= 5) {
- chrome.tabs.setZoom(newZoomLevel, updatePercent(newZoomLevel));
- }
- });
- }
- // Zooms out the page by the specified increment
- function decrementPercent() {
- chrome.tabs.getZoom(function (zoomLevel) {
- let newZoomLevel = zoomLevel - ZOOM_INCREMENT_AMOUNT / 100;
- // Min zoom that Vivaldi allows is 20 %
- if (newZoomLevel >= 0.2) {
- chrome.tabs.setZoom(newZoomLevel, updatePercent(newZoomLevel));
- }
- });
- }
- // Sets the zoom back to the default zoom level
- // - in Vivaldi's settings you can set the default page zoom, this
- // will follow that if RESET_ZOOM_LEVEL is set to "100"
- function resetZoom() {
- let zoomLevel = RESET_ZOOM_LEVEL / 100;
- chrome.tabs.setZoom(zoomLevel, updatePercent(zoomLevel));
- }
- // For modes 1 and 2:
- // Tracks if you are hovering over the zoom controls
- function zoomPanelHoverTracker() {
- let zoomPanel = document.getElementsByClassName("zoom-panel")[0];
- let elementToTheLeft = zoomPanel.parentElement.parentElement.previousElementSibling;
- let isHovered = false;
- let intervalID = null;
- let count = 0;
- // selects which element must be hovered to trigger action
- let hoverElement;
- if (MODE === 2 && IS_AUTO_OPENED_ON_ADDRESSBAR) {
- let addressBar = document.querySelector(".UrlBar-AddressField");
- hoverElement = addressBar;
- } else {
- let zoomBtnAndPanel = document.getElementById("zoom-hover-target");
- hoverElement = zoomBtnAndPanel;
- }
- // when the element is hovered, reset the interval counter and opens the controls if needed
- hoverElement.onmouseover = function () {
- count = 0;
- isHovered = true;
- if (MODE !== 1) {
- openNav(zoomPanel, elementToTheLeft);
- }
- };
- // when the element loses hover, closes the controls if enough time has passed
- hoverElement.onmouseout = function () {
- // removes any previous counters (needed for if hover is lost and regained multiple times)
- if (intervalID) {
- clearInterval(intervalID);
- }
- isHovered = false;
- // start a counter to see how long it has been since the element was last hovered
- intervalID = setInterval(function () {
- // only increment the counter as long as hover isn't regained
- if (isHovered === false) {
- count++;
- }
- // once the correct amount of time has ellapsed, close the controls panel
- if (count >= FADE_OUT_TIME * 2) {
- closeNav(zoomPanel, elementToTheLeft);
- clearInterval(intervalID);
- }
- }, 500);
- };
- }
- // CHANGE: Added in Update #1
- // updates zoom percentage on tab change
- function tabChangeUpdateZoomWrapper() {
- chrome.tabs.getZoom(function (zoomLevel) {
- let zoomInfo = {
- newZoomFactor: zoomLevel,
- };
- updateZoomIcon(zoomInfo);
- });
- }
- // zoom change listener
- chrome.tabs.onZoomChange.addListener(updateZoomIcon);
- // CHANGE: Added in Update #1
- // Listener for active tab change
- chrome.tabs.onActivated.addListener(tabChangeUpdateZoomWrapper);
- // CHANGE: Added in Update #4
- // Initially load icon, stopped getting added on startup in 3.5
- chrome.tabs.getZoom(function (zoomLevel) {
- let zoomInfo = {
- newZoomFactor: zoomLevel,
- };
- updateZoomIcon(zoomInfo);
- });
- }
- // Loop waiting for the browser to load the UI
- let intervalID = setInterval(function () {
- const browser = document.getElementById("browser");
- if (browser) {
- clearInterval(intervalID);
- zoomControl();
- }
- }, 300);
- })();
- /* ================================================================================================================================================================================================================================= */
- /* Toggle tab button before AddressBar */
- {
- function toggleTabsOnClick() {
- const WindowActions = vivaldi.jdhooks.require("WindowActions");
- WindowActions.toggleTabs(window.vivaldiWindowId);
- }
- vivaldi.jdhooks.hookClass("toolbars_Toolbar", (origClass) => {
- const React = vivaldi.jdhooks.require("React");
- const SettingsPaths = vivaldi.jdhooks.require("_PrefKeys");
- const ToolbarButton = vivaldi.jdhooks.require("toolbars_ToolbarButton");
- return class extends origClass {
- render() {
- let ret = super.render();
- if (this.props.name == SettingsPaths.kToolbarsNavigation) {
- ret.props.children.push(
- React.createElement(ToolbarButton, {
- tooltip: "Toggle Tab Bar",
- onClick: toggleTabsOnClick,
- image: vivaldi.jdhooks.require("_svg_tabs_large"),
- })
- );
- }
- return ret;
- }
- };
- });
- }
- /* ================================================================================================================================================================================================================================= */
- /* Theme Import and Export */
- // version 2021.9.0
- // https://forum.vivaldi.net/topic/33154/import-and-export-themes
- // Adds functionality to import, export, backup, sort and move themes to
- // Vivaldi's settings page.
- (function () {
- function _checkImport() {
- // written by tam710562
- if (
- typeof _test.colors !== "object" ||
- typeof _test.colors.accentBg !== "string" ||
- !/^#(?:[0-9a-f]{3}){1,2}$/i.test(_test.colors.accentBg) ||
- typeof _test.colors.baseBg !== "string" ||
- !/^#(?:[0-9a-f]{3}){1,2}$/i.test(_test.colors.baseBg) ||
- typeof _test.colors.baseFg !== "string" ||
- !/^#(?:[0-9a-f]{3}){1,2}$/i.test(_test.colors.baseFg) ||
- typeof _test.colors.highlightBg !== "string" ||
- !/^#(?:[0-9a-f]{3}){1,2}$/i.test(_test.colors.highlightBg) ||
- typeof _test.name !== "string" ||
- typeof _test.settings !== "object" ||
- typeof _test.settings.accentFromPage !== "boolean" ||
- typeof _test.settings.accentOnWindow !== "boolean" ||
- (typeof _test.settings.borderRadius !== "number" &&
- typeof _test.settings.borderRadius !== "string") ||
- typeof _test.settings.tabsTransparent !== "boolean" ||
- typeof _test.version !== "number"
- ) {
- return false;
- } else {
- return true;
- }
- }
- function _message(pnt) {
- clearTimeout(_timeout);
- if (pnt === "export") {
- _msg.innerText = "Theme code copied to clipboard.";
- } else if (pnt === "backup") {
- _msg.innerText = "Backup copied to clipboard.";
- } else if (pnt === "import") {
- _msg.innerText = "Theme imported.";
- } else if (pnt === "restore") {
- _msg.innerText = "Backup imported.";
- } else if (pnt === "notice") {
- _msg.innerText = "Nothing to import. Check console.log";
- } else if (pnt === "sort") {
- _msg.innerText = "User themes sorted alphabetically.";
- } else {
- _msg.innerText = "Theme code error.";
- }
- _timeout = setTimeout(function () {
- _msg.innerText = "";
- }, 5000);
- }
- function _importBackup() {
- chrome.storage.local.get({ THEMES_USER: "" }, function (res) {
- var userThemes = res.THEMES_USER;
- console.log("Importing themes...");
- for (i = 0; i < _set.length; i++) {
- _test = _set[i];
- var test = _checkImport;
- if (test()) {
- var compare = userThemes.findIndex((x) => x.name == _set[i].name);
- if (compare === -1) {
- var ok = true;
- userThemes.push(_set[i]);
- console.log(_set[i].name + " imported");
- } else {
- console.log(_set[i].name + " is a duplicate");
- }
- } else {
- console.log(_set[i].name + " failed");
- }
- }
- if (ok === true) {
- chrome.storage.local.set({ THEMES_USER: userThemes }, function () {
- _message("restore");
- });
- } else {
- _message("notice");
- }
- });
- }
- function _importTheme() {
- event.stopPropagation();
- event.preventDefault();
- if (_eventType === "paste") {
- var clipboardData = event.clipboardData || window.clipboardData;
- var themeCode = clipboardData.getData("text");
- } else {
- var themeCode = event.dataTransfer.getData("text");
- }
- try {
- _set = JSON.parse(themeCode);
- } catch (err) {
- _message("error");
- return;
- }
- if (Object.keys(_set)[0] === "colors") {
- _test = _set;
- var test = _checkImport;
- if (test()) {
- const nameField = document.querySelector(".theme-name");
- nameField.select();
- document.execCommand("insertText", false, _set.name);
- chrome.storage.local.get({ THEMES_USER: "" }, function (imp) {
- var userThemes = imp.THEMES_USER;
- for (i = 0; i < userThemes.length; i++) {
- if (userThemes[i].name === nameField.value) {
- _set.name = nameField.value;
- userThemes[i] = _set;
- chrome.storage.local.set({
- THEMES_USER: userThemes,
- BROWSER_COLOR_ACCENT_BG: _set.colors.accentBg,
- BROWSER_COLOR_BG: _set.colors.baseBg,
- BROWSER_COLOR_FG: _set.colors.baseFg,
- BROWSER_COLOR_HIGHLIGHT_BG: _set.colors.highlightBg,
- TABCOLOR_BEHIND_TABS: _set.settings.accentOnWindow,
- USE_TABCOLOR: _set.settings.accentFromPage,
- BORDER_RADIUS: _set.settings.borderRadius,
- USE_TAB_TRANSPARENT_TABS: _set.settings.tabsTransparent,
- THEME_CURRENT: _set.name,
- });
- _message("import");
- break;
- }
- }
- });
- } else {
- _message("error");
- }
- } else if (Object.keys(_set)[0] === "0") {
- _importBackup();
- } else {
- _message("error");
- }
- }
- function _exportTheme(event) {
- if (event.altKey) {
- var backup = true;
- }
- if (event.shiftKey) {
- var order = true;
- }
- chrome.storage.local.get(
- {
- THEME_CURRENT: "",
- THEMES_USER: "",
- },
- function (exp) {
- const themeName = exp.THEME_CURRENT;
- const userThemes = exp.THEMES_USER;
- if (backup === true) {
- const themeCode = JSON.stringify(userThemes);
- navigator.clipboard.writeText(themeCode);
- _message("backup");
- } else if (order === true) {
- userThemes.sort(function (a, b) {
- return a.name.localeCompare(b.name);
- });
- chrome.storage.local.set({ THEMES_USER: userThemes }, function () {
- _message("sort");
- });
- } else {
- for (i = 0; i < userThemes.length; i++) {
- if (userThemes[i].name === themeName) {
- const themeCode = JSON.stringify(userThemes[i]);
- navigator.clipboard.writeText(themeCode);
- _message("export");
- break;
- }
- }
- }
- }
- );
- }
- function _moveTheme() {
- chrome.storage.local.get(
- {
- THEME_CURRENT: "",
- THEMES_USER: "",
- },
- function (mv) {
- const themeName = mv.THEME_CURRENT;
- const userThemes = mv.THEMES_USER;
- var index = userThemes.findIndex((x) => x.name == themeName);
- if (index !== -1) {
- if (_toMove === "left") {
- if (index !== 0) {
- var fromI = userThemes[index];
- var toI = userThemes[index - 1];
- userThemes[index - 1] = fromI;
- userThemes[index] = toI;
- } else {
- return;
- }
- } else {
- var last = userThemes.length - 1;
- if (index < last) {
- var fromI = userThemes[index];
- var toI = userThemes[index + 1];
- userThemes[index + 1] = fromI;
- userThemes[index] = toI;
- } else {
- return;
- }
- }
- chrome.storage.local.set({ THEMES_USER: userThemes });
- }
- }
- );
- }
- function createPort() {
- if (
- document.querySelector(_themeBtn).classList.contains("button-pressed")
- ) {
- const cont = document.querySelector(".theme-metadata");
- const importBtn = document.createElement("input");
- importBtn.setAttribute("type", "text");
- importBtn.setAttribute("placeholder", "Import");
- importBtn.id = "importTheme";
- cont.appendChild(importBtn);
- const exportBtn = document.createElement("input");
- exportBtn.setAttribute("type", "submit");
- exportBtn.classList.add("primary");
- exportBtn.setAttribute("value", "Export");
- exportBtn.setAttribute(
- "title",
- "Click to export theme\nAlt-click to backup all themes\nShift-click to sort themes"
- );
- exportBtn.id = "exportTheme";
- cont.appendChild(exportBtn);
- _msg = document.createElement("span");
- _msg.id = "modInfo";
- cont.appendChild(_msg);
- document
- .getElementById("exportTheme")
- .addEventListener("click", _exportTheme);
- const importInput = document.getElementById("importTheme");
- importInput.addEventListener("paste", function () {
- _eventType = "paste";
- _importTheme(event);
- });
- importInput.addEventListener("drop", function () {
- _eventType = "drop";
- _importTheme(event);
- });
- _timeout = {};
- }
- }
- function portThemes() {
- const styleCheck = document.getElementById("portThemes");
- if (!styleCheck) {
- const style = document.createElement("style");
- style.type = "text/css";
- style.id = "portThemes";
- style.innerHTML =
- ".move-left button:focus, .move-right button:focus {border-color: var(--colorBorder) !important;box-shadow: none !important;}#importTheme, #exportTheme {width: 80px;margin-left: 6px;}#importTheme::-webkit-input-placeholder {opacity: 1;color: var(--colorHighlightBg);text-align: center;}#modInfo {margin-top: 6px;margin-left: 12px;}";
- document.getElementsByTagName("head")[0].appendChild(style);
- }
- const modCheck = document.querySelector(".move-left");
- if (!modCheck) {
- const group = document.createElement("div");
- group.classList.add("toolbar", "toolbar-group");
- group.innerHTML =
- '<div class="button-toolbar move-left"><button draggable="false" tabindex="auto" title="Move Theme Left" class=""><svg width="16" height="16" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1216 448v896q0 26-19 45t-45 19-45-19l-448-448q-19-19-19-45t19-45l448-448q19-19 45-19t45 19 19 45z"/></svg></button></div><hr><div class="button-toolbar move-right"><button draggable="false" tabindex="auto" title="Move Theme Right" class=""><svg width="16" height="16" viewBox="0 0 1792 1792" xmlns="http://www.w3.org/2000/svg"><path d="M1152 896q0 26-19 45l-448 448q-19 19-45 19t-45-19-19-45v-896q0-26 19-45t45-19 45 19l448 448q19 19 19 45z"/></svg></button></div>';
- document
- .querySelector(_themeBtn)
- .parentNode.parentNode.appendChild(group);
- document
- .querySelector(".move-left")
- .addEventListener("click", function () {
- _toMove = "left";
- _moveTheme();
- });
- document
- .querySelector(".move-right")
- .addEventListener("click", function () {
- _toMove = "right";
- _moveTheme();
- });
- document.querySelector(_themeBtn).addEventListener("click", function () {
- setTimeout(createPort, 50);
- });
- }
- }
- const settingsUrl =
- "chrome-extension://mpognobbkildjkofajifpdfhcoklimli/components/settings/settings.html?path=";
- const _themeBtn =
- ".setting-group.unlimited > .toolbar.toolbar-default > .button-toolbar > button";
- chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
- if (changeInfo.url === `${settingsUrl}themes`) {
- setTimeout(portThemes, 100);
- }
- });
- })();
- /* ================================================================================================================================================================================================================================= */
Advertisement
Add Comment
Please, Sign In to add comment