// ==UserScript== // @name 夜间模式助手 // @namespace https://github.com/syhyz1990/darkmode // @version 2.1.0 // @icon https://www.youxiaohou.com/darkmode.png // @description 实现任意网站的夜间模式,支持网站白名单 // @author YouXiaoHou // @license MIT // @homepage https://www.youxiaohou.com/tool/install-darkmode.html // @supportURL https://github.com/syhyz1990/darkmode // @match *://*/* // @require https://unpkg.com/darkrule@latest/dist/rule.min.js // @require https://unpkg.com/sweetalert2@10.16.6/dist/sweetalert2.min.js // @resource swalStyle https://unpkg.com/sweetalert2@10.16.6/dist/sweetalert2.min.css // @run-at document-start // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @grant GM_getResourceText // @downloadURL none // ==/UserScript== ;(function () { 'use strict'; let util = { getValue(name) { return GM_getValue(name); }, setValue(name, value) { GM_setValue(name, value); }, addStyle(id, tag, css) { tag = tag || 'style'; let doc = document, styleDom = doc.getElementById(id); if (styleDom) return; let style = doc.createElement(tag); style.rel = 'stylesheet'; style.id = id; tag === 'style' ? style.innerHTML = css : style.href = css; document.head.appendChild(style); }, removeElementById(eleId) { let ele = document.getElementById(eleId); ele && ele.parentNode.removeChild(ele); }, hasElementById(eleId) { return document.getElementById(eleId); }, filter: '-webkit-filter: url(#dark-mode-filter) !important; filter: url(#dark-mode-filter) !important;', reverseFilter: '-webkit-filter: url(#dark-mode-reverse-filter) !important; filter: url(#dark-mode-reverse-filter) !important;', noneFilter: '-webkit-filter: none !important; filter: none !important;', }; let main = { /** * 配置默认值 */ initValue() { let value = [{ name: 'dark_mode', value: 'light' }, { name: 'button_position', value: 'right' }, { name: 'button_size', value: 30 }, { name: 'exclude_list', value: ['youku.com', 'v.youku.com', 'www.douyu.com', 'www.iqiyi.com', 'vip.iqiyi.com', 'mail.qq.com', 'live.kuaishou.com'] }]; value.forEach((v) => { util.getValue(v.name) === undefined && util.setValue(v.name, v.value); }); }, addExtraStyle() { try { return darkModeRule; } catch (e) { return ''; } }, createDarkFilter() { if (util.hasElementById('dark-mode-svg')) return; let svgDom = ''; let div = document.createElementNS('http://www.w3.org/1999/xhtml', 'div'); div.innerHTML = svgDom; let frag = document.createDocumentFragment(); while (div.firstChild) frag.appendChild(div.firstChild); document.head.appendChild(frag); }, createDarkStyle() { util.addStyle('dark-mode-style', 'style', ` @media screen { html { ${util.filter} } /* Default Reverse rule */ img, video, iframe, canvas, :not(object):not(body) > embed, object, svg image, [style*="background:url"], [style*="background-image:url"], [style*="background: url"], [style*="background-image: url"], [background], twitterwidget, .sr-reader, .no-dark-mode, .sr-backdrop { ${util.reverseFilter} } [style*="background:url"] *, [style*="background-image:url"] *, [style*="background: url"] *, [style*="background-image: url"] *, input, [background] *, img[src^="https://s0.wp.com/latex.php"], twitterwidget .NaturalImage-image { ${util.noneFilter} } /* Text contrast */ html { text-shadow: 0 0 0 !important; } /* Full screen */ .no-filter, :-webkit-full-screen, :-webkit-full-screen *, :-moz-full-screen, :-moz-full-screen *, :fullscreen, :fullscreen * { ${util.noneFilter} } ::-webkit-scrollbar { background-color: #202324; color: #aba499; } ::-webkit-scrollbar-thumb { background-color: #454a4d; } ::-webkit-scrollbar-thumb:hover { background-color: #575e62; } ::-webkit-scrollbar-thumb:active { background-color: #484e51; } ::-webkit-scrollbar-corner { background-color: #181a1b; } /* Page background */ html { background: #fff !important; } ${this.addExtraStyle()} } @media print { .no-print { display: none !important; } }`); }, enableDarkMode() { if (this.isFullScreen()) return; this.createDarkFilter(); this.createDarkStyle(); }, disableDarkMode() { util.removeElementById('dark-mode-svg'); util.removeElementById('dark-mode-style'); }, addButton() { if (this.isTopWindow()) { let lightIcon = `
`, darkIcon = `
`; let o = document.createElement('div'), buttonPostion = util.getValue('button_position'); o.style.position = 'fixed'; o.style[buttonPostion] = '25px'; o.style.bottom = '25px'; o.style.cursor = 'pointer'; o.style.zIndex = '2147483999'; o.style.userSelect = 'none'; o.className = 'no-print'; o.id = 'darkBtn'; this.isDarkMode() ? o.innerHTML = lightIcon : o.innerHTML = darkIcon; document.body.appendChild(o); o.addEventListener("click", () => { if (this.isDarkMode()) { //黑暗模式变为正常模式 util.setValue('dark_mode', 'light'); o.innerHTML = darkIcon; this.disableDarkMode(); } else { util.setValue('dark_mode', 'dark'); o.innerHTML = lightIcon; this.enableDarkMode(); } }); } }, registerMenuCommand() { if (this.isTopWindow()) { let whiteList = util.getValue('exclude_list'); let host = location.host; if (whiteList.includes(host)) { GM_registerMenuCommand('💡 当前网站:❌', () => { let index = whiteList.indexOf(host); whiteList.splice(index, 1); util.setValue('exclude_list', whiteList); history.go(0); }); } else { GM_registerMenuCommand('💡 当前网站:✔️', () => { whiteList.push(host); util.setValue('exclude_list', Array.from(new Set(whiteList))); history.go(0); }); } GM_registerMenuCommand('⚙️ 设置', () => { let style = ` .darkmode-popup { font-size: 14px !important; } .darkmode-center { display: flex;align-items: center; } .darkmode-setting-label { display: flex;align-items: center;justify-content: space-between;padding-top: 15px; } .darkmode-setting-label-col { display: flex;align-items: flex-start;;padding-top: 15px;flex-direction:column } .darkmode-setting-radio { width: 16px;height: 16px; } .darkmode-setting-textarea { width: 100%; margin: 14px 0 0; height: 100px; resize: none; border: 1px solid #bbb; box-sizing: border-box; padding: 5px 10px; border-radius: 5px; color: #666; line-height: 1.2; } .darkmode-setting-input { border: 1px solid #bbb; box-sizing: border-box; padding: 5px 10px; border-radius: 5px; width: 100px} `; util.addStyle('darkmode-style', 'style', style); util.addStyle('swal-pub-style', 'style', GM_getResourceText('swalStyle')); let excludeListStr = util.getValue('exclude_list').join('\n'); let dom = `
`; Swal.fire({ title: '夜间模式配置', html: dom, icon: 'info', showCloseButton: true, confirmButtonText: '保存', footer: '
点击查看 使用说明,助手免费开源,检查更新
', customClass: { popup: 'darkmode-popup', }, }).then((res) => { res.isConfirmed && history.go(0); }); document.getElementById('S-Dark-Position').addEventListener('click', (e) => { e.target.tagName === "INPUT" && util.setValue('button_position', e.target.value); }); document.getElementById('S-Dark-Size').addEventListener('change', (e) => { util.setValue('button_size', e.currentTarget.value); document.getElementById('currentSize').innerText = '当前:' + e.currentTarget.value; }); document.getElementById('S-Dark-Exclude').addEventListener('change', (e) => { util.setValue('exclude_list', Array.from(new Set(e.currentTarget.value.split('\n').filter(Boolean)))); }); }); } }, isTopWindow() { return window.self === window.top; }, addListener() { document.addEventListener("fullscreenchange", (e) => { if (this.isFullScreen()) { //进入全屏 this.disableDarkMode(); } else { //退出全屏 this.isDarkMode() && this.enableDarkMode(); } }); }, isDarkMode() { return util.getValue('dark_mode') === 'dark'; }, isInExcludeList() { return util.getValue('exclude_list').includes(location.host); }, isFullScreen() { return document.fullscreenElement; }, firstEnableDarkMode() { if (document.head) { this.isDarkMode() && this.enableDarkMode(); } const headObserver = new MutationObserver(() => { this.isDarkMode() && this.enableDarkMode(); }); headObserver.observe(document.head, {childList: true, subtree: true}); if (document.body) { this.addButton(); } else { const bodyObserver = new MutationObserver(() => { if (document.body) { bodyObserver.disconnect(); this.addButton(); } }); bodyObserver.observe(document, {childList: true, subtree: true}); } }, init() { this.initValue(); this.registerMenuCommand(); if (this.isInExcludeList()) return; this.addListener(); this.firstEnableDarkMode(); } }; main.init(); })();