darkmode Fork

  • /*
     * @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
     */
    
    
    // 背景の色を黒にします。ただし、背景が透明の場合は変更しません。
    // 文字色の彩度を最大にします。
    // 訪問したことのあるリンクの色を紫にします。
    // 稀に色を変えられないこともあります。仕様です。
    // 画像の色は変わりません。
    // 一度実行した状態でもう一度実行すると、もとに戻ります。
    // 'use strict'の次の行の、bgcolorに入れる値を変更することで、背景色を変えることができます。
    // http://let.hatelabo.jp/nanikamado/let/hLHU5aii3YU5  の改良版です。
    
    
    (() => {
        'use strict';
        let bgcolor = 'rgb(22,22,22)';
        let time = {
            all: Date.now()
        };
        const computedStyleMap = new WeakMap();
        const formatColorCode = bgcolor => {
                if (bgcolor.slice(0, 1) === '#')
                    return 'rgb(' +
                        (bgcolor.length === 4 ? bgcolor.slice(1, 4).split('').map(st => st + st) : bgcolor.slice(1, 7).match(/../g))
                        .map(_bgc => parseInt(_bgc, 16)).join(",") + ')';
                if (bgcolor.slice(0, 3) !== 'rgb') {
                    let _cDiv = document.createElement('div');
                    _cDiv.style.setProperty('color', bgcolor, 'important');
                    if (!_cDiv.style.color) { console.log('color code is invalid'); return };
                    document.body.appendChild(_cDiv);
                    let color = window.getComputedStyle(_cDiv).color;
                    _cDiv.remove();
                    return color;
                }
                console.error('bgcolor=' + bgcolor);
            },
            setColor = (node, ignoreDCStyle) => {
                let dCStyle;
                let nodeStyleText = node.style.cssText;
                if (!ignoreDCStyle && (dCStyle = node.getAttribute('darkComputedStyle'))) {
                    if (!~nodeStyleText.indexOf(dCStyle)) node.style.cssText = dCStyle;
                    return;
                };
                if (node.getAttribute('darkBeforeStyle') === null) node.setAttribute('darkBeforeStyle', nodeStyleText);
                let CS_C, CS_BC, CS_BI, CS;
                if (computedStyleMap.has(node)) {
                    let sCS = computedStyleMap.get(node);
                    CS_C = sCS.C;
                    CS_BC = sCS.BC;
                    CS_BI = sCS.BI;
                    CS = sCS.CS;
                } else {
                    CS = window.getComputedStyle(node);
                    CS_C = CS.color;
                    CS_BC = CS.backgroundColor;
                    CS_BI = CS.backgroundImage;
                };
                if (!CS_C) return;
                const [r, g, b] = CS_C.match(/\d+/g).slice(0, 3).map(Number);
                const s = 255 - Math.max(r, g, b);
                //color
                node.style.setProperty('color', `rgb(${r+s},${g+s},${b+s})`, node.tagName === 'A' ? '' : 'important');
                //backgroundColor
                if (CS_BC !== 'rgba(0, 0, 0, 0)') {
                    if (CS_BC.slice(3, 4) !== 'a') {
                        node.style.setProperty('background-color', bgcolor, 'important');
                    } else {
                        if (bgcolor.slice(0, 3) !== 'rgb') {
                            bgcolor = formatColorCode(bgcolor);
                        }
                        node.style.setProperty('background-color', bgcolor.replace('rgb', 'rgba').replace(')', ',') +
                            CS_BC.replace(/[^,]*,[^,]+,[^,]+,[^,\d]*(\d?.?\d+)/, '$1'), 'important');
                    }
                }
                //backgroundImage gradient
                if (CS_BI.indexOf('gradient') !== -1)
                    node.style.backgroundImage = CS_BI
                    .replace(/[^,]*gradient\((?:[^\(\)]*\((?:[^\(\)]*\([^\(\)]*\))*[^\(\)]*[^\(\)]*\))*[^\(\)]*\),?/gi, '') || 'none';
    
                node.setAttribute('darkComputedStyle', node.style.cssText.split(';').filter(cssplo => {
                    switch (cssplo.split(':')[0].replace(' ', '')) {
                        case 'background-color':
                        case 'color':
                            return true;
                        default:
                            return false;
                    }
                }).join(';'));
            },
            allElm = () => {
                let all = [document.body, document.body.parentNode, ...document.body.querySelectorAll('*')];
                document.body.querySelectorAll('iframe').forEach(iframe => {
                    if ((new URL(iframe.src, location.href)).origin === location.origin || !iframe.src || iframe.src === 'about:blank') {
                        all.push(...[
                            ...iframe.contentDocument.body.querySelectorAll('*'),
                            iframe.contentDocument.body
                        ]);
                    };
                });
                return all;
            },
            setColorAll = () => {
                allElm().forEach(setColor);
                document.body.style.setProperty('background-color', bgcolor, 'important');
                document.body.querySelectorAll('A').forEach(node => {
                    if (getComputedStyle(node).color !== node.style.color) node.style.setProperty('color', node.style.color, 'important');
                });
            },
            main = () => {
                let darkVisitedStyle = document.body.querySelector('.darkVisitedStyle');
                if (darkVisitedStyle) { //even times
                    allElm().forEach(node => {
                        if (!node.style) return;
                        const style = node.getAttribute('darkBeforeStyle');
                        if (style === null) return;
                        node.style.cssText = style;
                    });
                    darkVisitedStyle.remove();
                    console.log('1');
                } else { //odd times
                    allElm().forEach(node => {
                        const CS = window.getComputedStyle(node);
                        computedStyleMap.set(node, {
                            C: CS.color,
                            BC: CS.backgroundColor,
                            BI: CS.backgroundImage,
                            CS: CS
                        });
                    });
                    let elmsSetedStyle = [];
                    const observer = new MutationObserver(maa => {
                        if (!document.body.querySelector('.darkVisitedStyle')) {
                            observer.disconnect();
                            return;
                        };
                        maa.forEach(ma => {
                            if (ma.type === 'childList') {
                                ma.addedNodes.forEach(addedNode => {
                                    if (addedNode.nodeType !== 1) return;
                                    [addedNode, ...addedNode.querySelectorAll('*')].forEach(setColor);
                                });
                            } else {
                                const darkComputedStyle = ma.target.getAttribute('darkComputedStyle') || '';
    
                                if (~ma.target.style.cssText.indexOf(darkComputedStyle)) {
                                    console.log('return cssText===DCS');
                                    return
                                };
                                let DCStyleOb = {};
                                for (const styleProp of darkComputedStyle.split(';')) {
                                    const s = styleProp.split(':');
                                    DCStyleOb[s[0].replace(' ', '')] = s[1].replace('!important', '').replace(/ /g, '');
                                };
                                if ((!DCStyleOb['color'] || DCStyleOb['color'] === ma.target.style.color.replace(/ /g, '')) &&
                                    (!DCStyleOb['background-color'] || DCStyleOb['background-color'] === ma.target.style.backgroundColor.replace(/ /g, ''))) {
                                    console.log('return DCSColor===color&&DCSBackgroundColor===backgroundcolor');
                                    return
                                };
    
                                let ind;
                                if ((ind = elmsSetedStyle.map(e => e.elm).indexOf(ma.target)) >= 0) {
                                    if (++elmsSetedStyle[ind].num >= 10) {
                                        console.log({
                                            0: 'avoided infinite loop =>',
                                            num: elmsSetedStyle[ind].num,
                                            color: ma.target.style.color.replace(/ /g, ''),
                                            bgcolor: ma.target.style.backgroundColor.replace(/ /g, '')
                                        });
                                        return;
                                    }
                                } else elmsSetedStyle.push({
                                    elm: ma.target,
                                    num: 0
                                });
                                setColor(ma.target, true);
                            }
                        });
                        console.log('Mutation');
                    });
    
                    let resizeTimer;
                    const resizeListener = () => {
                        console.log('resizing');
                        if (resizeTimer) clearTimeout(resizeTimer);
                        resizeTimer = setTimeout(() => {
                            if (document.body.querySelector('.darkVisitedStyle')) setColorAll();
                            else window.removeEventListener('resize', resizeListener);
                        }, 500);
                    };
                    window.addEventListener('resize', resizeListener);
    
                    const darkStyle = document.createElement('style');
                    darkStyle.textContent = `a:visited{color:#b553ff!important}
                    *{text-shadow:none!important}:after,:before{background-color:transparent!important}
                    ::-webkit-scrollbar{overflow:hidden;width:.8rem;background:#000}
                    ::-webkit-scrollbar-thumb{overflow:hidden;border-radius:.4rem;background:#ddd}`;
                    darkStyle.className = 'darkVisitedStyle';
                    document.body.appendChild(darkStyle);
    
                    setColorAll();
                    observer.observe(document.body, {
                        childList: true,
                        subtree: true,
                        attributeFilter: ['style'],
                        attributes: true,
                    });
                    console.log('0');
                };
            };
        if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', main);
        else main();
        time.all = Date.now() - time.all;
        console.log(time);
    })();
  • Permalink
    このページへの個別リンクです。
    RAW
    書かれたコードへの直接のリンクです。
    Packed
    文字列が圧縮された書かれたコードへのリンクです。
    Userscript
    Greasemonkey 等で利用する場合の .user.js へのリンクです。
    Loader
    @require やソースコードが長い場合に多段ロードする Loader コミのコードへのリンクです。
    Metadata
    コード中にコメントで @xxx と書かれたメタデータの JSON です。