维基百科黑色主题

给予维基百科网页一个黑色主题

当前为 2019-09-07 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         Wikipedia Dark Theme
// @namespace    https://github.com/MaxsLi/WikipediaDarkTheme
// @version      0.90
// @icon         https://www.wikipedia.org/favicon.ico
// @description  Script gives wikipedia pages a dark color theme
// @author       Shangru Li
// @match        *://*.wikipedia.org/*
// @grant        none
// @run-at       document-start
// @license      MIT
//###############---localizations---##################
// @name:zh-CN          维基百科黑色主题
// @description:zh-CN   给予维基百科网页一个黑色主题
// @name:zh-TW          维基百科黑色主题
// @description:zh-TW   給予維基百科網頁壹個黑色主題
// @name:ja             Wikipedia ダークテーマ
// @description:ja      Wikipediaのサイトのバックグラウンドを黒に変更するスクリプトです
// ==/UserScript==

// The main function is called at every `onreadystatechange`
// Document state will go from `loading` --> `interactive` --> `complete`
// Metadata Block `@run-at document-start` will ensure the script start executing when `loading`
(document.onreadystatechange = function () {

    //##################---default_values---#####################################
    var default_contrastValue = 8;
    var default_foregroundColor = "rgb(238, 255, 255)";
    var default_backgroundColor = "rgb(35, 35, 35)";
    var default_backgroundColorRGB = splitToRGB(default_backgroundColor);
    //##################---one_could_alter_if_desired---#########################

    // list of tags of the wikipedia logos and symbols to be excluded from setting backgroundColor to white
    var exclude_src_tag = [
        "protection-shackle", "green_check", "symbol_support_vote",
        "edit-clear", "information_icon", "increase2", "decrease_positive",
        "steady2", "decrease2", "increase_negative", "red_question_mark",
        "blue_check", "x_mark", "yes_check", "twemoji", "walnut", "cscr-featured",
        "sound-openclipart", "folder_hexagonal_icon", "symbol_book_class2",
        "question_book-new", "wiktionary-logo", "commons-logo", "wikinews-logo",
        "wikiquote-logo", "wikivoyage-logo", "sound-icon", "wikibooks-logo",
        "wikiversity-logo", "ambox", "system-search", "split-arrows", "wikiversity_logo",
        "wikisource-logo", "wikimedia_community_logo", "wikidata-logo", "mediawiki-logo",
        "wikispecies-logo", "nuvola_apps", "white_flag_icon",
        "wiki_letter_w_cropped", "edit-copy_purple-wikiq", "acap", "portal-puzzle",
        "star_of_life", "disambig-dark", "gnome", "office-book", "audio-input-microphone",
        "hsutvald2", "hspolitic", "hsdagensdatum", "hsvissteduatt", "pl_wiki_aktualnosci_ikona",
        "hsbild", "wiki_aktualnosci_ikona", "hs_vdq", "hssamarbete", "hswpedia", "w-circle",
        "red_pencil_icon", "merge-arrow", "generic_with_pencil", "hsaktuell", "hsearth",
        "wikimedia-logo-circle", "wiktionary_ko_without_text", "mediawiki-notext",
        "wiktprintable_without_text", "dialog-information", "applications-office",
        "celestia", "antistub", "wiki_letter", "kit_body_basketball", "ui_icon_edit-ltr-progressive",
        "merge-split-transwiki", "mergefrom", "px-steady", "px-decrease", "px-increase",
        "question_book", "unbalanced_scales", "padlock-silver"
    ];

    // list of tags of images to have color inverted, both lists are subjected to amend
    var invert_src_tag = [
        "loudspeaker", "signature", "signatur", "chinese_characters", "/media/math/render/",
        "translation_to_english_arrow", "disambig_gray", "wikimedia-logo_black", "blue_pencil",
        "latin_alphabet_", "_cursiva"
    ];

    //##################---controller---#########################################

    if ('loading' == document.readyState)
        setPageVisibility("hidden");
    else if ('interactive' == document.readyState)
        setPage();
    else if ('complete' == document.readyState)
        setPageVisibility("visible");

    //##################---functions---#########################################

    function setPageVisibility(visibility) {
        var entirePage = document.getElementsByTagName('html')[0];
        entirePage.style.backgroundColor = default_backgroundColor;
        entirePage.style.visibility = visibility;
    }

    function setPage() {
        'use strict';
        // General idea is to put all elements on a wikipedia page to an array `allElements`
        // traverse through this array and reverse the color of each element accordingly
        // running time o(n), where n is the number of elements on a page
        var allElements = document.querySelectorAll('*');

        for (var i = 0; i < allElements.length; i++) {
            var currentElement = allElements[i];
            try {
                if (!isSpecialElement(currentElement)) {
                    changeForegroundColor(currentElement);
                    changeBackgroundColor(currentElement);
                }
            } catch (e) {
                console.log(e);
            }
        }
    }

    function isSpecialElement(e) {
        if (elementIsImage(e)) {
            changeImageIfOnLists(e);
            return true;
        } else if (elementIsWikiLogo(e)) {
            invertImage(e, 90);
            return true;
        } else if (elementIsKeyboardKey(e)) {
            e.style.foregroundColor = default_backgroundColor;
            return true;
        } else if (elementIsLegendOrPieCharts(e)) {
            return true;
        } else {
            return false;
        }
    }

    function elementIsImage(e) {
        if (e.nodeName.toLowerCase() == 'img')
            return true;
    }

    function changeImageIfOnLists(img) {
        if (!imageInExcludeList(img))
            img.style.backgroundColor = "rgb(255, 255, 255)";
        if (imageInInvertList(img))
            invertImage(img, 86);
    }

    function imageInExcludeList(img) {
        for (var i = 0; i < exclude_src_tag.length; i++)
            if (img.src.toLowerCase().includes(exclude_src_tag[i]))
                return true;
        return false;
    }

    function imageInInvertList(img) {
        for (var i = 0; i < invert_src_tag.length; i++)
            if (img.src.toLowerCase().includes(invert_src_tag[i]))
                return true;
        return false;
    }

    function invertImage(img, percent) {
        img.style.filter = "invert(" + percent + "%)";
    }

    function elementIsWikiLogo(e) {
        if (e.className.toLowerCase().includes("mw-wiki-logo"))
            return true;
    }

    function elementIsKeyboardKey(e) {
        if (e.className.toLowerCase().includes('keyboard-key'))
            return true;
    }

    function elementIsLegendOrPieCharts(e) {
        if (e.className.toLowerCase().includes('legend') ||
            e.className.toLowerCase().includes('border') ||
            e.style.borderColor.toLowerCase().includes('transparent') ||
            e.style.border.toLowerCase().includes("1px solid rgb(0, 0, 0)"))
            return true;
    }

    function changeForegroundColor(e) {
        var foregroundColor = window.getComputedStyle(e, null).getPropertyValue("color");
        if (colorIsRGB(foregroundColor)) {
            foregroundColor = splitToRGB(foregroundColor);
            foregroundColor = inverseRBGColor(foregroundColor);
            foregroundColor = increaseRGBToMatchContrastValue(foregroundColor, default_backgroundColorRGB, default_contrastValue, 30);
            e.style.color = RGBArrayToString(foregroundColor);
        } else {
            e.style.color = default_foregroundColor;
        }
    }

    function changeBackgroundColor(e) {
        var backgroundColor = window.getComputedStyle(e, null).getPropertyValue("background-color");
        if (colorIsRGB(backgroundColor)) {
            backgroundColor = splitToRGB(backgroundColor);
            backgroundColor = inverseRBGColor(backgroundColor);
            if (RGBTooDark(backgroundColor))
                backgroundColor = addValueToRGB(backgroundColor, 30);
            backgroundColor = decreaseRGBToMatchContrastValue(backgroundColor, default_backgroundColorRGB, default_contrastValue, -30);
            e.style.backgroundColor = RGBArrayToString(backgroundColor);
        } else {
            e.style.backgroundColor = default_backgroundColor;
        }
    }

    function colorIsRGB(c) {
        return c.match(/rgb\((\d{1,3}), (\d{1,3}), (\d{1,3})\)/);
    }

    function splitToRGB(c) {
        var rgb = colorIsRGB(c);
        return [rgb[1], rgb[2], rgb[3]];
    }

    function inverseRBGColor(c) {
        var r, g, b;
        r = 255 - c[0];
        g = 255 - c[1];
        b = 255 - c[2];
        return [r, g, b];
    }

    function increaseRGBToMatchContrastValue(colorToChange, colorToMatch, contrastValue, changePerLoop) {
        var result = colorToChange;
        while (contrast(result, colorToMatch) < contrastValue)
            result = addValueToRGB(result, changePerLoop);
        return result;
    }

    function decreaseRGBToMatchContrastValue(colorToChange, colorToMatch, contrastValue, changePerLoop) {
        var result = colorToChange;
        while (contrast(result, colorToMatch) > contrastValue)
            result = addValueToRGB(result, changePerLoop);
        return result;
    }

    function contrast(rgb1, rgb2) {
        return (luminance(rgb1) + 0.05) / (luminance(rgb2) + 0.05);
    }

    function luminance(rgb) {
        var result = rgb.map(function (v) {
            v /= 255;
            return v <= 0.03928 ? v / 12.92 : Math.pow((v + 0.055) / 1.055, 2.4);
        });
        return result[0] * 0.2126 + result[1] * 0.7152 + result[2] * 0.0722;
    }

    function addValueToRGB(rgb, value) {
        return [rgb[0] + value, rgb[1] + value, rgb[2] + value];
    }

    function RGBTooDark(rgb) {
        if (rgb[0] < default_backgroundColorRGB[0] - 10 && rgb[1] < default_backgroundColorRGB[1] - 10 &&
            rgb[2] < default_backgroundColorRGB[2] - 10)
            return true;
    }

    function RGBArrayToString(rgb) {
        return 'rgb(' + rgb[0] + ', ' + rgb[1] + ', ' + rgb[2] + ')';
    }
})();