Tumblr Old Favicon: 2014

Replaces the current Tumblr favicon with the one from 2014.

// ==UserScript==
// @name         Tumblr Old Favicon: 2014
// @namespace    https://greasyfork.org/en/scripts/538227-tumblr-old-favicon-2014
// @version      1.0.1
// @description  Replaces the current Tumblr favicon with the one from 2014.
// @author       Valerie moon
// @match        https://www.tumblr.com/*
// @grant        none
// @icon         https://web.archive.org/web/20140723164326im_/http://assets.tumblr.com/images/favicons/favicon.ico?_v=0
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    const oldFaviconUrl = "https://web.archive.org/web/20140723164326im_/http://assets.tumblr.com/images/favicons/favicon.ico?_v=0";
    let lastKnownFaviconElement = null;

    function changeFavicon() {
        let head = document.head;
        if (!head) {
            setTimeout(changeFavicon, 100);
            return;
        }

        let link = document.querySelector("link[rel~='icon'], link[rel='shortcut icon']");

        if (!link) {
            link = document.createElement('link');
            link.rel = 'icon';
            head.appendChild(link);
            lastKnownFaviconElement = link;
        } else {
            lastKnownFaviconElement = link;
        }

        if (link.href !== oldFaviconUrl) {
            link.href = oldFaviconUrl;
            link.type = 'image/x-icon';
        } else if (link.type !== 'image/x-icon') {
            link.type = 'image/x-icon';
        }
    }

    if (document.head) {
        changeFavicon();
    } else {
        const initialObserver = new MutationObserver((mutations, obs) => {
            if (document.head) {
                obs.disconnect();
                changeFavicon();
                startMainObservers();
            }
        });
        initialObserver.observe(document.documentElement, { childList: true });
    }

    function startMainObservers() {
        if (!document.head) {
            return;
        }

        const headObserver = new MutationObserver(function(mutationsList) {
            let needsUpdate = false;
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    let currentFavicon = document.querySelector("link[rel~='icon'], link[rel='shortcut icon']");
                    if (!currentFavicon || (currentFavicon === lastKnownFaviconElement && currentFavicon.href !== oldFaviconUrl) || (currentFavicon !== lastKnownFaviconElement)) {
                        needsUpdate = true;
                        break;
                    }
                } else if (mutation.type === 'attributes') {
                    if (mutation.target === lastKnownFaviconElement && (mutation.attributeName === 'href' || mutation.attributeName === 'rel')) {
                        if (lastKnownFaviconElement.href !== oldFaviconUrl) {
                            needsUpdate = true;
                            break;
                        }
                    } else if (mutation.target.matches("link[rel~='icon'], link[rel='shortcut icon']") && mutation.target !== lastKnownFaviconElement) {
                        needsUpdate = true;
                        break;
                    }
                }
            }

            if (needsUpdate) {
                setTimeout(changeFavicon, 50);
            } else {
                let currentFavicon = document.querySelector("link[rel~='icon'], link[rel='shortcut icon']");
                if (!currentFavicon || currentFavicon.href !== oldFaviconUrl) {
                    setTimeout(changeFavicon, 50);
                }
            }
        });

        headObserver.observe(document.head, {
            childList: true,
            subtree: true,
            attributes: true,
            attributeFilter: ['href', 'rel', 'type', 'sizes']
        });

        const titleElement = document.querySelector('head > title');
        if (titleElement) {
            const titleObserver = new MutationObserver(function() {
                setTimeout(changeFavicon, 50);
            });
            titleObserver.observe(titleElement, { childList: true });
        }
    }

    if (document.head) {
        startMainObservers();
    }

    function handleNavigationEvent() {
        setTimeout(changeFavicon, 150);
    }

    window.addEventListener('popstate', handleNavigationEvent);
    window.addEventListener('hashchange', handleNavigationEvent);

    const originalPushState = history.pushState;
    history.pushState = function(...args) {
        const result = originalPushState.apply(this, args);
        handleNavigationEvent();
        return result;
    };

    const originalReplaceState = history.replaceState;
    history.replaceState = function(...args) {
        const result = originalReplaceState.apply(this, args);
        handleNavigationEvent();
        return result;
    };
})();