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