Gelbooru Visited and Type Highlighter

Marks previously visited images on Gelbooru search pages, and extends animation highlighting from webm to also include gifs.

Verze ze dne 02. 11. 2020. Zobrazit nejnovější verzi.

// ==UserScript==
// @name         Gelbooru Visited and Type Highlighter
// @namespace    http://tampermonkey.net/
// @version      11.1.0
// @description  Marks previously visited images on Gelbooru search pages, and extends animation highlighting from webm to also include gifs.
// @author       Xerodusk
// @homepage     https://greasyfork.org/en/users/460331-xerodusk
// @include      https://gelbooru.com/index.php*page=post*s=list*
// @include      https://gelbooru.com/index.php*page=post*s=view*
// @include      https://gelbooru.com/index.php*page=pool*s=show*
// @include      https://gelbooru.com/index.php*page=favorites*s=view*
// @include      https://gelbooru.com/index.php*page=tags*s=saved_search*
// @include      https://gelbooru.com/index.php*page=wiki*s=view*
// @include      https://gelbooru.com/index.php*page=account*s=profile*
// @grant        none
// @icon         https://gelbooru.com/favicon.png
// ==/UserScript==
/* jshint esversion: 6 */

/*   configuration   */

// Highlight colors
// Values can be hexadecimal, rgb, rgba, hsl, hsla, color name, or whatever CSS color definitions your browser supports
const imgUnvisitedColor = '#E1F5FE'; // Color for unvisted images
const imgVisitedColor = '#2E7D32'; // Color for visited images
const webmUnvisitedColor = '#1565C0'; // Color for unvisted WebMs
const webmVisitedColor = '#C62828'; // Color for visited WebMs
const gifUnvisitedColor = '#FFD600'; // Color for unvisited animated gifs/pngs
const gifVisitedColor = '#6A1B9A'; // Color for visited animated gifs/pngs

// Whether to display visited/unvisited highlighting for your own favorites
// If false: Will only show visited/unvisited on other users' favorites pages
//           Animated GIF/WebM type highlighting will always be shown on all favorites
// If true:  Will also show visited/unvisited on your own favorites page
const displayCurrentUserFavoritesVisited = false;

/*-------------------*/

// Tests whether value is in items
function inSortedList(items, value) {
    'use strict';

    function binarySearch(array, value, first, last) {
        if (first > last) {
            return false;
        }
        const middle = (last + first) >> 1;
        if (array[middle] === value) {
            return true;
        }
        if (array[middle] > value) {
            return binarySearch(array, value, first, middle - 1);
        } else {
            return binarySearch(array, value, middle + 1, last);
        }
    }
    return binarySearch(items, value, 0, items.length - 1);
}

// Inserts value in items if not already present, returns whether insertion took place
function insertIntoSortedList(items, value) {
    'use strict';

    let first = 0,
        last = items.length - 1,
        middle;
    while (first <= last) {
        middle = (last + first) >> 1;
        if (items[middle] > value) {
            last = middle - 1;
            continue;
        }
        first = middle + 1;
        if (items[middle] === value) {
            return false;
        }
    }
    items.splice(first, 0, value);
    return true;
}

// Check if link is in visited list
function markIfVisited(galleryLink, visitedIDs) {
    'use strict';

    const linkURL = new URL(galleryLink.getAttribute('href'), window.location.href);
    const linkSearchParams = new URLSearchParams(linkURL.search);
    const id = parseInt(linkSearchParams.get('id'));

    if (inSortedList(visitedIDs, id)) {
        galleryLink.classList.add('visited');
    }
}

// Checks all provided links and marks visited if in list
function markVisitedLinks(galleryLinks) {
    'use strict';

    const links = galleryLinks;

    function applyVisitedToAllLinksInList() {
        const visitedIDs = JSON.parse(localStorage.getItem('visitedIDs')) || [];
        links.forEach(link => markIfVisited(link, visitedIDs));
    }

    applyVisitedToAllLinksInList();
    // Also mark visited images opened in new tab/windows from this page, or by any other means while this page is open
    window.addEventListener('storage', applyVisitedToAllLinksInList);
}

// Get cookie by name
// From https://www.w3schools.com/js/js_cookies.asp
function getCookie(cname) {
    'use strict';

    const name = cname + "=";
    const decodedCookie = decodeURIComponent(document.cookie);
    const ca = decodedCookie.split(';');
    for (let i = 0; i < ca.length; i++) {
        let c = ca[i];
        while (c.charAt(0) == ' ') {
            c = c.substring(1);
        }
        if (c.indexOf(name) == 0) {
            return c.substring(name.length, c.length);
        }
    }
    return "";
}

// Get current user's user ID, if exists
function getUserID() {
    'use strict';

    // Get user ID from cookie
    const userID = getCookie('user_id');

    return userID ? parseInt(userID) : -1;
}

// Create interface for history backups
function createBackupInterface() {
    'use strict';

    // Get header
    const header = document.getElementById('navbar') || document.querySelector('.header .center');
    if (!header) {
        return;
    }

    // Create element for header
    const headerWrapper = document.createElement('ul');
    headerWrapper.classList.add('flat-list');
    headerWrapper.classList.add('navbar-nav');
    headerWrapper.classList.add('nav');
    headerWrapper.style = 'float: right';

    // Create button
    const openDialogButtonContainer = document.createElement('li');
    const openDialogButton = document.createElement('a');
    openDialogButton.textContent = 'Visited History Backups';
    openDialogButton.setAttribute('role', 'button');
    openDialogButton.href = 'javascript:void(0)';
    openDialogButton.onclick = () => {
        let visitedIDs = localStorage.getItem('visitedIDs') || '[]';
        visitedIDs = visitedIDs.slice(0, visitedIDs.length - 1).slice(1);
        const textArea = document.getElementById('dialog-data-field');
        textArea.value = visitedIDs;
        textArea.select();
        document.getElementById('backup-dialog').classList.add('open');
    };
    openDialogButtonContainer.appendChild(openDialogButton);
    headerWrapper.appendChild(openDialogButtonContainer);

    // Create dialog
    const dialog = document.createElement('div');
    dialog.id = 'backup-dialog';
    const dialogHeader = document.createElement('h2');
    dialogHeader.id = 'dialog-header';
    dialogHeader.textContent = 'Back Up Visited Image History';
    const dialogText = document.createElement('label');
    dialogText.id = 'dialog-text';
    dialogText.setAttribute('for', 'dialog-data-field');
    dialogText.textContent = 'Copy the content of the text field and save it somewhere. To import a backup, paste in the text field and click "Import" to overwrite the current history or "Merge" to combine them.';
    const dialogDataField = document.createElement('textarea');
    dialogDataField.id = 'dialog-data-field';
    dialogDataField.setAttribute('autocomplete', 'off');
    dialogDataField.setAttribute('name', 'dialog-data-field');
    dialogDataField.setAttribute('rows', '3');

    // Create the buttons
    const dialogButtons = document.createElement('div');
    dialogButtons.id = 'dialog-buttons';
    const copyButton = document.createElement('button');
    const importButton = document.createElement('button');
    const mergeButton = document.createElement('button');
    const closeButton = document.createElement('button');
    copyButton.id = 'dialog-copy-button';
    copyButton.textContent = 'Copy to Clipboard';
    copyButton.onclick = async () => {
        const backupText = document.getElementById('dialog-data-field').value;
        try {
            await navigator.clipboard.writeText(backupText);
        } catch (e) {
            console.error('Failed to copy', e);
        }
    };
    importButton.id = 'dialog-import-button';
    importButton.textContent = 'Import';
    importButton.onclick = () => {
        const textareaContents = document.getElementById('dialog-data-field').value;
        if (!(/(^$)|(^[0-9]+(,[0-9]+)*$)/.test(textareaContents))) {
            document.getElementById('dialog-data-field').value = 'Invalid input';
            return false;
        }
        const importedIDs = JSON.parse('[' + textareaContents + ']');
        importedIDs.sort((a, b) => a - b);
        localStorage.setItem('visitedIDs', JSON.stringify(importedIDs));
        document.getElementById('backup-dialog').classList.remove('open');
    };
    mergeButton.id = 'dialog-merge-button';
    mergeButton.textContent = 'Merge';
    mergeButton.onclick = () => {
        const textareaContents = document.getElementById('dialog-data-field').value;
        if (!(/(^$)|(^[0-9]+(,[0-9]+)*$)/.test(textareaContents))) {
            document.getElementById('dialog-data-field').value = 'Invalid input';
            return false;
        }
        const importedIDs = JSON.parse('[' + textareaContents + ']');
        const visitedIDs = JSON.parse(localStorage.getItem('visitedIDs')) || [];
        const combinedIDs = [...importedIDs, ...visitedIDs];
        const mergedIDs = [...new Set(combinedIDs)];
        mergedIDs.sort((a, b) => a - b);
        localStorage.setItem('visitedIDs', JSON.stringify(mergedIDs));
        document.getElementById('backup-dialog').classList.remove('open');
    };
    closeButton.id = 'dialog-close-button';
    closeButton.textContent = 'Close';
    closeButton.onclick = () => {
        document.getElementById('backup-dialog').classList.remove('open');
    };
    if (!!navigator.clipboard) {
        dialogButtons.appendChild(copyButton);
    }
    dialogButtons.appendChild(importButton);
    dialogButtons.appendChild(mergeButton);
    dialogButtons.appendChild(closeButton);

    dialog.appendChild(dialogHeader);
    dialog.appendChild(dialogText);
    dialog.appendChild(dialogDataField);
    dialog.appendChild(dialogButtons);

    // Style everything
    const css = document.createElement('style');
    css.innerHTML = `
        #backup-dialog {
            position: fixed;
            top: 0;
            right: -400px;
            background-color: white;
            box-shadow: 0 2px 2px 0 rgba(0,0,0,0.14), 0 3px 1px -2px rgba(0,0,0,0.12), 0 1px 5px 0 rgba(0,0,0,0.2);
            width: 400px;
            max-width: 90vw;
            max-height: 90vh;
            padding: 12px;
            font-size: 12px;
            line-height: 1.42857143;
            box-sizing: border-box;
            transition: right 0.2s cubic-bezier(0,0,0.3,1);
        }
        #backup-dialog.open {
            right: 0;
            transition: right 0.25s cubic-bezier(0,0,0.3,1);
        }
        #backup-dialog * {
            box-sizing: border-box;
            font-family: verdana, sans-serif;
            line-height: inherit;
        }
        #dialog-header {
            all: revert;
        }
        #dialog-text {
            display: inline-block;
            max-width: 100%;
            margin-bottom: 5px;
            white-space: unset;
        }
        #dialog-data-field {
            width: 100%;
            resize: vertical;
            font-size: inherit;
            padding: revert;
            display: revert;
        }
        #dialog-data-field:focus {
            background-color: unset;
        }
        #dialog-buttons button {
            margin-right: 6px;
            cursor: pointer;
            font-size: inherit;
        }
    `;
    document.head.appendChild(css);

    // Attach button to header
    header.appendChild(headerWrapper);
    // Attach dialog to page
    document.body.appendChild(dialog);
}

(function() {
    'use strict';

    // Find out what kind of page we're on
    const searchParams = new URLSearchParams(window.location.search);

    if (!searchParams.has('page') || !searchParams.has('s')) {
        return false;
    }

    const page = searchParams.get('page');
    const s = searchParams.get('s');

    if (page === 'post') {
        if (s === 'view') { // Image page
            // Get id of current image
            const url = new URL(window.location);
            const currentURLSearchParams = new URLSearchParams(url.search);
            const id = parseInt(currentURLSearchParams.get('id'));

            // Add to list of visited images
            function updateVisitedIDs(event) {
                const visitedIDs = JSON.parse(localStorage.getItem('visitedIDs')) || [];
                if (insertIntoSortedList(visitedIDs, id)) {
                    localStorage.setItem('visitedIDs', JSON.stringify(visitedIDs));
                } else {
                    window.removeEventListener('storage', updateVisitedIDs);
                }
            }
            window.addEventListener('storage', updateVisitedIDs); // Update changes if another image being loaded in a different window changes the list before this one
            updateVisitedIDs();

            // "More Like This" results, currently in beta, could break, but this code should hypothetically never break the page from this end
            // Unfortunately, type highlighting is impossible with their current implementation, but visited highlighting is still possible
            const mltContainer = document.getElementsByClassName('contain-push')[0];
            // Get all image thumbnail links
            const mltLinks = mltContainer.querySelectorAll('#right-col > div > div > a');
            if (!!mltLinks) {
                markVisitedLinks(mltLinks);
            }

            const css = document.createElement('style');
            css.innerHTML = `
                a img.mltThumbs {
                    padding: 5px;
                    margin: 5px !important;
                    outline: 3px solid ` + imgUnvisitedColor + `;
                    background-color: #FFFFFF;
                }
                a:visited img.mltThumbs,
                a.visited img.mltThumbs {
                    outline-color: ` + imgVisitedColor + `;
                }
                a:not(.visited):visited img.mltThumbs {
                    background-color: #9E9E9E;
                }
            `;
            document.head.appendChild(css);
        } else if (s === 'list') { // Search page
            // Get search results area
            const galleryContainer = document.querySelector('.thumbnail-container');
            if (!!galleryContainer) {
                // Get all image thumbnail links
                const galleryLinks = galleryContainer.querySelectorAll('div.thumbnail-preview a');
                if (!!galleryLinks) {
                    markVisitedLinks(galleryLinks);
                }
            }

            // Apply borders
            const css = document.createElement('style');
            css.innerHTML = `
                div.thumbnail-preview {
                    background-color: transparent;
                }
                div.thumbnail-preview a img.thumbnail-preview {
                    background-color: #FFFFFF;
                }
                div.thumbnail-preview a:not(.visited):visited img.thumbnail-preview {
                    background-color: #9E9E9E;
                }
                div.thumbnail-preview a img.thumbnail-preview:not(.webm) {
                    outline: 3px solid ` + imgUnvisitedColor + `;
                }
                div.thumbnail-preview a:visited img.thumbnail-preview,
                div.thumbnail-preview a.visited img.thumbnail-preview {
                    outline-color: ` + imgVisitedColor + `;
                }
                div.thumbnail-preview a img.thumbnail-preview.webm {
                    border-color: ` + webmUnvisitedColor + ` !important;
                }
                div.thumbnail-preview a:visited img.thumbnail-preview.webm,
                div.thumbnail-preview a.visited img.thumbnail-preview.webm {
                    border-color: ` + webmVisitedColor + ` !important;
                }
                div.thumbnail-preview a img.thumbnail-preview[title*="animated_gif"],
                div.thumbnail-preview a img.thumbnail-preview[title*="animated_png"],
                div.thumbnail-preview a img.thumbnail-preview[title*="animated "]:not(.webm) {
                    outline-color: ` + gifUnvisitedColor + `;
                }
                div.thumbnail-preview a:visited img.thumbnail-preview[title*="animated_gif"],
                div.thumbnail-preview a:visited img.thumbnail-preview[title*="animated_png"],
                div.thumbnail-preview a:visited img.thumbnail-preview[title*="animated "]:not(.webm),
                div.thumbnail-preview a.visited img.thumbnail-preview[title*="animated_gif"],
                div.thumbnail-preview a.visited img.thumbnail-preview[title*="animated_png"],
                div.thumbnail-preview a.visited img.thumbnail-preview[title*="animated "]:not(.webm) {
                    outline-color: ` + gifVisitedColor + `;
                }
                div.thumbnail-preview a:focus img.thumbnail-preview:not(.webm) {
                    outline-color: #FFA726 !important;
                }
                div.thumbnail-preview a:focus img.thumbnail-preview.webm {
                    border-color: #FFA726 !important;
                }
            `;
            document.head.appendChild(css);
        }
    } else if (page === 'pool' && s === 'show') { // Pool page
        // Get image thumbnails area
        const galleryContainer = document.querySelector('.thumbnail-container');
        if (!!galleryContainer) {
            // Get all image thumbnail links
            const galleryLinks = galleryContainer.querySelectorAll('span a');
            if (!!galleryLinks) {
                markVisitedLinks(galleryLinks);
            }
        }
        // Apply borders
        const css = document.createElement('style');
        css.innerHTML = `
            div.thumbnail-container a img {
                outline: 3px solid ` + imgUnvisitedColor + `;
                background-color: #FFFFFF;
            }
            div.thumbnail-container a:visited img,
            div.thumbnail-container a.visited img {
                outline-color: ` + imgVisitedColor + `;
            }
            div.thumbnail-container a:not(.visited):visited img {
                background-color: #9E9E9E;
            }
            div.thumbnail-container a img[title*=" webm "] {
                outline: 5px solid ` + webmUnvisitedColor + ` !important;
            }
            div.thumbnail-container a:visited img[title*=" webm "],
            div.thumbnail-container a.visited img[title*=" webm "] {
                outline-color: ` + webmVisitedColor + ` !important;
            }
            div.thumbnail-container a img[title*="animated_gif"],
            div.thumbnail-container a img[title*="animated_png"],
            div.thumbnail-container a img[title*="animated "]:not([title*=" webm "]) {
                outline-color: ` + gifUnvisitedColor + `;
            }
            div.thumbnail-container a:visited img[title*="animated_gif"],
            div.thumbnail-container a:visited img[title*="animated_png"],
            div.thumbnail-container a:visited img[title*="animated "]:not([title*=" webm "]),
            div.thumbnail-container a.visited img[title*="animated_gif"],
            div.thumbnail-container a.visited img[title*="animated_png"],
            div.thumbnail-container a.visited img[title*="animated "]:not([title*=" webm "]) {
                outline-color: ` + gifVisitedColor + `;
            }
            div.thumbnail-container a:focus img {
                outline-color: #FFA726 !important;
            }
        `;
        document.head.appendChild(css);
    } else if (page === 'favorites' && s === 'view') { // Favorites page
        // Apply borders
        const css = document.createElement('style');
        css.innerHTML = `
            .thumb {
                margin: 5px;
            }
            .thumb a img {
                padding: 5px;
                margin: 5px;
            }
            .thumb a img[title*="animated_gif"],
            .thumb a img[title*="animated_png"],
            .thumb a img[title*="animated "]:not([title*=" webm"]) {
                outline: 3px solid ` + gifUnvisitedColor + `;
            }
            .thumb a img[title*=" webm"] {
                outline: 5px solid ` + webmUnvisitedColor + `;
            }
        `;

        const userID = displayCurrentUserFavoritesVisited ? -1 : getUserID();
        if (searchParams.has('id') && parseInt(searchParams.get('id')) != userID) {
            // Get list of visited images
            const visitedIDs = JSON.parse(localStorage.getItem('visitedIDs')) || [];
            // Mark visited links
            if (visitedIDs.length > 0) {
                const galleryLinks = document.querySelectorAll('.thumb a[href*="page=post"]');
                markVisitedLinks(galleryLinks);
            }
            css.innerHTML += `
                .thumb a img {
                    outline: 3px solid ` + imgUnvisitedColor + `;
                    background-color: #FFFFFF;
                }
                .thumb a:visited img,
                .thumb a.visited img {
                    outline-color: ` + imgVisitedColor + `;
                }
                .thumb a:not(.visited):visited img {
                    background-color: #9E9E9E;
                }
                .thumb a:visited img[title*=" webm"],
                .thumb a.visited img[title*=" webm"] {
                    outline-color: ` + webmVisitedColor + `;
                }
                .thumb a:visited img[title*="animated_gif"],
                .thumb a:visited img[title*="animated_png"],
                .thumb a:visited img[title*="animated "]:not([title*=" webm"]),
                .thumb a.visited img[title*="animated_gif"],
                .thumb a.visited img[title*="animated_png"],
                .thumb a.visited img[title*="animated "]:not([title*=" webm"]) {
                    outline-color: ` + gifVisitedColor + `;
                }
            `;
        }
        document.head.appendChild(css);
    } else if (page === 'tags' && s === 'saved_search') { // Saved Searches page
        /// Mark visited links
        const galleryLinks = document.querySelectorAll('.container-fluid > .thumb a');
        markVisitedLinks(galleryLinks);

        // Apply borders
        const css = document.createElement('style');
        css.innerHTML = `
            .container-fluid > .thumb a .thumbnail-preview {
                outline: 3px solid ` + imgUnvisitedColor + `;
                background-color: #FFFFFF;
            }
            .container-fluid > .thumb a:visited .thumbnail-preview,
            .container-fluid > .thumb a.visited .thumbnail-preview {
                outline-color: ` + imgVisitedColor + `;
            }
            .container-fluid > .thumb a:not(.visited):visited .thumbnail-preview {
                background-color: #9E9E9E;
            }
            .container-fluid > .thumb a .thumbnail-preview[alt*="animated_gif"],
            .container-fluid > .thumb a .thumbnail-preview[alt*="animated_png"],
            .container-fluid > .thumb a .thumbnail-preview[alt*="animated "]:not([alt*=" webm"]) {
                outline: 3px solid ` + gifUnvisitedColor + `;
            }
            .container-fluid > .thumb a .thumbnail-preview[alt*=" webm"] {
                outline: 5px solid ` + webmUnvisitedColor + `;
                margin: 5px 7px;
            }
            .container-fluid > .thumb a:visited .thumbnail-preview[alt*=" webm"],
            .container-fluid > .thumb a.visited .thumbnail-preview[alt*=" webm"] {
                outline-color: ` + webmVisitedColor + `;
            }
            .container-fluid > .thumb a:visited .thumbnail-preview[alt*="animated_gif"],
            .container-fluid > .thumb a:visited .thumbnail-preview[alt*="animated_png"],
            .container-fluid > .thumb a:visited .thumbnail-preview[alt*="animated "]:not([alt*=" webm"]),
            .container-fluid > .thumb a.visited .thumbnail-preview[alt*="animated_gif"],
            .container-fluid > .thumb a.visited .thumbnail-preview[alt*="animated_png"],
            .container-fluid > .thumb a.visited .thumbnail-preview[alt*="animated "]:not([alt*=" webm"]) {
                outline-color: ` + gifVisitedColor + `;
            }
        `;
        document.head.appendChild(css);
    } else if (page === 'wiki' && s === 'view') { // Wiki entry page
        // Mark visited links
        const galleryLinks = document.querySelectorAll('tr > td:nth-child(2) a[href*="s=view"]');
        markVisitedLinks(galleryLinks);

        // Apply borders
        const css = document.createElement('style');
        css.innerHTML = `
            a .thumbnail-preview img {
                padding: 5px;
                outline: 3px solid ` + imgUnvisitedColor + `;
                background-color: #FFFFFF;
            }
            a:visited .thumbnail-preview img,
            a.visited .thumbnail-preview img {
                outline-color: ` + imgVisitedColor + `;
            }
            a:not(.visited):visited .thumbnail-preview img {
                background-color: #9E9E9E;
            }
            a .thumbnail-preview img[alt*="animated_gif"],
            a .thumbnail-preview img[alt*="animated_png"],
            a .thumbnail-preview img[alt*="animated "]:not([alt*=" webm"]) {
                outline: 3px solid ` + gifUnvisitedColor + `;
            }
            a .thumbnail-preview img[alt*=" webm"] {
                outline: 5px solid ` + webmUnvisitedColor + `;
                margin: 5px 7px;
            }
            a:visited .thumbnail-preview img[alt*=" webm"],
            a.visited .thumbnail-preview img[alt*=" webm"] {
                outline-color: ` + webmVisitedColor + `;
            }
            a:visited .thumbnail-preview img[alt*="animated_gif"],
            a:visited .thumbnail-preview img[alt*="animated_png"],
            a:visited .thumbnail-preview img[alt*="animated "]:not([alt*=" webm"]),
            a.visited .thumbnail-preview img[alt*="animated_gif"],
            a.visited .thumbnail-preview img[alt*="animated_png"],
            a.visited .thumbnail-preview img[alt*="animated "]:not([alt*=" webm"]) {
                outline-color: ` + gifVisitedColor + `;
            }
        `;
        document.head.appendChild(css);
    } else if (page === 'account' && s === 'profile') { // Profile page
        // Mark visited links
        const galleryLinks = document.querySelectorAll('a[href*="s=view"]');
        markVisitedLinks(galleryLinks);

        // Apply borders
        const css = document.createElement('style');
        css.innerHTML = `
            .profileThumbnailPadding {
                max-width: none !important;
            }
            #statistics > span:last-child {
                display: none;
            }
            a[href*="s=view"] img {
                padding: 5px;
                outline: 3px solid ` + imgUnvisitedColor + `;
                max-height: 190px;
                object-fit: scale-down;
                background-color: #FFFFFF;
            }
            a[href*="s=view"]:visited img,
            a[href*="s=view"].visited img {
                outline-color: ` + imgVisitedColor + `;
            }
            a[href*="s=view"]:not(.visited):visited img {
                background-color: #9E9E9E;
            }
            a[href*="s=view"] img[alt*="animated_gif"],
            a[href*="s=view"] img[alt*="animated_png"],
            a[href*="s=view"] img[alt*="animated "]:not([alt*=" webm"]) {
                outline: 3px solid ` + gifUnvisitedColor + `;
            }
            a[href*="s=view"] img[alt*=" webm"] {
                outline: 5px solid ` + webmUnvisitedColor + `;
                margin: 5px 7px;
            }
            a[href*="s=view"]:visited img[alt*=" webm"],
            a[href*="s=view"].visited img[alt*=" webm"] {
                outline-color: ` + webmVisitedColor + `;
            }
            a[href*="s=view"]:visited img[alt*="animated_gif"],
            a[href*="s=view"]:visited img[alt*="animated_png"],
            a[href*="s=view"]:visited img[alt*="animated "]:not([alt*=" webm"]),
            a[href*="s=view"].visited img[alt*="animated_gif"],
            a[href*="s=view"].visited img[alt*="animated_png"],
            a[href*="s=view"].visited img[alt*="animated "]:not([alt*=" webm"]) {
                outline-color: ` + gifVisitedColor + `;
            }
        `;
        document.head.appendChild(css);
    }

    createBackupInterface();
})();