Custom Title

【使用前先看介绍/有问题可反馈】自定义标题 (Custom Title): 支持自定义网页标题。

От 05.08.2022. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         Custom Title
// @namespace    http://tampermonkey.net/
// @version      1.0.0
// @description  【使用前先看介绍/有问题可反馈】自定义标题 (Custom Title): 支持自定义网页标题。
// @author       cc
// @include      *
// @license      MIT
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function() {
    'use strict';

    // storage
    let storage;
    const _VERSION_ = '1.0.0';
    const focusKey = 'KeyT';    // T
    const expiredTime = 30 * 86400 * 1000;  // 30 day
    const interval = 86400 * 1000;  // 1 day

    function notify(msg) {
        console.log(`%c${msg}`, 'background-color: yellow; color: black;');
    }

    function getDefaultStorage() {
        return {
            lastUpdateTime: new Date().getTime(),
            titleInfoDict: {},
        }
    }

    function loadStorage() {
        storage = GM_getValue('storage');
        if (!storage) {
            storage = getDefaultStorage();
            GM_setValue('storage', storage);
            notify('[Custom Title]: storage initialized');
            console.log(storage);
        } else {
            notify('[Custom Title]: storage down here');
            console.log(storage);
            clearExpiredTitle();
        }
    }

    function applyTitle() {
        let titleInfo = storage.titleInfoDict[location.href];
        if (titleInfo && document.title !== titleInfo.newTitle) {
            document.title = titleInfo.newTitle;
            notify('[Custom Title]: applied title');
        } else if (titleInfo) {
            notify('[Custom Title]: no need to replace title');
        } else {
            notify('[Custom Title]: no title can be appied');
        }
    }

    // functions
    function updateTitle() {
        let titleInfo = storage.titleInfoDict[location.href];
        let newTitle = prompt('请输入需要为此网页自定义的标题,可通过 \'Ctrl+Shift+T\' 或直接输入空以取消:', titleInfo ? titleInfo.newTitle : document.title);
        if (newTitle) {
            storage.titleInfoDict[location.href] = {
                oldTitle: titleInfo ? titleInfo.oldTitle : document.title,
                newTitle: newTitle,
                lastUpdateTime: new Date().getTime(),
            };
            document.title = newTitle;
            GM_setValue('storage', storage);
            notify('[Custom Title]: updated title');
        } else if (newTitle.constructor === String) {
            removeTitle();
        }
    }

    function removeTitle() {
        let titleInfo = storage.titleInfoDict[location.href];
        if (titleInfo) {
            let res = confirm('是否为此网页移除自定义标题?');
            if (res) {
                document.title = titleInfo.oldTitle;
                delete storage.titleInfoDict[location.href];
                GM_setValue('storage', storage);
                notify('[Custom Title]: removed title');
            }
        } else {
            alert('你还未为此网页设置自定义标题');
        }
    }

    function removeAllTitles() {
        let res = confirm('是否清除所有自定义标题?');
        if (res) {
            let titleInfo = storage.titleInfoDict[location.href];
            if (titleInfo) {
                document.title = titleInfo.oldTitle;
            }
            storage = getDefaultStorage();
            GM_setValue('storage', storage);
            alert('已清除所有自定义标题');
            notify('[Custom Title]: removed all titles');
        }
    }

    function clearExpiredTitle() {
        let currentTime = new Date().getTime();
        if (storage.lastUpdateTime + interval < currentTime) {
            let titleInfoDict = storage.titleInfoDict;
            for (let key of Object.keys(titleInfoDict)) {
                if (titleInfoDict[key].lastUpdateTime + expiredTime < currentTime) {
                    delete titleInfoDict[key];
                }
            }
            storage.titleInfoDict = titleInfoDict;
            storage.lastUpdateTime = currentTime;
            GM_setValue('storage', storage);
            notify('[Custom Title]: cleared expired titles');
        }
    }

    function bindObserver() {
        // key observer
        document.onkeydown = function(e) {
            console.log(e);
            if (e.ctrlKey && e.shiftKey && e.code == focusKey) {
                e.preventDefault();
                removeTitle();
                notify('[Custom Title]: trigger remove title');
            } else if (e.ctrlKey && e.metaKey && e.code == focusKey) {
                e.preventDefault();
                removeAllTitles();
            } else if (e.ctrlKey && e.code == focusKey) {
                // update title
                e.preventDefault();
                updateTitle();
                notify('[Custom Title]: trigger update title');
            }
        }
        // title observer
        let title = document.querySelector('title');
        let observer = new MutationObserver(function(mutations) {
            console.log(mutations);
            applyTitle();
            notify('[Custom Title]: trigger title change event');
        });
        let config = { subtree: true, characterData: true, childList: true };
        observer.observe(title, config);

        // hash observer
        window.onhashchange = function() {
            applyTitle();
            notify('[Custom Title]: trigger hash change event');
        };

        // call apply again
        applyTitle();
    }

    // main function
    document.onreadystatechange = function() {
        if (document.readyState === 'complete') {
            notify(`[Custom Title]: version ${_VERSION_}`);
            loadStorage();
            applyTitle();
            bindObserver();
        }
    }
})();