darkmode Fork

    @@ -18,8 +18,7 @@ // | - 画像の色は変わりません。文字や背景に画像が使われている場合、見づらくなるかもしれません。 // | - 稀に色を変えられないこともあります。仕様です。 // | - // | 1 : - - // | - 全ての要素の背景が黒になります。 + // | 1 : - 全ての要素の背景が黒になります。 // | - はじめに色が変わらなかったテキストでも色が変わる可能性があります。 // | - 訪問したことのあるリンクの色は、そうでないリンクの色と同じになります。 // | - 画像の色は変わりません。
  • /*
     * @title darkmode
     * @description サイトの背景色を黒にし、文字色を明るくします。詳しい説明はソースコード内の先頭のコメントを見てください。
     * @include http://*
     * @include https://*
     * @contributor noromanba   http://let.hatelabo.jp/noromanba/let/hJmc5cn7v_h5
     * @license     MIT license https://opensource.org/licenses/MIT
     * @javascript_url
     */
    
    //http://let.hatelabo.jp/nanikamado/let/hLHU5aii3YU5  の改良版です。
    
    // 実行する度に以下が切り替わります。
    // 0 -> 1 -> 2 -> 0 -> 1 -> 2 -> 0...
    //  | 0 : - 背景の色を黒にします。ただし、背景が透明の場合は変更しません。
    //  |     - 文字色の彩度を最大にします。
    //  |     - 訪問したことのあるリンクの色を紫にします。
    //  |     - 画像の色は変わりません。文字や背景に画像が使われている場合、見づらくなるかもしれません。
    //  |     - 稀に色を変えられないこともあります。仕様です。
    //  |     
    //  | 1 : - 全ての要素の背景が黒になります。
    //  |     - はじめに色が変わらなかったテキストでも色が変わる可能性があります。
    //  |     - 訪問したことのあるリンクの色は、そうでないリンクの色と同じになります。
    //  |     - 画像の色は変わりません。
    //  |     
    //  | 2 : - もとに戻ります。
    //  v
    
    
    (() => {
        'use strict';
        const set_color = (nodes, background) => {
            const imp = background === 'minimum' ? undefined : 'important';
            nodes.forEach((node) => {
                if (typeof node.getAttribute !== "function") return;
                if (node.getAttribute('dark_before_style') === null) node.setAttribute('dark_before_style', node.style.cssText);
                const computed_style = window.getComputedStyle(node);
                if (!computed_style.color) return;
                const [r, g, b, ] = computed_style.color.match(/\d+/g).slice(0, 3).map(Number);
                const s = 255 - Math.max(r, g, b);
                node.style.setProperty('color', `rgb(${r+s},${g+s},${b+s})`, imp);
                if (background === 'minimum' && computed_style.backgroundColor.indexOf('a') !== -1) {
                    if (computed_style.backgroundColor.indexOf('rgba(0, 0, 0, 0)') === -1)
                        node.style.setProperty('background-color', 'rgba(0, 0, 0,' +
                            computed_style.backgroundColor.replace(/[^,]*,[^,]+,[^,]+,[^,\d]*(\d?.?\d+)/, '$1'), 'important');
                } else {
                    node.style.setProperty('background-color', '#000', 'important');
                }
                if (computed_style.backgroundImage.indexOf('gradient') !== -1)
                    node.style.backgroundImage = computed_style.backgroundImage
                    .replace(/[^,]*gradient\((?:[^\(\)]*\((?:[^\(\)]*\([^\(\)]*\))*[^\(\)]*[^\(\)]*\))*[^\(\)]*\),?/, '') || 'none';
    
            });
        };
        const set_color_all = (background) => {
            set_color(document.body.querySelectorAll(':not(style):not(script)'), background);
            set_color([document.body, document.body.parentNode], background);
            document.body.querySelectorAll('iframe[src]').forEach(iframe => {
                if (!iframe.src) return;
                if ((new URL(iframe.src, location.href)).origin === location.origin || iframe.src === 'about:blank') {
                    set_color(iframe.contentDocument.body.querySelectorAll('*'), background);
                    let if_dc_b = iframe.contentDocument.body || console.error(iframe);
                    if_dc_b.style.backgroundColor = '#000';
                }
            });
            document.body.style.backgroundColor = '#000';
        };
    
        switch (document.body.getAttribute('dark_mode_state') || '0') {
            case '0':
                let observer = new MutationObserver((maa) => {
                    let imp;
                    switch (document.body.getAttribute('dark_mode_state')) {
                        case '0':
                            observer.disconnect();
                            return;
                        case '1':
                            imp = 'minimum';
                            break;
                        case '2':
                            imp = 'all';
                            break;
                    }
                    maa.forEach(ma => {
                        set_color(ma.addedNodes, imp);
                        ma.addedNodes.forEach(added_node => {
                            if (added_node.nodeName === '#text') return;
                            set_color(added_node.querySelectorAll('*'), imp);
                        });
                    });
                    console.log('reseted');
                });
    
                let resize_timer,
                    resize_listener;
                window.addEventListener('resize', resize_listener = () => {
                    console.log('resizing');
                    if (resize_timer) clearTimeout(resize_timer);
                    resize_timer = setTimeout(() => {
                        switch (document.body.getAttribute('dark_mode_state')) {
                            case '0':
                                window.removeEventListener('resize', resize_listener);
                                break;
                            case '1':
                                set_color_all('minimum');
                                break;
                            case '2':
                                set_color_all('all');
                                break;
                        }
                    }, 500);
                });
    
                let dark_style = document.createElement('style');
                dark_style.textContent = `a:visited {
                    color: #b553ff!important
                }
                *{
                    text-shadow:none!important;
                }
    
                ::before,::after{
                    background-color:transparent!important;
                }
                
                ::-webkit-scrollbar {
                    overflow: hidden;
                    width: .8rem;
                    background: #000;
                }
                
                ::-webkit-scrollbar-thumb {
                    overflow: hidden;
                    border-radius: .4rem;
                    background: #ddd;
                }`;
                dark_style.className = 'dark_visited_style';
                document.body.appendChild(dark_style);
    
                document.body.setAttribute('dark_mode_state', '1');
                set_color_all('minimum');
                observer.observe(document.body, {
                    childList: true,
                    subtree: true
                });
                console.log('0');
                break;
    
            case '1':
                document.body.setAttribute('dark_mode_state', '2');
                set_color_all('all');
                console.log('1');
                break;
    
            case '2':
                document.body.setAttribute('dark_mode_state', '0');
                document.querySelectorAll('*').forEach(node => {
                    if (!node.style) return;
                    const style = node.getAttribute('dark_before_style');
                    if (style === null) return;
                    node.style.cssText = style;
                });
                document.body.getElementsByClassName('dark_visited_style')[0].remove();
                console.log('2');
                break;
        }
    })();
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。