darkmode Fork

    @@ -15,100 +15,208 @@ // 稀に色を変えられないこともあります。仕様です。 // 画像の色は変わりません。 // 一度実行した状態でもう一度実行すると、もとに戻ります。 - //http://let.hatelabo.jp/nanikamado/let/hLHU5aii3YU5 の改良版です。 + // 'use strict'の次の行の、bgcolorに入れる値を変更することで、背景色を変えることができます。 + // http://let.hatelabo.jp/nanikamado/let/hLHU5aii3YU5 の改良版です。 (() => { 'use strict'; - const set_color = (nodes) => { - nodes.forEach(node => { - if (!node.style) 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); - //color - if (node.tagName === 'A') node.style.color = `rgb(${r+s},${g+s},${b+s})`; - else node.style.setProperty('color', `rgb(${r+s},${g+s},${b+s})`, 'important'); - //backgroundColor - if (computed_style.backgroundColor.indexOf('a') === -1) - node.style.setProperty('background-color', '#000', 'important'); - else 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'); - //backgroundImage gradient - if (computed_style.backgroundImage.indexOf('gradient') !== -1) - node.style.backgroundImage = computed_style.backgroundImage - .replace(/[^,]*gradient\((?:[^\(\)]*\((?:[^\(\)]*\([^\(\)]*\))*[^\(\)]*[^\(\)]*\))*[^\(\)]*\),?/gi, '') || 'none'; - }); + let bgcolor = '#111'; + 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); }, - set_color_all = () => { - set_color(document.body.querySelectorAll('*')); - set_color([document.body, document.body.parentNode]); - 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('*')); - const if_dc_b = iframe.contentDocument.body || console.error(iframe); - if_dc_b.style.backgroundColor = '#000'; + 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 + ]); }; }); - document.body.style.setProperty('background-color', '#000', 'important'); + 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 dark_visited_style; - if (dark_visited_style = document.body.querySelector('.dark_visited_style')) { //even times - document.querySelectorAll('*').forEach(node => { + let darkVisitedStyle = document.body.querySelector('.darkVisitedStyle'); + if (darkVisitedStyle) { //even times + allElm().forEach(node => { if (!node.style) return; - const style = node.getAttribute('dark_before_style'); + const style = node.getAttribute('darkBeforeStyle'); if (style === null) return; node.style.cssText = style; }); - dark_visited_style.remove(); + darkVisitedStyle.remove(); console.log('1'); } else { //odd times - const observer = new MutationObserver((maa) => { - if (!document.body.querySelector('.dark_visited_style')) { + 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 => { - set_color(ma.addedNodes); - ma.addedNodes.forEach(added_node => { - if (added_node.nodeName === '#text') return; - set_color(added_node.querySelectorAll('*')); - }); + 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('reseted'); + console.log('Mutation'); }); - let resize_timer; - const resize_listener = () => { + let resizeTimer; + const resizeListener = () => { console.log('resizing'); - if (resize_timer) clearTimeout(resize_timer); - resize_timer = setTimeout(() => { - if (document.body.querySelector('.dark_visited_style')) set_color_all(); - else window.removeEventListener('resize', resize_listener); + if (resizeTimer) clearTimeout(resizeTimer); + resizeTimer = setTimeout(() => { + if (document.body.querySelector('.darkVisitedStyle')) setColorAll(); + else window.removeEventListener('resize', resizeListener); }, 500); }; - window.addEventListener('resize', resize_listener); + window.addEventListener('resize', resizeListener); - const dark_style = document.createElement('style'); - dark_style.textContent = `a:visited{color:#b553ff!important} + 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}`; - dark_style.className = 'dark_visited_style'; - document.body.appendChild(dark_style); + darkStyle.className = 'darkVisitedStyle'; + document.body.appendChild(darkStyle); - set_color_all(); + 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); })();
  • /*
     * @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 = '#111';
        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 です。