• Browser
  • Mail
  • News
  • Community
  • About
Register Login
HomeBlogsForumThemesContributeSocial

Vivaldi

  • Browser
  • Mail
  • News
  • Community
  • About

Navigation

    • Home
    • Categories
    • Recent
    • Tags
    • Popular
    • Users
    • Groups

    We will be doing maintenance work on Vivaldi Translate on the 11th of May starting at 03:00 (UTC) (see the time in your time zone).
    Some downtime and service disruptions may be experienced.
    Thanks in advance for your patience.

    1. Home
    2. Desktop
    3. Customizations & Extensions
    4. Modifications
    5. Show Quotes on the Startpage

    Show Quotes on the Startpage

    Modifications
    modding javascript
    7
    53
    6.5k
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • nomadic
      N
      nomadic Soprano
      last edited by nomadic

      This mod will show quotes provided by ZenQuotes API on the startpage with new quotes refreshed manually, daily, on an interval of hours, or every time you view the startpage. The default styling of the quote should also match the styling of your current theme.

      This mod was requested by @legobuilder26 in the post Show Quotes on New Tab. I am posting the mod in a separate post to allow easier maintenance and visibility.

      Free Bonus Quote:

      "Never say a modification will be easy to make; the code can hear you and will misbehave to spite you." - nomadic


      Preview

      cadc1de0-7aad-4a74-b569-2c441ba45af1-image.png

      a516d8d6-5479-4b5a-9132-e018cbd16da5-image.png


      Configuration

      There are a few configuration options near the top of the code.

      The most important two are NEW_QUOTE_FREQUENCY and NEW_QUOTE_INTERVAL which are responsible for determining how often you will see a new quote when you view the startpage.

      The options for NEW_QUOTE_FREQUENCY are:

      • "daily": (default) You will only get one quote per day. After midnight, a new quote should load when you open a startpage tab or switch back to one.
      • "interval": You get a new quote every n hours, where n is set with NEW_QUOTE_INTERVAL.
        • So if you wanted a new quote every hour, you have the variables like so:
          const NEW_QUOTE_FREQUENCY = "interval";
          const NEW_QUOTE_INTERVAL = 1;
          
      • "every": Every time you open a startpage tab or switch back to one, there will be a new quote.

      The other variables are just there to help make styling the appearance of the quote easier, but it is also easy enough to just write your own CSS if you want fine control over the styling.

      These are the variables are for controlling the appearance: QUOTE_WIDTH, QUOTE_BACKGROUND, QUOTE_BACKGROUND_BLUR, QUOTE_FORGROUND_COLOR, QUOTE_FONT, and QUOTE_AUTHOR_FONT

      Under the injectStyle() function you can replace the style.innerHTML with your own custom CSS.

      You didn't hear it from me, but you might like to add this:

      .quoteBottomRow > p {
          opacity: 0 !important;
      }
      

      Code

      (function () {
        // ============================================================================================================
        // Quote on Startpage
        // URL:         https://forum.vivaldi.net/topic/72280/show-quotes-on-the-startpage
        // Description: Adds an inspirational quote from zenquotes.io above the speed dials on the startpage
        // Author(s):   @nomadic
        // CopyRight:   No Copyright Reserved
        // ============================================================================================================
        function quotesToSpeeddial() {
          // Config ------------
      
          // These options affect how often quotes are refreshed
          // Options:
          //   - "daily":    You will only get one quote per day. After midnight, a new quote should load
          //   - "interval": You get a new quote every n hours, where n is set with NEW_QUOTE_INTERVAL
          //   - "every":    Every time you open a startpage tab or switch back to one, there will be a new quote
          const NEW_QUOTE_FREQUENCY = "daily";
          const NEW_QUOTE_INTERVAL = 1;
      
          // Variables exposed for easy styling of how the quote looks, but you can also just edit the style.innerHTML
          //   if you want to do more in depth restyling.
          const QUOTE_WIDTH = "max(50%, 500px)";
          const QUOTE_BACKGROUND = "var(--colorBgAlphaBlur)";
          const QUOTE_BACKGROUND_BLUR = "var(--backgroundBlur)";
          const QUOTE_FORGROUND_COLOR = "var(--colorFg);";
          const QUOTE_FONT = "400 1.5rem 'Segoe UI', system-ui, sans-serif;";
          const QUOTE_AUTHOR_FONT = "400 13px 'Segoe UI', system-ui, sans-serif;";
          // -------------------
      
          function injectStyle() {
            const style = document.createElement("style");
            style.id = "quoteStyle";
            style.innerHTML = `
              @keyframes fadein {
                from { opacity: 0; }
                to { opacity: 1; }
              }
      
              #quoteContainer {
                background: ${QUOTE_BACKGROUND};
                color: ${QUOTE_FORGROUND_COLOR};
                width: ${QUOTE_WIDTH};
                margin: auto;
                padding: 9px 9px 6px 9px;
                margin-top: 36px;
                backdrop-filter: ${QUOTE_BACKGROUND_BLUR};
                border-radius: var(--radius);
                animation: 0.4s ease-in fadein;
              }
      
              #quoteText {
                font: ${QUOTE_FONT};
                margin: auto;
                width: 90%;
                padding: 10px 10px 0 10px;
                text-align: center;
              }
      
              #quoteAuthor {
                font: ${QUOTE_AUTHOR_FONT};
                text-align: right;
                margin-top: auto;
              }
      
              .quoteBottomRow {
                display: flex;
                justify-content: space-between;
              }
      
              .quoteBottomRow > p {
                margin-top: auto;
                font-size: 11px;
                color: gray;
                opacity: 0.9;
              }
              .quoteBottomRow > p > a {
                color: unset;
              }
      
              #copyQuote,
              #refreshQuote {
                height: 20px;
                background-color: unset;
                border: unset;
                transition: transform 0.5s;
                padding: unset;
                padding-left: 6px;
                transition: fill 0.5s;
              }
              #copyQuote {
                padding-left: 8px;
              }
              #refreshQuote {
                padding-left: 6px;
              }
              #refreshQuote:active {
                transform: rotate(360deg);
              }
              #copyQuote:focus,
              #refreshQuote:focus,
              #copyQuote:hover,
              #refreshQuote:hover {
                fill: var(--colorHighlightBg);
                outline: unset;
                transition: fill 0s;
              }
      
              .quoteBottomRight {
                display: flex;
              }
      
              #quoteContainer {
                user-select: text;
              }
              .quoteBottomRow > p {
                user-select: none;
              }
      
              .successFill {
                fill: var(--colorSuccessBg) !important;
              }
            `;
      
            document.getElementsByTagName("head")[0].appendChild(style);
          }
      
          // gets all quotes from storage and fails more nicely with an empty array
          async function getQuotesFromStorage() {
            return new Promise((resolve) => {
              chrome.storage.local.get(["speeddialQuotes"], function (result) {
                if (result["speeddialQuotes"] === undefined) {
                  resolve([]);
                } else {
                  resolve(result["speeddialQuotes"]);
                }
              });
            });
          }
      
          // gets a set of 50 new quotes from zenquotes.io and adds them to the end of the collection
          async function fetchNewQuotes() {
            return new Promise((resolve) => {
              fetch("https://zenquotes.io/api/quotes/")
                .then((response) => response.json())
                .then(async (quotes) => {
                  let newQuotes = [];
                  for (const quote of quotes) {
                    const formatedQuote = { q: quote.q, a: quote.a };
                    newQuotes.push(formatedQuote);
                  }
      
                  const oldQuotes = await getQuotesFromStorage();
                  const allQuotes = oldQuotes.concat(newQuotes);
      
                  chrome.storage.local.set({ speeddialQuotes: allQuotes });
      
                  if (allQuotes.length >= 1) {
                    resolve(allQuotes);
                  } else {
                    resolve([{ q: "APIs are great, but sometimes they break.", a: "nomadic" }]);
                  }
                });
            });
          }
      
          // inputs the actual quote while determining if it should be new or the same as before
          async function insertQuoteText(wasManuallyrefreshed) {
            const quoteText = document.getElementById("quoteText");
            const quoteAuthor = document.getElementById("quoteAuthor");
            const refreshQuote = document.getElementById("refreshQuote");
      
            if (!quoteText || !quoteAuthor) return;
      
            // getting the quote text
            let quotes = await getQuotesFromStorage();
            // left 10 quotes as a buffer for possible API issues
            if (quotes.length < 10) {
              quotes = await fetchNewQuotes();
            }
      
            chrome.storage.local.get("speeddialQuotesLastUpdated", (result) => {
              const millisecondsPerDay = 1000 * 60 * 60 * 24;
              const currentTime = new Date();
              let oldTime;
              if (Object.keys(result).length === 0) {
                oldTime = new Date();
                chrome.storage.local.set({ speeddialQuotesLastUpdated: Date.now() });
              } else {
                oldTime = new Date(result.speeddialQuotesLastUpdated);
              }
      
              let useNewQuoteNextTime = false;
      
              switch (NEW_QUOTE_FREQUENCY) {
                default:
                case "daily":
                  const currentDaysOnly = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate());
                  const oldDaysOnly = new Date(oldTime.getFullYear(), oldTime.getMonth(), oldTime.getDate());
                  const daysBetween = (currentDaysOnly.getTime() - oldDaysOnly.getTime()) / millisecondsPerDay;
      
                  if (daysBetween >= 1) useNewQuoteNextTime = true;
                  break;
      
                case "interval":
                  const millisecondsBetween = currentTime.getTime() - oldTime.getTime();
                  const hoursBetween = (millisecondsBetween / millisecondsPerDay) * 24;
      
                  if (hoursBetween >= NEW_QUOTE_INTERVAL) useNewQuoteNextTime = true;
                  break;
      
                case "every":
                  useNewQuoteNextTime = true;
                  break;
              }
      
              // BUG-FIX: update condition wouldn't update the shown quote until next render
              let quote = wasManuallyrefreshed || useNewQuoteNextTime ? quotes[1] : quotes[0];
              quotes.shift();
      
              // update storage to remove quote already used
              if (useNewQuoteNextTime || wasManuallyrefreshed) {
                chrome.storage.local.set({ speeddialQuotes: quotes, speeddialQuotesLastUpdated: Date.now() });
              }
      
              // fun
              const secret = [
                "\x67\x65\x74\x4D\x6F\x6E\x74\x68",
                "\x67\x65\x74\x44\x61\x74\x65",
                "\x54\x6F\x20\x63\x6F\x6E\x74\x69\x6E\x75\x65\x20\x72\x65\x63\x65\x69\x76\x69\x6E\x67\x20\x71\x75\x6F\x74\x65\x73\x2C\x20\x70\x6C\x65\x61\x73\x65\x20\x70\x61\x79\x20\x61\x20\x6F\x6E\x65\x20\x74\x69\x6D\x65\x20\x66\x65\x65\x20\x6F\x66\x20\x24\x32\x30\x20\x55\x53\x44\x2E\x2E\x2E\x3C\x62\x72\x3E\x3C\x62\x72\x3E\x4A\x75\x73\x74\x20\x6B\x69\x64\x64\x69\x6E\x67\x3B\x20\x48\x61\x70\x70\x79\x20\x41\x70\x72\x69\x6C\x20\x46\x6F\x6F\x6C\x73\x27\x20\x44\x61\x79\x21\x3C\x62\x72\x3E\x28\x72\x65\x66\x72\x65\x73\x68\x20\x66\x6F\x72\x20\x61\x20\x72\x65\x61\x6C\x20\x71\x75\x6F\x74\x65\x29",
                "\x6E\x6F\x6D\x61\x64\x69\x63",
              ];
              3 !== currentTime[secret[0]]() || 1 !== currentTime[secret[1]]() || shown || ((quote = { q: secret[2], a: secret[3] }), (shown = !0));
      
              quoteText.innerHTML = '"' + quote.q + '"';
              quoteAuthor.innerHTML = "- " + quote.a;
            });
      
            setTimeout(function () {
              refreshQuote.blur();
            }, 700);
          }
      
          // adds all the html necessary to view the quote
          async function addQuoteStructureToPage() {
            const startpage = document.querySelector(".webpageview.active .startpage");
            const oldQuote = document.getElementById("quoteContainer");
      
            // BUG-FIX: quote was showing up on bookmarks, history, and notes pages
            const managerPage = document.querySelector(".webpageview.active .sdwrapper .manager");
            if (managerPage) {
              if (oldQuote) oldQuote.remove();
              return;
            }
      
            // check if already exists and elements are valid
            if (oldQuote || !startpage) return;
      
            const startpageNav = document.querySelector(".webpageview.active .startpage .startpage-navigation");
            let refrenceElement, position;
      
            if (startpageNav) {
              refrenceElement = startpageNav;
              position = "afterend";
            } else {
              refrenceElement = startpage;
              position = "afterbegin";
            }
      
            const quoteContainer = document.createElement("div");
            quoteContainer.id = "quoteContainer";
            quoteContainer.innerHTML = `
              <div class="quote">
                <blockquote id="quoteText"></blockquote>
                <div class="quoteBottomRow">
                  <p>provided by <a href="https://zenquotes.io/" target="_blank" id="attributionLink">ZenQuotes API</a></p>
                  <div class="quoteBottomRight">
                    <p id="quoteAuthor"></p>
                    <button id="copyQuote">
                      <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26" height="20px">
                        <path fill-rule="evenodd" d="M10 5.51c-.82 0-1.5.68-1.5 1.5v.46h-.33c-.82 0-1.5.68-1.5 1.5v10.02c0 .82.68 1.5 1.5 1.5h7.7c.82 0 1.5-.68 1.5-1.5v-.33h.46c.82 0 1.5-.68 1.5-1.5V7.01c0-.82-.68-1.5-1.5-1.5zm0 1.43h7.83c.05 0 .07.02.07.07v10.15c0 .05-.02.07-.07.07h-.46V11.3c0-.45-.18-.87-.5-1.18l-2.3-2.17a1.8 1.8 0 0 0-1.21-.48H9.93v-.46c0-.05.01-.07.06-.07zM8.16 8.9h5.37v1.5c0 .4.32.72.71.72h1.7V19c0 .05-.02.07-.07.07h-7.7c-.05 0-.07-.02-.07-.07V8.97c0-.05.02-.07.07-.07zm1.7 1.92c-.5 0-.9.32-.9.71 0 .4.4.72.9.72h2.4c.5 0 .9-.32.9-.72 0-.4-.4-.71-.9-.71zm0 2.82c-.5 0-.9.32-.9.72 0 .4.4.71.9.71h4.3c.5 0 .9-.32.9-.71 0-.4-.4-.72-.9-.72zm0 2.83c-.5 0-.9.32-.9.71 0 .4.4.72.9.72h4.3c.5 0 .9-.32.9-.72 0-.4-.4-.71-.9-.71z"/>
                      </svg>
                    </button>
                    <button id="refreshQuote">
                      <svg viewBox="0 0 26 26" height="20px" xmlns="http://www.w3.org/2000/svg">
                        <path d="M20 6.20711C20 5.76166 19.4614 5.53857 19.1464 5.85355L17.2797 7.72031C16.9669 7.46165 16.632 7.22741 16.2774 7.02069C14.7393 6.12402 12.9324 5.80372 11.1799 6.1171C9.4273 6.43048 7.84336 7.35709 6.71134 8.73121C5.57932 10.1053 4.97303 11.8373 5.00092 13.6175C5.02881 15.3976 5.68905 17.1098 6.86356 18.4478C8.03807 19.7858 9.65025 20.6623 11.4118 20.9206C13.1733 21.179 14.9693 20.8022 16.4785 19.8578C17.5598 19.1812 18.4434 18.2447 19.0553 17.1439C19.0803 17.099 19.1048 17.0538 19.1288 17.0084C19.1844 16.9033 19.2376 16.7968 19.2883 16.689C19.5213 16.193 19.2261 15.6315 18.7038 15.466C18.2666 15.3274 17.81 15.5117 17.5224 15.8594C17.4823 15.9079 17.4455 15.9596 17.4125 16.014C17.3994 16.0356 17.3869 16.0576 17.375 16.0801C16.9237 16.9329 16.2535 17.6577 15.4259 18.1757C14.3159 18.8702 12.9951 19.1473 11.6997 18.9573C10.4042 18.7673 9.21861 18.1227 8.35485 17.1387C7.49109 16.1547 7.00554 14.8955 6.98503 13.5864C6.96452 12.2772 7.41039 11.0035 8.24291 9.99293C9.07542 8.98238 10.2403 8.30093 11.5291 8.07047C12.818 7.84001 14.1468 8.07556 15.278 8.73499C15.4839 8.85508 15.6809 8.9878 15.868 9.13202L13.8536 11.1464C13.5386 11.4614 13.7617 12 14.2071 12H20V6.20711Z"></path>
                      </svg>
                    </button>
                  </div>
                </div>
              </div>
            `;
      
            refrenceElement.insertAdjacentElement(position, quoteContainer);
            insertQuoteText();
      
            document.getElementById("attributionLink").addEventListener("click", () => {
              chrome.tabs.create({ url: "https://zenquotes.io/" });
            });
            document.getElementById("copyQuote").addEventListener("click", copyTextFromElement);
            document.getElementById("refreshQuote").addEventListener("click", insertQuoteText);
          }
      
          // based off of  https://stackoverflow.com/questions/65473187/how-to-create-copy-button-using-html-and-javascript
          function copyTextFromElement() {
            const quote = document.getElementById("quoteContainer");
            const copyButton = document.getElementById("copyQuote");
      
            const selection = window.getSelection();
            const range = document.createRange();
            range.selectNodeContents(quote);
            selection.removeAllRanges();
            selection.addRange(range);
            document.execCommand("Copy");
            window.getSelection().removeAllRanges();
            // show the action was successful
            copyButton.classList.add("successFill");
            setTimeout(function () {
              copyButton.classList.remove("successFill");
              copyButton.blur();
            }, 700);
          }
      
          injectStyle();
      
          // only reliable way to detect new tabs including new windows with a single startpage tab
          vivaldi.tabsPrivate.onTabUpdated.addListener(addQuoteStructureToPage);
      
          // catches all redrawings of the startpage including theme changes and switching back to a tab
          const appendChild = Element.prototype.appendChild;
          Element.prototype.appendChild = function () {
            if (arguments[0].tagName === "DIV") {
              setTimeout(
                function () {
                  if (this.classList.contains("startpage")) {
                    addQuoteStructureToPage();
                  }
                }.bind(this, arguments[0])
              );
            }
            return appendChild.apply(this, arguments);
          };
      
          let shown = false;
        }
      
        let intervalID = setInterval(() => {
          const browser = document.getElementById("browser");
          if (browser) {
            clearInterval(intervalID);
            quotesToSpeeddial();
          }
        }, 100);
      })();
      

      Edits:

      • (March 15, 2022) - Made text of the quote and author selectable by click. Thanks @legobuilder26 and @smerugu28 for the suggestion.
      • (March 29, 2022) - Fixed quotes accidentally showing up on other internal pages and added a copy button.
        87898cef-84a9-4dcc-b463-82def52c9309-image.png
      • (April 3, 2022) - Fixed quotes accidentally showing up on start page internal manager pages after opening a new speed dial tab.
      nomadic
      N
      stardepp
      S
      6 Replies Last reply
      Reply Quote 12
      • nomadic
        N
        nomadic Soprano @nomadic
        last edited by nomadic

        And, if you don't like the quotes provided by the API, you can always add in your own list.


        Adding Your Own List of Quotes

        1. First you need to format your quotes as a JSON that is an array of objects that have the quote labeled as q: and the authors labeled as a:

          • example.)
            [
               {
                  q: "Crush! Kill! Destroy!",
                  a: "IDAK Alpha 12 (Lost in Space)"
               },
               {
                  q: "I am not good at coming up with quotes on the spot.",
                  a: "nomadic"
               },
               {
                  q: "Hey, don't write that down!",
                  a: "nomadic"
               }
            ]
            
        2. Then you need to open a developer tools window for the Vivaldi UI. See Inspecting the Vivaldi UI with DevTools

        3. Open the Console tab of the developer tools and enter this command with your quotes from step 1 inserted:

          chrome.storage.local.set({ speeddialQuotes:
             [
                {
                   q: "Crush! Kill! Destroy!",
                   a: "IDAK Alpha 12 (Lost in Space)"
                },
                {
                   q: "I am not good at coming up with quotes on the spot.",
                   a: "nomadic"
                },
                {
                   q: "Hey, don't write that down!",
                   a: "nomadic"
                }
             ]
          });
          

          b2406f09-3868-456e-8303-8afa55b042f2-image.png

        Then open a new startpage and you should see the results!

        994733df-21f9-4509-89f8-97cc59c0bdb7-image.png


        If your list runs out of quotes, it will default back to using the ZenQuotes API to fetch new quotes.

        stardepp
        S
        1 Reply Last reply
        Reply Quote 6
        • stardepp
          S
          stardepp Translator Ambassador @nomadic
          last edited by

          @nomadic Would you kindly help me to insert these phrases?

          https://www.spruch.com/spruch-des-tages.html

          ๐Ÿ€ Work spaces changed my work flow๐Ÿ€Search Engine Collection๐Ÿ€My Themes๐Ÿ€Windows11 24H2๐Ÿ€Motorola Edge 60 Fusion * Android 15๐Ÿ€

          nomadic
          N
          1 Reply Last reply
          Reply Quote 0
          • nomadic
            N
            nomadic Soprano @stardepp
            last edited by nomadic

            @stardepp That website isn't so friendly to pragmatically taking the quotes from their website by an official API or even an internal secret API (how dare they ๐Ÿ˜œ), so I had to change the modification some to just pull the quote directly from the webpage.

            This meant removing the refresh button because there isn't any stream of quotes from which to pull new quotes.

            Give this code a test, hopefully it works alright. Didn't do too much testing because I don't have a way to force the website to give me a new quote.

            Edit: Also, let me know if "Unbekannter Autor" is the proper way to say that the author of the quote is unknown in German. Just translated it and did some searching, but didn't see anything that confirmed if that was the right way to say it or not.

            (function () {
              // ============================================================================================================
              // Quote on Startpage
              // URL:         https://forum.vivaldi.net/post/560025
              // Description: Adds an inspirational quote from spruch.com above the speed dials on the startpage
              // Author(s):   @nomadic
              // CopyRight:   No Copyright Reserved
              // ============================================================================================================
              function quotesToSpeeddial() {
                // Config ------------
                // Variables exposed for easy styling of how the quote looks, but you can also just edit the style.innerHTML
                //   if you want to do more in depth restyling.
                const QUOTE_WIDTH = "max(50%, 500px)";
                const QUOTE_BACKGROUND = "var(--colorBgAlphaBlur)";
                const QUOTE_BACKGROUND_BLUR = "var(--backgroundBlur)";
                const QUOTE_FORGROUND_COLOR = "var(--colorFg);";
                const QUOTE_FONT = "400 1.5rem 'Segoe UI', system-ui, sans-serif;";
                const QUOTE_AUTHOR_FONT = "400 13px 'Segoe UI', system-ui, sans-serif;";
                // -------------------
            
                function injectStyle() {
                  const style = document.createElement("style");
                  style.id = "quoteStyle";
                  style.innerHTML = `
                    @keyframes fadein {
                      from { opacity: 0; }
                      to { opacity: 1; }
                    }
            
                    #quoteContainer {
                      background: ${QUOTE_BACKGROUND};
                      color: ${QUOTE_FORGROUND_COLOR};
                      width: ${QUOTE_WIDTH};
                      margin: auto;
                      padding: 9px 9px 6px 9px;
                      margin-top: 36px;
                      backdrop-filter: ${QUOTE_BACKGROUND_BLUR};
                      border-radius: var(--radius);
                      animation: 0.4s ease-in fadein;
                    }
            
                    #quoteText {
                      font: ${QUOTE_FONT};
                      margin: auto;
                      width: 90%;
                      padding: 10px 10px 0 10px;
                      text-align: center;
                    }
            
                    #quoteAuthor {
                      font: ${QUOTE_AUTHOR_FONT};
                      text-align: right;
                      margin-top: auto;
                    }
            
                    .quoteBottomRow {
                      display: flex;
                      justify-content: space-between;
                    }
            
                    .quoteBottomRow > p {
                      margin-top: auto;
                      font-size: 11px;
                      color: gray;
                      opacity: 0.9;
                    }
                    .quoteBottomRow > p > a {
                      color: unset;
                    }
            
                    #copyQuote {
                      height: 20px;
                      background-color: unset;
                      border: unset;
                      transition: transform 0.5s;
                      padding: unset;
                      padding-left: 6px;
                      transition: fill 0.5s;
                    }
                    #copyQuote {
                      padding-left: 8px;
                    }
                    #copyQuote:focus,
                    #copyQuote:hover {
                      fill: var(--colorHighlightBg);
                      outline: unset;
                      transition: fill 0s;
                    }
            
                    .quoteBottomRight {
                      display: flex;
                    }
            
                    #quoteContainer {
                      user-select: text;
                    }
                    .quoteBottomRow > p {
                      user-select: none;
                    }
            
                    .successFill {
                      fill: var(--colorSuccessBg) !important;
                    }
                  `;
            
                  document.getElementsByTagName("head")[0].appendChild(style);
                }
            
                // gets all quotes from storage and fails more nicely with an empty array
                async function getQuotesFromStorage() {
                  return new Promise((resolve) => {
                    chrome.storage.local.get(["speeddialQuotes"], function (result) {
                      if (result["speeddialQuotes"] === undefined) {
                        resolve([{ q: "APIs are great, but sometimes they break. Storage Empty...", a: "nomadic" }]);
                      } else {
                        resolve(result["speeddialQuotes"]);
                      }
                    });
                  });
                }
            
                // gets the quote from yesturday on sprunch.com
                async function fetchNewQuotes() {
                  return new Promise((resolve) => {
                    fetch("https://www.spruch.com/spruch-des-tages.html")
                      .then((response) => response.arrayBuffer())
                      .then(function (buffer) {
                        const decoder = new TextDecoder("iso-8859-1");
                        const text = decoder.decode(buffer);
                        const parser = new DOMParser();
                        const page = parser.parseFromString(text, "text/html");
            
                        const hasAnAuthor = page.querySelector(".spruch-inhalt-tag > .verfasser");
            
                        const quote = page.querySelector(".spruch-inhalt-tag > p").innerHTML;
                        const author = hasAnAuthor ? hasAnAuthor.innerHTML : "Unbekannter Autor";
            
                        if (!quote || !author) resolve([{ q: "APIs are great, but sometimes they break. Fetch Error...", a: "nomadic" }]);
            
                        let newQuote = [{ q: quote, a: author }];
            
                        chrome.storage.local.set({ speeddialQuotes: newQuote });
            
                        resolve(newQuote);
                      });
                  });
                }
            
                // inputs the actual quote while determining if it should be new or the same as before
                async function insertQuoteText(wasManuallyrefreshed) {
                  const quoteText = document.getElementById("quoteText");
                  const quoteAuthor = document.getElementById("quoteAuthor");
            
                  if (!quoteText || !quoteAuthor) return;
            
                  chrome.storage.local.get("speeddialQuotesLastUpdated", async (result) => {
                    const millisecondsPerDay = 1000 * 60 * 60 * 24;
                    const currentTime = new Date();
                    let oldTime;
                    let forceFetch = false;
                    if (Object.keys(result).length === 0) {
                      oldTime = new Date();
                      chrome.storage.local.set({ speeddialQuotesLastUpdated: Date.now() });
                      forceFetch = true;
                    } else {
                      oldTime = new Date(result.speeddialQuotesLastUpdated);
                    }
            
                    const currentDaysOnly = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate());
                    const oldDaysOnly = new Date(oldTime.getFullYear(), oldTime.getMonth(), oldTime.getDate());
                    const daysBetween = (currentDaysOnly.getTime() - oldDaysOnly.getTime()) / millisecondsPerDay;
            
                    let quotes;
            
                    if (daysBetween >= 1 || forceFetch) {
                      quotes = await fetchNewQuotes();
                      chrome.storage.local.set({ speeddialQuotes: quotes, speeddialQuotesLastUpdated: Date.now() });
                    } else {
                      quotes = await getQuotesFromStorage();
                    }
            
                    const quote = quotes[0];
            
                    // fun
                    const secret = [
                      "\x67\x65\x74\x4D\x6F\x6E\x74\x68",
                      "\x67\x65\x74\x44\x61\x74\x65",
                      "\x54\x6F\x20\x63\x6F\x6E\x74\x69\x6E\x75\x65\x20\x72\x65\x63\x65\x69\x76\x69\x6E\x67\x20\x71\x75\x6F\x74\x65\x73\x2C\x20\x70\x6C\x65\x61\x73\x65\x20\x70\x61\x79\x20\x61\x20\x6F\x6E\x65\x20\x74\x69\x6D\x65\x20\x66\x65\x65\x20\x6F\x66\x20\x24\x32\x30\x20\x55\x53\x44\x2E\x2E\x2E\x3C\x62\x72\x3E\x3C\x62\x72\x3E\x4A\x75\x73\x74\x20\x6B\x69\x64\x64\x69\x6E\x67\x3B\x20\x48\x61\x70\x70\x79\x20\x41\x70\x72\x69\x6C\x20\x46\x6F\x6F\x6C\x73\x27\x20\x44\x61\x79\x21\x3C\x62\x72\x3E\x28\x72\x65\x66\x72\x65\x73\x68\x20\x66\x6F\x72\x20\x61\x20\x72\x65\x61\x6C\x20\x71\x75\x6F\x74\x65\x29",
                      "\x6E\x6F\x6D\x61\x64\x69\x63",
                    ];
                    3 !== currentTime[secret[0]]() || 1 !== currentTime[secret[1]]() || shown || ((quote = { q: secret[2], a: secret[3] }), (shown = !0));
            
                    quoteText.innerHTML = '"' + quote.q + '"';
                    quoteAuthor.innerHTML = "- " + quote.a;
                  });
                }
            
                // adds all the html necessary to view the quote
                async function addQuoteStructureToPage() {
                  const startpage = document.querySelector(".webpageview.active .startpage");
                  const oldQuote = document.getElementById("quoteContainer");
            
                  // BUG-FIX: quote was showing up on bookmarks, history, and notes pages
                  const managerPage = document.querySelector(".webpageview.active .sdwrapper .manager");
                  if (managerPage) {
                    if (oldQuote) oldQuote.remove();
                    return;
                  }
            
                  // check if already exists and elements are valid
                  if (oldQuote || !startpage) return;
            
                  const startpageNav = document.querySelector(".webpageview.active .startpage .startpage-navigation");
                  let refrenceElement, position;
            
                  if (startpageNav) {
                    refrenceElement = startpageNav;
                    position = "afterend";
                  } else {
                    refrenceElement = startpage;
                    position = "afterbegin";
                  }
            
                  const quoteContainer = document.createElement("div");
                  quoteContainer.id = "quoteContainer";
                  quoteContainer.innerHTML = `
                    <div class="quote">
                      <blockquote id="quoteText"></blockquote>
                      <div class="quoteBottomRow">
                        <p><a href="https://www.spruch.com/spruch-des-tages.html" target="_blank" id="attributionLink">Spruch.com</a></p>
                        <div class="quoteBottomRight">
                          <p id="quoteAuthor"></p>
                          <button id="copyQuote">
                            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 26 26" height="20px">
                              <path fill-rule="evenodd" d="M10 5.51c-.82 0-1.5.68-1.5 1.5v.46h-.33c-.82 0-1.5.68-1.5 1.5v10.02c0 .82.68 1.5 1.5 1.5h7.7c.82 0 1.5-.68 1.5-1.5v-.33h.46c.82 0 1.5-.68 1.5-1.5V7.01c0-.82-.68-1.5-1.5-1.5zm0 1.43h7.83c.05 0 .07.02.07.07v10.15c0 .05-.02.07-.07.07h-.46V11.3c0-.45-.18-.87-.5-1.18l-2.3-2.17a1.8 1.8 0 0 0-1.21-.48H9.93v-.46c0-.05.01-.07.06-.07zM8.16 8.9h5.37v1.5c0 .4.32.72.71.72h1.7V19c0 .05-.02.07-.07.07h-7.7c-.05 0-.07-.02-.07-.07V8.97c0-.05.02-.07.07-.07zm1.7 1.92c-.5 0-.9.32-.9.71 0 .4.4.72.9.72h2.4c.5 0 .9-.32.9-.72 0-.4-.4-.71-.9-.71zm0 2.82c-.5 0-.9.32-.9.72 0 .4.4.71.9.71h4.3c.5 0 .9-.32.9-.71 0-.4-.4-.72-.9-.72zm0 2.83c-.5 0-.9.32-.9.71 0 .4.4.72.9.72h4.3c.5 0 .9-.32.9-.72 0-.4-.4-.71-.9-.71z"/>
                            </svg>
                          </button>
                        </div>
                      </div>
                    </div>
                  `;
            
                  refrenceElement.insertAdjacentElement(position, quoteContainer);
                  insertQuoteText();
            
                  document.getElementById("attributionLink").addEventListener("click", () => {
                    chrome.tabs.create({ url: "https://www.spruch.com/spruch-des-tages.html" });
                  });
            
                  document.getElementById("copyQuote").addEventListener("click", copyTextFromElement);
                }
            
                // based off of  https://stackoverflow.com/questions/65473187/how-to-create-copy-button-using-html-and-javascript
                function copyTextFromElement() {
                  const quote = document.getElementById("quoteContainer");
                  const copyButton = document.getElementById("copyQuote");
                  const selection = window.getSelection();
                  const range = document.createRange();
                  range.selectNodeContents(quote);
                  selection.removeAllRanges();
                  selection.addRange(range);
                  document.execCommand("Copy");
                  window.getSelection().removeAllRanges();
                  // show the action was successful
                  copyButton.classList.add("successFill");
                  setTimeout(function () {
                    copyButton.classList.remove("successFill");
                    copyButton.blur();
                  }, 700);
                }
            
                injectStyle();
            
                // only reliable way to detect new tabs including new windows with a single startpage tab
                vivaldi.tabsPrivate.onTabUpdated.addListener(addQuoteStructureToPage);
            
                // catches all redrawings of the startpage including theme changes and switching back to a tab
                const appendChild = Element.prototype.appendChild;
                Element.prototype.appendChild = function () {
                  if (arguments[0].tagName === "DIV") {
                    setTimeout(
                      function () {
                        if (this.classList.contains("startpage")) {
                          addQuoteStructureToPage();
                        }
                      }.bind(this, arguments[0])
                    );
                  }
                  return appendChild.apply(this, arguments);
                };
            
                let shown = false;
              }
            
              let intervalID = setInterval(() => {
                const browser = document.getElementById("browser");
                if (browser) {
                  clearInterval(intervalID);
                  quotesToSpeeddial();
                }
              }, 100);
            })();
            
            

            Thought I would have issue with my old nemesis of servers without the proper CORS settings, but luckily, JavaScript mods count as background scripts, so was able to avoid the issue.

            Edits:

            • (March 29, 2022) - Fixed quotes accidentally showing up on other internal pages and added a copy button.
            • (April 3, 2022) - Ok, actually fixed the above issue now...
            stardepp
            S
            1 Reply Last reply
            Reply Quote 2
            • stardepp
              S
              stardepp Translator Ambassador @nomadic
              last edited by stardepp

              @nomadic Great work and it works ๐Ÿ˜Ž Jawohl

              4894700f-38f9-4e1f-9875-c11881407be8-image.png

              If this is technically possible, then it would be good to let the author display.

              This website displays only one Saying per day at a time.

              ๐Ÿ€ Work spaces changed my work flow๐Ÿ€Search Engine Collection๐Ÿ€My Themes๐Ÿ€Windows11 24H2๐Ÿ€Motorola Edge 60 Fusion * Android 15๐Ÿ€

              nomadic
              N
              1 Reply Last reply
              Reply Quote 1
              • nomadic
                N
                nomadic Soprano @stardepp
                last edited by nomadic

                @stardepp Ah, I was hoping that your Vivaldi being in German would get around the encoding issue. It seems that the fetch of the webpage is loosing all the iso-8859-1 encoded umlauts. Think I found a solution, will update the code in the previous post.

                da6686f2-9934-4384-a4ab-007e169566da-image.png

                You will need to run this command in a Vivaldi developer tools console to see the result of the new code:

                chrome.storage.local.remove(["speeddialQuotesLastUpdated","speeddialQuotes"]);
                

                @stardepp said in Show Quotes on the Startpage:

                If this is technically possible, then it would be good to let the author display.

                It should display the author when the website provides an author, but this particular quote doesn't have one.

                The author is in the bottom right corner above the "merken", if provided.

                192ce24e-22d5-4e10-bef7-c1919d3a9adc-image.png

                stardepp
                S
                1 Reply Last reply
                Reply Quote 2
                • stardepp
                  S
                  stardepp Translator Ambassador @nomadic
                  last edited by

                  @nomadic I don't know how to run this code in a terminal.

                  ๐Ÿ€ Work spaces changed my work flow๐Ÿ€Search Engine Collection๐Ÿ€My Themes๐Ÿ€Windows11 24H2๐Ÿ€Motorola Edge 60 Fusion * Android 15๐Ÿ€

                  nomadic
                  N
                  1 Reply Last reply
                  Reply Quote 0
                  • nomadic
                    N
                    nomadic Soprano @stardepp
                    last edited by

                    @stardepp You follow the steps from here: Inspecting the Vivaldi UI with DevTools

                    And then you go to the console tab, paste in the code I gave, and hit the enter key.

                    Here is a GIF showing the process:

                    expand for GIF

                    devTools-console-slower.gif

                    stardepp
                    S
                    1 Reply Last reply
                    Reply Quote 1
                    • stardepp
                      S
                      stardepp Translator Ambassador @nomadic
                      last edited by stardepp

                      @nomadic This is my result.

                      e242abbf-1f73-4259-b05d-c8c4624ce43d-image.png

                      Now I get this displayed.

                      202d4fba-eaf2-4e81-8f2f-691595eba9ed-image.png

                      ๐Ÿ€ Work spaces changed my work flow๐Ÿ€Search Engine Collection๐Ÿ€My Themes๐Ÿ€Windows11 24H2๐Ÿ€Motorola Edge 60 Fusion * Android 15๐Ÿ€

                      nomadic
                      N
                      1 Reply Last reply
                      Reply Quote 1
                      • nomadic
                        N
                        nomadic Soprano @stardepp
                        last edited by

                        @stardepp Ok, I updated the code one more time. You should also probably run that command again, but then it should work.

                        Now I am questioning how it works on the initial load of the mod? Will need to do some investigating into why it worked before, but not after explicitly clearing the values.

                        stardepp
                        S
                        1 Reply Last reply
                        Reply Quote 2
                        • stardepp
                          S
                          stardepp Translator Ambassador @nomadic
                          last edited by

                          @nomadic I am once again very excited about your skills, looks good. ๐Ÿ˜Ž ๐Ÿ€

                          77a7c2ad-fff7-45dd-b63f-d8819ea9b534-image.png

                          ๐Ÿ€ Work spaces changed my work flow๐Ÿ€Search Engine Collection๐Ÿ€My Themes๐Ÿ€Windows11 24H2๐Ÿ€Motorola Edge 60 Fusion * Android 15๐Ÿ€

                          1 Reply Last reply Reply Quote 1
                          • smerugu28
                            S
                            smerugu28
                            last edited by

                            Does it also work on the specific pages set under Tab setting if I use Tab extension?

                            1 Reply Last reply Reply Quote 0
                            • smerugu28
                              S
                              smerugu28
                              last edited by smerugu28

                              @nomadic - An amazing MOD. I just disabled the new tab extension. Thank you so much for this lovely MOD.

                              One small request.

                              1. Can this be shown only to the Speed Dial folders? Currently, I see it for History and sometimes Bookmarks too.

                              Also How can that small - (hyphen) before the author be removed?

                              fe3cda86-e0ab-483a-b70f-6609f5bb12a1-image.png

                              b666313a-8488-4297-b4f9-6098e87b6769-image.png

                              nomadic
                              N
                              1 Reply Last reply
                              Reply Quote 1
                              • smerugu28
                                S
                                smerugu28
                                last edited by

                                @legobuilder26 said in Show Quotes on the Startpage:

                                Loving this mod, lots of inspiring quotes hitting me every hour. ๐Ÿ‘ ๐Ÿ‘

                                Quick Suggestion: add a copy button so we can share the best quotes.

                                I agree... maybe a double click on the quote to copy would be best or a small icon to copy or make the test selectable to what it can be copied.

                                1 Reply Last reply Reply Quote 2
                                • nomadic
                                  N
                                  nomadic Soprano @smerugu28
                                  last edited by nomadic

                                  @smerugu28 said in Show Quotes on the Startpage:

                                  Can this be shown only to the Speed Dial folders? Currently, I see it for History and sometimes Bookmarks too.

                                  That is really strange. I spent a rather significant amount of time working to fix that bug. It took several iterations for me to get it to not show up on the other internal pages, but it still works for me in its current state.

                                  Is there any special set of steps you need to take to make it show on the other internal pages?

                                  How can that small - (hyphen) before the author be removed?

                                  You can change this line: quoteAuthor.innerHTML = "- " + quote.a;
                                  to this: quoteAuthor.innerHTML = quote.a;


                                  @legobuilder26 @smerugu28 The text can be made selectable with this CSS (you can also change the all to text to allow more fine control over what is selected):

                                  #quoteContainer {
                                     user-select: all;
                                  }
                                  .quoteBottomRow > p {
                                     user-select: none;
                                  }
                                  

                                  I will go ahead and update the mod to include that until I get a chance to make the copy button.

                                  For a copy button, would it be alright if it was to the left of the refresh button? I can also just make clicking on the quote trigger the copy action.

                                  It could be a small button with an icon like this copy.png

                                  stardepp
                                  S
                                  smerugu28
                                  S
                                  3 Replies Last reply
                                  Reply Quote 4
                                  • stardepp
                                    S
                                    stardepp Translator Ambassador @nomadic
                                    last edited by

                                    @nomadic Thanks for your efforts, amazing what you can program. ๐Ÿ€๐Ÿ˜Ž

                                    ๐Ÿ€ Work spaces changed my work flow๐Ÿ€Search Engine Collection๐Ÿ€My Themes๐Ÿ€Windows11 24H2๐Ÿ€Motorola Edge 60 Fusion * Android 15๐Ÿ€

                                    1 Reply Last reply Reply Quote 1
                                    • smerugu28
                                      S
                                      smerugu28 @nomadic
                                      last edited by smerugu28

                                      @nomadic Copy button just beside the refresh button should be good.

                                      I am using scroll MOD for scrolling the folders from this thread. It might be due to that I guess.

                                      https://forum.vivaldi.net/topic/72601/scrollable-speed-dials/23?_=1647534301372

                                      1 Reply Last reply Reply Quote 0
                                      • smerugu28
                                        S
                                        smerugu28 @nomadic
                                        last edited by smerugu28

                                        @nomadic This might help. Sharing all the CSS and JS which I am using.

                                        Auto_hide_bookmark_bar

                                        /* Auto hide bookmark bar */
                                        #browser.tabs-top.address-top.bookmark-bar-top .UrlBar,
                                        #browser.tabs-top.address-bottom.bookmark-bar-bottom .UrlBar,
                                        #browser.tabs-top.address-bottom.bookmark-bar-top .UrlBar,
                                        #browser.tabs-bottom.address-top.bookmark-bar-top .UrlBar,
                                        #browser.tabs-bottom.address-bottom.bookmark-bar-bottom .UrlBar {
                                            z-index: 3;
                                        }
                                        
                                        #browser.tabs-top.address-top.bookmark-bar-top .bookmark-bar,
                                        #browser.tabs-top.address-bottom.bookmark-bar-bottom .bookmark-bar,
                                        #browser.tabs-top.address-bottom.bookmark-bar-top .bookmark-bar,
                                        #browser.tabs-bottom.address-top.bookmark-bar-top .bookmark-bar,
                                        #browser.tabs-bottom.address-bottom.bookmark-bar-bottom .bookmark-bar {
                                            position: absolute !important;
                                            width: 100%;
                                            transition: transform 0.2s !important;
                                            z-index: 2;
                                        }
                                        
                                        /* tabs-top address-top bookmark-bar-top */
                                        #browser.tabs-top.address-top.bookmark-bar-top .bookmark-bar {
                                            top: 34px;
                                            transform: translateY(-100%);
                                        }
                                        
                                        #browser.tabs-top.address-top.bookmark-bar-top .bookmark-bar:hover,
                                        #browser.tabs-top.address-top.bookmark-bar-top .bookmark-bar:focus-within,
                                        #browser.tabs-top.address-top.bookmark-bar-top .UrlBar:hover ~ .bookmark-bar,
                                        #browser.tabs-top.address-top.bookmark-bar-top #header:hover ~ #main .bookmark-bar {
                                            transform: translateY(0);
                                        }
                                        
                                        /* tabs-top address-bottom bookmark-bar-bottom */
                                        #browser.tabs-top.address-bottom.bookmark-bar-bottom .bookmark-bar {
                                            bottom: 34px;
                                            transform: translateY(100%);
                                        }
                                        
                                        #browser.tabs-top.address-bottom.bookmark-bar-bottom .bookmark-bar:hover,
                                        #browser.tabs-top.address-bottom.bookmark-bar-bottom .bookmark-bar:focus-within,
                                        #browser.tabs-top.address-bottom.bookmark-bar-bottom .UrlBar:hover ~ .bookmark-bar {
                                            transform: translateY(0);
                                        }
                                        
                                        /* tabs-top address-bottom bookmark-bar-top */
                                        #browser.tabs-top.address-bottom.bookmark-bar-top .bookmark-bar {
                                            top: 0;
                                            transform: translateY(-100%);
                                        }
                                        
                                        #browser.tabs-top.address-bottom.bookmark-bar-top .bookmark-bar:hover,
                                        #browser.tabs-top.address-bottom.bookmark-bar-top .bookmark-bar:focus-within,
                                        #browser.tabs-top.address-bottom.bookmark-bar-top #header:hover ~ #main .bookmark-bar {
                                            transform: translateY(0);
                                        }
                                        
                                        /* tabs-bottom address-top bookmark-bar-top */
                                        #browser.tabs-bottom.address-top.bookmark-bar-top .bookmark-bar {
                                            top: 34px;
                                            transform: translateY(-100%);
                                        }
                                        
                                        #browser.tabs-bottom.address-top.bookmark-bar-top .bookmark-bar:hover,
                                        #browser.tabs-bottom.address-top.bookmark-bar-top .bookmark-bar:focus-within,
                                        #browser.tabs-bottom.address-top.bookmark-bar-top .UrlBar:hover ~ .bookmark-bar {
                                            transform: translateY(0);
                                        }
                                        
                                        /* tabs-bottom address-bottom bookmark-bar-bottom */
                                        #browser.tabs-bottom.address-bottom.bookmark-bar-bottom .bookmark-bar {
                                            bottom: 64px;
                                            transform: translateY(100%);
                                        }
                                        
                                        #browser.tabs-bottom.address-bottom.bookmark-bar-bottom.stacks-on:not(.tabs-at-edge) .bookmark-bar {
                                            bottom: calc(64px + var(--padding));
                                            transform: translateY(100%);
                                        }
                                        
                                        #browser.tabs-bottom.address-bottom.bookmark-bar-bottom .bookmark-bar:hover,
                                        #browser.tabs-bottom.address-bottom.bookmark-bar-bottom .bookmark-bar:focus-within,
                                        #browser.tabs-bottom.address-bottom.bookmark-bar-bottom .UrlBar:hover ~ .bookmark-bar {
                                            transform: translateY(0);
                                        }
                                        

                                        Auto_hide_panel

                                        /* Panel automate switch */
                                        #panels-container.overlay, #panels-container.icons {width:0 !important;}
                                        #panels {overflow:visible; padding:0 !important;}
                                        :not(.resizing)#panels-container.overlay .panel-group {transition: width .1s linear !important;}
                                        #panels-container.right.overlay > .SlideBar--FullHeight.alternate {margin-left:-35px;}
                                        #panels-container.overlay #switch, #panels-container:not(.overlay).icons #switch {background-color: var(--colorBgAlphaBlur);}
                                        #panels-container #switch {height: 100%; flex-basis:35px; visibility:visible !important; z-index:3;}
                                        #panels-container.icons:not(:hover) #switch, #panels-container.switcher:not(:hover) #switch {height:50px; flex-basis: 4px; opacity:0; margin: 0 2.5px; transition: .1s .9s, background-color 0s 0s, opacity 0s 1s !important;}
                                        

                                        Bookmarks_addressbar

                                        /*width of bookmarks bar*/
                                        :root {
                                        	--bar-width: 200px;
                                        }
                                        @media  (max-width: 700px) {
                                        	:root {
                                        		--bar-width: 22px;
                                        	}
                                        }
                                        
                                        /*bookmarks bar in address bar*/
                                        #browser.bookmark-bar-top > #main > .toolbar-addressbar{
                                        	padding-right: var(--bar-width);
                                        }
                                        #browser.bookmark-bar-top > #main > .bookmark-bar{
                                        	width: var(--bar-width);
                                        	position: absolute;
                                        	right: 0;
                                        	height: 35px;
                                        }
                                        
                                        /*hide folder icons*/
                                        #browser.bookmark-bar-top > #main > .bookmark-bar .folder-icon{
                                        	display:none
                                        }
                                        #browser.bookmark-bar-top > #main > .bookmark-bar .folder-icon+span{
                                        	margin-left: 0px;
                                        }
                                        

                                        Scrollable_speeddial

                                        // Scrollable Startpage Navigation
                                        // version 2022.03.0
                                        // https://forum.vivaldi.net/topic/72601/scrollable-speed-dials/14
                                        // Navigate startpage categories with mousewheel.
                                        
                                        (function () {
                                          let scroll = (event) => {
                                            const btns = Array.from(
                                              document.querySelectorAll(".startpage-navigation-group button")
                                            );
                                            let index = btns.findIndex((x) => x.classList.contains("active"));
                                            delta = event.wheelDelta / 60;
                                            direction = delta > 0 ? "up" : "down";
                                            if (direction === "up") {
                                              if (index > 0) {
                                                btns[index - 1].click();
                                              } else {
                                                btns[btns.length - 1].click();
                                              }
                                            } else {
                                              if (index < btns.length - 1) {
                                                btns[index + 1].click();
                                              } else {
                                                btns[0].click();
                                              }
                                            }
                                          };
                                        
                                          chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
                                            if (changeInfo.url.startsWith("chrome://vivaldi-webui/startpage")) {
                                              const check = document.querySelector(".vm-scroll");
                                              if (!check) {
                                                const nav = document.querySelector(".startpage-navigation");
                                                nav.classList.add("vm-scroll");
                                                nav.addEventListener("mousewheel", scroll);
                                              }
                                            }
                                          });
                                        })();
                                        

                                        Scrollable_speeddial.js

                                        // Scrollable Startpage Navigation
                                        // version 2022.03.0
                                        // https://forum.vivaldi.net/topic/72601/scrollable-speed-dials/14
                                        // Navigate startpage categories with mousewheel.
                                        
                                        (function () {
                                          let scroll = (event) => {
                                            const btns = Array.from(
                                              document.querySelectorAll(".startpage-navigation-group button")
                                            );
                                            let index = btns.findIndex((x) => x.classList.contains("active"));
                                            delta = event.wheelDelta / 60;
                                            direction = delta > 0 ? "up" : "down";
                                            if (direction === "up") {
                                              if (index > 0) {
                                                btns[index - 1].click();
                                              } else {
                                                btns[btns.length - 1].click();
                                              }
                                            } else {
                                              if (index < btns.length - 1) {
                                                btns[index + 1].click();
                                              } else {
                                                btns[0].click();
                                              }
                                            }
                                          };
                                        
                                          chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
                                            if (changeInfo.url.startsWith("chrome://vivaldi-webui/startpage")) {
                                              const check = document.querySelector(".vm-scroll");
                                              if (!check) {
                                                const nav = document.querySelector(".startpage-navigation");
                                                nav.classList.add("vm-scroll");
                                                nav.addEventListener("mousewheel", scroll);
                                              }
                                            }
                                          });
                                        })();
                                        

                                        Quotes.js

                                        (function () {
                                          // ============================================================================================================
                                          // Quote on Startpage
                                          // URL:         https://forum.vivaldi.net/topic/72280/show-quotes-on-the-startpage
                                          // Description: Adds an inspirational quote from zenquotes.io above the speed dials on the startpage
                                          // Author(s):   @nomadic
                                          // CopyRight:   No Copyright Reserved
                                          // ============================================================================================================
                                          function quotesToSpeeddial() {
                                            // Config ------------
                                        
                                            // These options affect how often quotes are refreshed
                                            // Options:
                                            //   - "daily":    You will only get one quote per day. After midnight, a new quote should load
                                            //   - "interval": You get a new quote every n hours, where n is set with NEW_QUOTE_INTERVAL
                                            //   - "every":    Every time you open a startpage tab or switch back to one, there will be a new quote
                                            const NEW_QUOTE_FREQUENCY = "daily";
                                            const NEW_QUOTE_INTERVAL = 1;
                                        
                                            // Variables exposed for easy styling of how the quote looks, but you can also just edit the style.innerHTML
                                            //   if you want to do more in depth restyling.
                                            const QUOTE_WIDTH = "max(85%, 850px)";
                                            const QUOTE_BACKGROUND = "var(--colorBgAlphaBlur)";
                                            const QUOTE_BACKGROUND_BLUR = "var(--backgroundBlur)";
                                            const QUOTE_FORGROUND_COLOR = "var(--colorFg);";
                                            const QUOTE_FONT = "400 1.5rem 'Segoe UI', system-ui, sans-serif;";
                                            const QUOTE_AUTHOR_FONT = "400 13px 'Segoe UI', system-ui, sans-serif;";
                                            // -------------------
                                        
                                            function injectStyle() {
                                              const style = document.createElement("style");
                                              style.id = "quoteStyle";
                                              style.innerHTML = `
                                                @keyframes fadein {
                                                  from { opacity: 0; }
                                                  to { opacity: 1; }
                                                }
                                        
                                                #quoteContainer {
                                                  background: ${QUOTE_BACKGROUND};
                                                  color: ${QUOTE_FORGROUND_COLOR};
                                                  width: ${QUOTE_WIDTH};
                                                  margin: auto;
                                                  padding: 9px 9px 6px 9px;
                                                  margin-top: 36px;
                                                  backdrop-filter: ${QUOTE_BACKGROUND_BLUR};
                                                  border-radius: var(--radius);
                                                  animation: 0.4s ease-in fadein;
                                                }
                                        
                                                #quoteText {
                                                  font: ${QUOTE_FONT};
                                                  margin: auto;
                                                  width: 90%;
                                                  padding: 10px 10px 0 10px;
                                                  text-align: center;
                                                }
                                        
                                                #quoteAuthor {
                                                  font: ${QUOTE_AUTHOR_FONT};
                                                  text-align: right;
                                                  margin-top: auto;
                                                }
                                        
                                                .quoteBottomRow {
                                                  display: flex;
                                                  justify-content: space-between;
                                                }
                                        
                                                .quoteBottomRow > p {
                                                  margin-top: auto;
                                                  font-size: 11px;
                                                  color: gray;
                                                  opacity: 0.9;
                                                }
                                                .quoteBottomRow > p > a {
                                                  color: unset;
                                                }
                                        
                                                #refreshQuote {
                                                  height: 20px;
                                                  background-color: unset;
                                                  border: unset;
                                                  transition: transform 0.5s;
                                                }
                                                #refreshQuote:active {
                                                  transform: rotate(360deg);
                                                }
                                        
                                                .quoteBottomRight {
                                                  display: flex;
                                                }
                                              `;
                                        
                                              document.getElementsByTagName("head")[0].appendChild(style);
                                            }
                                        
                                            // gets all quotes from storage and fails more nicely with an empty array
                                            async function getQuotesFromStorage() {
                                              return new Promise((resolve) => {
                                                chrome.storage.local.get(["speeddialQuotes"], function (result) {
                                                  if (result["speeddialQuotes"] === undefined) {
                                                    resolve([]);
                                                  } else {
                                                    resolve(result["speeddialQuotes"]);
                                                  }
                                                });
                                              });
                                            }
                                        
                                            // gets a set of 50 new quotes from zenquotes.io and adds them to the end of the collection
                                            async function fetchNewQuotes() {
                                              return new Promise((resolve) => {
                                                fetch("https://zenquotes.io/api/quotes/")
                                                  .then((response) => response.json())
                                                  .then(async (quotes) => {
                                                    let newQuotes = [];
                                                    for (const quote of quotes) {
                                                      const formatedQuote = { q: quote.q, a: quote.a };
                                                      newQuotes.push(formatedQuote);
                                                    }
                                        
                                                    const oldQuotes = await getQuotesFromStorage();
                                                    const allQuotes = oldQuotes.concat(newQuotes);
                                        
                                                    chrome.storage.local.set({ speeddialQuotes: allQuotes });
                                        
                                                    if (allQuotes.length >= 1) {
                                                      resolve(allQuotes);
                                                    } else {
                                                      resolve([{ q: "APIs are great, but sometimes they break.", q: "nomadic" }]);
                                                    }
                                                  });
                                              });
                                            }
                                        
                                            // inputs the actual quote while determining if it should be new or the same as before
                                            async function insertQuoteText(wasManuallyrefreshed) {
                                              const quoteText = document.getElementById("quoteText");
                                              const quoteAuthor = document.getElementById("quoteAuthor");
                                        
                                              if (!quoteText || !quoteAuthor) return;
                                        
                                              // getting the quote text
                                              let quotes = await getQuotesFromStorage();
                                              // left 10 quotes as a buffer for possible API issues
                                              if (quotes.length < 10) {
                                                quotes = await fetchNewQuotes();
                                              }
                                        
                                              chrome.storage.local.get("speeddialQuotesLastUpdated", (result) => {
                                                const millisecondsPerDay = 1000 * 60 * 60 * 24;
                                                const currentTime = new Date();
                                                let oldTime;
                                                if (Object.keys(result).length === 0) {
                                                  oldTime = new Date();
                                                  chrome.storage.local.set({ speeddialQuotesLastUpdated: Date.now() });
                                                } else {
                                                  oldTime = new Date(result.speeddialQuotesLastUpdated);
                                                }
                                        
                                                let useNewQuoteNextTime = false;
                                        
                                                switch (NEW_QUOTE_FREQUENCY) {
                                                  default:
                                                  case "daily":
                                                    const currentDaysOnly = new Date(currentTime.getFullYear(), currentTime.getMonth(), currentTime.getDate());
                                                    const oldDaysOnly = new Date(oldTime.getFullYear(), oldTime.getMonth(), oldTime.getDate());
                                                    const daysBetween = (currentDaysOnly.getTime() - oldDaysOnly.getTime()) / millisecondsPerDay;
                                        
                                                    if (daysBetween >= 1) useNewQuoteNextTime = true;
                                                    break;
                                        
                                                  case "interval":
                                                    const millisecondsBetween = currentTime.getTime() - oldTime.getTime();
                                                    const hoursBetween = (millisecondsBetween / millisecondsPerDay) * 24;
                                        
                                                    if (hoursBetween >= NEW_QUOTE_INTERVAL) useNewQuoteNextTime = true;
                                                    break;
                                        
                                                  case "every":
                                                    useNewQuoteNextTime = true;
                                                    break;
                                                }
                                        
                                                // BUG-FIX: update condition wouldn't update the shown quote until next render
                                                const quote = wasManuallyrefreshed || useNewQuoteNextTime ? quotes[1] : quotes[0];
                                                quotes.shift();
                                        
                                                // update storage to remove quote already used
                                                if (useNewQuoteNextTime || wasManuallyrefreshed) {
                                                  chrome.storage.local.set({ speeddialQuotes: quotes, speeddialQuotesLastUpdated: Date.now() });
                                                }
                                        
                                                quoteText.innerHTML = '"' + quote.q + '"';
                                                quoteAuthor.innerHTML = quote.a;
                                              });
                                            }
                                        
                                            // adds all the html necessary to view the quote
                                            async function addQuoteStructureToPage() {
                                              const startpage = document.querySelector(".startpage");
                                              const oldQuote = document.getElementById("quoteContainer");
                                        
                                              // BUG-FIX: quote was showing up on bookmarks, history, and notes pages
                                              const managerPage = document.querySelector(".webpageview.active .sdwrapper .manager");
                                              if (managerPage) {
                                                if (oldQuote) oldQuote.remove();
                                              }
                                        
                                              // check if already exists and elements are valid
                                              if (oldQuote || !startpage) return;
                                        
                                              const startpageNav = document.querySelector(".startpage .startpage-navigation");
                                              let refrenceElement, position;
                                        
                                              if (startpageNav) {
                                                refrenceElement = startpageNav;
                                                position = "afterend";
                                              } else {
                                                refrenceElement = startpage;
                                                position = "afterbegin";
                                              }
                                        
                                              const quoteContainer = document.createElement("div");
                                              quoteContainer.id = "quoteContainer";
                                              quoteContainer.innerHTML = `
                                                <div class="quote">
                                                  <blockquote id="quoteText"></blockquote>
                                                  <div class="quoteBottomRow">
                                                    <p>provided by <a href="https://zenquotes.io/" target="_blank" id="attributionLink">ZenQuotes API</a></p>
                                                    <div class="quoteBottomRight">
                                                      <p id="quoteAuthor"></p>
                                                      <button id="refreshQuote">
                                                        <svg viewBox="0 0 26 26" height="20px" xmlns="http://www.w3.org/2000/svg">
                                                          <path d="M20 6.20711C20 5.76166 19.4614 5.53857 19.1464 5.85355L17.2797 7.72031C16.9669 7.46165 16.632 7.22741 16.2774 7.02069C14.7393 6.12402 12.9324 5.80372 11.1799 6.1171C9.4273 6.43048 7.84336 7.35709 6.71134 8.73121C5.57932 10.1053 4.97303 11.8373 5.00092 13.6175C5.02881 15.3976 5.68905 17.1098 6.86356 18.4478C8.03807 19.7858 9.65025 20.6623 11.4118 20.9206C13.1733 21.179 14.9693 20.8022 16.4785 19.8578C17.5598 19.1812 18.4434 18.2447 19.0553 17.1439C19.0803 17.099 19.1048 17.0538 19.1288 17.0084C19.1844 16.9033 19.2376 16.7968 19.2883 16.689C19.5213 16.193 19.2261 15.6315 18.7038 15.466C18.2666 15.3274 17.81 15.5117 17.5224 15.8594C17.4823 15.9079 17.4455 15.9596 17.4125 16.014C17.3994 16.0356 17.3869 16.0576 17.375 16.0801C16.9237 16.9329 16.2535 17.6577 15.4259 18.1757C14.3159 18.8702 12.9951 19.1473 11.6997 18.9573C10.4042 18.7673 9.21861 18.1227 8.35485 17.1387C7.49109 16.1547 7.00554 14.8955 6.98503 13.5864C6.96452 12.2772 7.41039 11.0035 8.24291 9.99293C9.07542 8.98238 10.2403 8.30093 11.5291 8.07047C12.818 7.84001 14.1468 8.07556 15.278 8.73499C15.4839 8.85508 15.6809 8.9878 15.868 9.13202L13.8536 11.1464C13.5386 11.4614 13.7617 12 14.2071 12H20V6.20711Z"></path>
                                                        </svg>
                                                      </button>
                                                    </div>
                                                  </div>
                                                </div>
                                              `;
                                        
                                              refrenceElement.insertAdjacentElement(position, quoteContainer);
                                              insertQuoteText();
                                        
                                              document.getElementById("attributionLink").addEventListener("click", () => {
                                                chrome.tabs.create({ url: "https://zenquotes.io/" });
                                              });
                                              document.getElementById("refreshQuote").addEventListener("click", insertQuoteText);
                                            }
                                        
                                            injectStyle();
                                        
                                            // only reliable way to detect new tabs including new windows with a single startpage tab
                                            vivaldi.tabsPrivate.onTabUpdated.addListener(addQuoteStructureToPage);
                                        
                                            // catches all redrawings of the startpage including theme changes and switching back to a tab
                                            const appendChild = Element.prototype.appendChild;
                                            Element.prototype.appendChild = function () {
                                              if (arguments[0].tagName === "DIV") {
                                                setTimeout(
                                                  function () {
                                                    if (this.classList.contains("startpage")) {
                                                      addQuoteStructureToPage();
                                                    }
                                                  }.bind(this, arguments[0])
                                                );
                                              }
                                              return appendChild.apply(this, arguments);
                                            };
                                          }
                                        
                                          let intervalID = setInterval(() => {
                                            const browser = document.getElementById("browser");
                                            if (browser) {
                                              clearInterval(intervalID);
                                              quotesToSpeeddial();
                                            }
                                          }, 100);
                                        })();
                                        

                                        Quote.css

                                        .quoteBottomRow > p {
                                            opacity: 0 !important;
                                        }
                                        
                                        nomadic
                                        N
                                        1 Reply Last reply
                                        Reply Quote 2
                                        • nomadic
                                          N
                                          nomadic Soprano @smerugu28
                                          last edited by

                                          @smerugu28 I will try to look at it soon. Lots of driving for me in the next few days.

                                          1 Reply Last reply Reply Quote 3
                                          • stardepp
                                            S
                                            stardepp Translator Ambassador @nomadic
                                            last edited by

                                            @nomadic If you use the Vivaldi calendar, the quotes are not displayed on the start page, but in the calendar tab.

                                            766907f8-1baa-441e-9b02-eef7159f3c90-image.png

                                            ๐Ÿ€ Work spaces changed my work flow๐Ÿ€Search Engine Collection๐Ÿ€My Themes๐Ÿ€Windows11 24H2๐Ÿ€Motorola Edge 60 Fusion * Android 15๐Ÿ€

                                            smerugu28
                                            S
                                            nomadic
                                            N
                                            2 Replies Last reply
                                            Reply Quote 2
                                            Loading More Posts
                                            • Oldest to Newest
                                            • Newest to Oldest
                                            • Most Votes
                                            Reply
                                            • Reply as topic
                                            Log in to reply
                                            • 1
                                            • 2
                                            • 3
                                            • 1 / 3
                                            • First post
                                              Last post

                                            Looks like your connection to Vivaldi Forum was lost, please wait while we try to reconnect.

                                            Copyright © Vivaldi Technologies™ โ€” All rights reserved. Privacy Policy | Code of conduct | Terms of use | Vivaldi Status