/*
* @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);
})();