diff --git a/.gitignore b/.gitignore index b7afa48..f451a33 100644 --- a/.gitignore +++ b/.gitignore @@ -10,8 +10,16 @@ build/ *.iml .DS_Store node_modules/ +.pnpm-store/ dist/ # Playwright 测试工具 — 只跟踪脚本和配置,忽略输出/截图/缓存 workplace/* !workplace/*.py !workplace/*.yaml + +# 暗色扫描输出与会话 +scripts/output/ +scripts/.browser-profile/ + +# Dark Reader 源码下载目录(构建依赖已 vendor 到 ui/vendor/darkreader) +docs/darkreader/ diff --git a/CLAUDE.md b/CLAUDE.md index 8b0ea15..542a7ce 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -54,6 +54,7 @@ halo-dark-mode-plugin/ │ ├── 前端 (Vue 3 / TypeScript) ← 核心实现 │ ├── index.ts ← definePlugin() 入口,注册路由+组件 +│ ├── darkreader-engine.ts ← Dark Reader 通用暗色引擎 │ ├── injector.ts ← ThemeToggle 侧边栏注入器(MutationObserver 方案) │ ├── composables/ │ │ ├── useDarkMode.ts ← 单例状态管理(light/dark/auto + localStorage 持久化) @@ -80,33 +81,39 @@ halo-dark-mode-plugin/ ### 主题切换机制 - **触发方式**:`document.documentElement` 上设置/移除 `data-halo-theme="dark"` 属性 +- **主引擎**:Dark Reader(本地 vendored 于 `ui/vendor/darkreader`,API 包由 + `npm run api` 构建),监听 `useDarkMode().isDark`,深色时 `enable()`,浅色时 + `disable()`,可自动分析 Halo 核心与第三方插件的动态 DOM - **CSS 变量体系**:所有颜色通过 `--halo-*` 前缀的 CSS 自定义属性控制,一个语义变量对应一个视觉属性 - **颜色空间**:全部使用 OKLCH(感知均匀,暗色模式天然适配) - **暗色配色策略**:用亮度层次区分背景(越"高"的层越亮),中性色含微量蓝色调,强调色略降饱和 +- **CSS 覆盖定位**:现有 `overrides/` 仅作为 Dark Reader 的兼容层与兜底, + 不再逐页新增手工转换规则;新页面残留统一交给 Dark Reader 处理 ### ⚠️ 最关键的教训:Halo 2.25 用 UnoCSS,不是 Tailwind -Halo 2.25 的 Console 实际使用 **UnoCSS**(hash 类如 `uno-*`)加 **BEM 语义类**。**不要写 `.bg-white`、`.text-gray-900`、`.v-card` 这类 Tailwind/Vuetify 原子选择器**——它们在真实 DOM 中不存在,覆盖会静默失效。 +Halo 2.25 的 Console 实际使用 **UnoCSS**(hash 类如 `uno-*`)加 **BEM 语义aliyun "curl -sI --max-time 5 http://localhost/ 2>&1 |类**。**不要写 `.bg-white`、`.text-gray-900`、`.v-card` 这类 Tailwind/Vuetify 原子选择器**——它们在真实 DOM 中不存在,覆盖会静默失效。 真实 DOM 里的容器类名(已被 `halo-core.css` 覆盖): -| 语义 | 真实类名 | -|------|---------| -| 页面顶栏 | `.page-header` / `.page-header__title-text` | -| 列表卡片容器 | `.card-wrapper` | -| 文章/用户列表项标题 | `.entity-field-title` / `.entity-field-title-body` | -| 分页 | `.pagination` / `.pagination__btn` | -| 标签 | `.tag-default` / `.tag-content` | -| 模态框 | `.modal-content` / `.modal-header` / `.modal-body` / `.modal-footer` | -| 详情页描述项 | `.description-item-wrapper` / `.description-item__label` / `.description-item__content` | -| Toast | `.toast-container .toast-body` | -| 用户头像 | `.avatar-wrapper` / `.avatar-circle` | +| 语义 | 真实类名 | +| ------------------- | --------------------------------------------------------------------------------------------- | +| 页面顶栏 | `.page-header` / `.page-header__title-text` | +| 列表卡片容器 | `.card-wrapper` | +| 文章/用户列表项标题 | `.entity-field-title` / `.entity-field-title-body` | +| 分页 | `.pagination` / `.pagination__btn` | +| 标签 | `.tag-default` / `.tag-content` | +| 模态框 | `.modal-content` / `.modal-header` / `.modal-body` / `.modal-footer` | +| 详情页描述项 | `.description-item-wrapper` / `.description-item__label` / `.description-item__content` | +| Toast | `.toast-container .toast-body` | +| 用户头像 | `.avatar-wrapper` / `.avatar-circle` | 新增覆盖时:**先到真实环境确认类名,不要凭经验写**。 ### 覆盖策略(重写后) `halo-core.css` 是主要覆盖文件,按语义类精准覆盖。它包含三类规则: + 1. **BEM 语义类**(如 `.card-wrapper`)— 直接 `[data-halo-theme="dark"] .card-wrapper { background-color: var(--halo-bg-card) }` 2. **通用 UnoCSS 工具类**(如 `.bg-gray-50`、`.hover:text-gray-600`)— 批量覆盖文字/背景/边框 3. **根背景兜底** — `html, body { background-color: var(--halo-bg-body) !important }`(body 不设暗色会在溢出时露白) @@ -134,6 +141,15 @@ Halo 扩展点系统**没有侧边栏插槽**。`injector.ts` 用 `MutationObser 后端极简 — `DarkModePlugin extends BasePlugin` 仅含 `start()`/`stop()` 生命周期钩子。所有核心逻辑在前端。插件不依赖后端 Setting API。 +## 打包约定(必须) + +每次执行 `./gradlew build` 打 JAR 前,**必须先递增版本标签**: + +1. 修改 `gradle.properties` 中的 `version`(如 `1.0.2` → `1.0.3`) +2. 同步更新 `src/main/java/run/halo/darkmode/DarkModePlugin.java` 的 `@since` +3. 构建完成后核对 `build/libs/plugin-dark-mode-<新版本>.jar` 存在,且 JAR 内的 + `ui/style.css`、`ui/main.js` 包含本次改动(用关键字检查,例如新增类名) +4. 向用户交付时明确给出新 JAR 路径和版本号,避免线上仍加载旧 bundle 的混淆 ## 验证工作流(必须) 对 CSS 覆盖的任何改动,都要在**真实 Halo 环境**验证,不能只靠 `vite build` 通过: diff --git a/README.md b/README.md index e5c6d13..709a5fe 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,8 @@ Halo 2.25 暗色模式插件 — 为 Halo 后台管理面板提供深色/浅色 - ⚡ **瞬间切换**:CSS 变量瞬时生效,无可见闪烁 - 🧩 **侧边栏注入**:切换按钮自动出现在侧边栏底部(UserProfileBanner 上方) - ⚙️ **设置页面**:提供详细的模式选择界面(菜单 → 偏好设置 → 深色模式) +- 🔍 **Dark Reader 引擎**:内置成熟的开源暗色转换引擎,自动分析页面 CSS 与 DOM, + 第三方插件页面的黑字/白底也能自动转换,不再依赖逐页手工覆盖 - 🎨 **OKLCH 色彩空间**:感知均匀,暗色模式天然适配,WCAG AA 对比度保证 - 📦 **零后端依赖**:纯前端实现,不需要后端 API - ✅ **覆盖已验证**:仪表盘、内容管理(文章/页面/评论)、链接、订阅、瞬间、用户、主题、设置、编辑器、模态框等页面已针对 Halo 2.25 真实 DOM 逐一验证 @@ -69,6 +71,7 @@ pnpm test:unit # 单元测试 └── ui/ └── src/ ├── index.ts # definePlugin 入口 + ├── darkreader-engine.ts # Dark Reader 通用暗色引擎 ├── injector.ts # ThemeToggle 侧边栏注入器 ├── composables/ │ ├── useDarkMode.ts # 核心状态管理(模块级单例) diff --git a/gradle.properties b/gradle.properties index 07899a3..a30291e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ -version=1.0.1 +version=1.0.3 org.gradle.jvmargs=-Xmx2g -Dfile.encoding=UTF-8 -org.gradle.java.home=D:/settings/Language/Java/jdk-25.0.1 \ No newline at end of file +org.gradle.java.home=D:/settings/Language/Java/jdk-25.0.1 diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..1cc0b0c --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,60 @@ +# 暗色模式回归扫描工具 + +用 Playwright 驱动 Edge 对 Halo 后台做全路由深浅色残留扫描,验证每次 CSS 改动。 + +## 依赖 + +- Python 3.13 环境(本机:`D:\settings\settings\uv\my_uv_env`) +- Playwright(已安装):`pip install playwright` 或 `uv pip install playwright` +- 本机 Edge(Playwright 通过 `channel="msedge"` 复用,无需下载浏览器内核) + +## 首次使用:登录 + +```powershell +D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\login-wait.py +``` + +会弹出 Edge 窗口,登录 `https://blog.liuhangyv.top/console` 后脚本自动退出, +会话保存在 `scripts/.browser-profile/`(已被 gitignore,不会入库)。 + +已有登录态想复用,可显式指定 profile: + +```powershell +D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\scan-dark.py --profile workplace\pw-profile +``` + +## 常用命令 + +```powershell +# 全路由深色扫描,注入本地构建产物做预部署验证,存在残留时返回非 0 +D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\scan-dark.py --local-css ui\build\dist\style.css --assert-zero + +# 只扫指定页面 +D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\scan-dark.py --pages /console/posts,/console/users + +# 浅色模式回归抽查 +D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\scan-dark.py --mode light --pages /console/dashboard,/console/posts +``` + +## 输出 + +- `scripts/output/scan-results.json`:按路由分组的结果 +- `scripts/output/shots/*.png`:每页截图 +- 均已被 gitignore,不入库。 + +## 切换行为验证 + +```powershell +D:\settings\settings\uv\my_uv_env\Scripts\python.exe scripts\verify-toggle.py +``` + +部署新版插件后运行,会检查侧边栏按钮切换、`data-halo-theme` 属性、 +localStorage 持久化以及 Monaco 日志查看器主题是否同步。 + +## 注意事项 + +- 扫描会忽略 `img/canvas/video/svg/iframe`,避免把文章图片、编辑器画布等内容性 + 元素误判为界面残留。 +- 路由从 Vue Router 动态发现,Halo 或插件升级新增页面后无需维护清单。 +- `--local-css` 用 `add_style_tag` 追加本地样式(不替换线上 bundle),可安全验证 + CSS 修复;JS 改动(如 Monaco 主题同步)需要重新构建并部署后再跑 `verify-toggle.py`。 \ No newline at end of file diff --git a/scripts/login-wait.py b/scripts/login-wait.py new file mode 100644 index 0000000..0b21fc2 --- /dev/null +++ b/scripts/login-wait.py @@ -0,0 +1,53 @@ +"""打开带持久化配置的 Edge 窗口,等待用户在 Halo 后台完成登录。 + +登录成功后会话保存在 profile 目录,供 scan-dark.py 复用。 +""" +import argparse +import pathlib +import sys +import time + +from playwright.sync_api import sync_playwright + +DEFAULT_PROFILE = pathlib.Path(__file__).parent / ".browser-profile" +LOGIN_URL = "https://blog.liuhangyv.top/console/login" +TIMEOUT_S = 280 + + +def main() -> int: + parser = argparse.ArgumentParser(description="登录 Halo 后台并保存会话") + parser.add_argument("--profile", default=str(DEFAULT_PROFILE)) + args = parser.parse_args() + + with sync_playwright() as p: + ctx = p.chromium.launch_persistent_context( + args.profile, + channel="msedge", + headless=False, + viewport={"width": 1600, "height": 950}, + ) + page = ctx.pages[0] if ctx.pages else ctx.new_page() + page.goto(LOGIN_URL) + print("浏览器窗口已打开,请在其中登录 Halo 后台...", flush=True) + + deadline = time.time() + TIMEOUT_S + while time.time() < deadline: + try: + url = page.url + except Exception: + print("检测到窗口被关闭", flush=True) + return 2 + if "/console" in url and "/login" not in url: + time.sleep(3) + print(f"检测到登录成功: {url}", flush=True) + ctx.close() + return 0 + time.sleep(2) + + print("等待超时,未检测到登录", flush=True) + ctx.close() + return 1 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/scripts/scan-dark.py b/scripts/scan-dark.py new file mode 100644 index 0000000..8fffcf0 --- /dev/null +++ b/scripts/scan-dark.py @@ -0,0 +1,220 @@ +"""Halo 后台暗色模式残留扫描器(正式回归工具)。 + +用法示例: + python scripts/scan-dark.py --local-css ui/build/dist/style.css --assert-zero + python scripts/scan-dark.py --mode light --pages /console/posts,/console/users + python scripts/scan-dark.py --profile workplace/pw-profile +""" +import argparse +import json +import pathlib +import re +import sys +import time + +from playwright.sync_api import sync_playwright + +try: + sys.stdout.reconfigure(encoding="utf-8") +except Exception: + pass + +BASE = "https://blog.liuhangyv.top" +DEFAULT_PROFILE = pathlib.Path(__file__).parent / ".browser-profile" +DEFAULT_OUTPUT = pathlib.Path(__file__).parent / "output" + +SCAN_JS = r""" +() => { + const EXCLUDED_TAGS = new Set(['IMG', 'CANVAS', 'VIDEO', 'SVG', 'IFRAME']); + const results = []; + const seen = new Set(); + const isVisible = (el) => { + const cs = getComputedStyle(el); + return cs.display !== 'none' && cs.visibility !== 'hidden' && +cs.opacity > 0.05; + }; + const shortPath = (el) => { + const parts = []; + let cur = el; + for (let i = 0; i < 5 && cur && cur !== document.body; i++) { + let p = cur.tagName.toLowerCase(); + if (cur.id) p += '#' + cur.id; + else if (typeof cur.className === 'string' && cur.className.trim()) { + p += '.' + cur.className.trim().split(/\s+/).slice(0, 2).join('.'); + } + parts.unshift(p); + cur = cur.parentElement; + } + return parts.join(' > '); + }; + document.querySelectorAll('body *').forEach(el => { + if (EXCLUDED_TAGS.has(el.tagName)) return; + if (!isVisible(el)) return; + const r = el.getBoundingClientRect(); + if (r.width < 50 || r.height < 20) return; + const cs = getComputedStyle(el); + const issues = []; + const bg = cs.backgroundColor.match(/rgba?\(([\d.]+),\s*([\d.]+),\s*([\d.]+)(?:,\s*([\d.]+))?\)/); + if (bg && bg[4] !== '0' && +bg[1] > 235 && +bg[2] > 235 && +bg[3] > 235) { + issues.push('light-bg ' + cs.backgroundColor); + } + const hasText = [...el.childNodes].some(n => n.nodeType === 3 && n.textContent.trim()); + const c = cs.color.match(/rgba?\(([\d.]+),\s*([\d.]+),\s*([\d.]+)(?:,\s*([\d.]+))?\)/); + if (hasText && c && (+c[4] ?? 1) > 0.05 && (+c[1] + +c[2] + +c[3]) / 3 < 95) { + issues.push('dark-text ' + cs.color); + } + if (!issues.length) return; + const cls = (typeof el.className === 'string' ? el.className : '').trim().replace(/\s+/g, ' ').slice(0, 150); + const key = el.tagName + '|' + cls + '|' + issues.join(','); + if (seen.has(key)) return; + seen.add(key); + results.push({ + tag: el.tagName.toLowerCase(), + cls, + path: shortPath(el), + issues, + size: Math.round(r.width) + 'x' + Math.round(r.height), + text: (el.textContent || '').trim().slice(0, 40), + }); + }); + return results; +} +""" + + +def slug(route: str) -> str: + return re.sub(r"[^a-z0-9]+", "-", route.lower()).strip("-") or "root" + + +def main() -> int: + parser = argparse.ArgumentParser(description="Halo 后台暗色模式残留扫描") + parser.add_argument("--base", default=BASE) + parser.add_argument("--profile", default=str(DEFAULT_PROFILE)) + parser.add_argument("--output-dir", default=str(DEFAULT_OUTPUT)) + parser.add_argument("--mode", choices=["dark", "light"], default="dark") + parser.add_argument("--pages", default="", help="逗号分隔的路由子集,默认扫描全部") + parser.add_argument("--local-css", default="", help="注入本地构建的 style.css 做预部署验证") + parser.add_argument("--assert-zero", action="store_true", help="存在残留时以退出码 1 结束") + args = parser.parse_args() + + output = pathlib.Path(args.output_dir) + shots = output / "shots" + shots.mkdir(parents=True, exist_ok=True) + + if args.mode == "dark": + init_js = """ + try { localStorage.setItem('halo-dark-mode-theme', 'dark'); } catch (e) {} + document.documentElement.setAttribute('data-halo-theme', 'dark'); + """ + else: + init_js = """ + try { localStorage.setItem('halo-dark-mode-theme', 'light'); } catch (e) {} + document.documentElement.removeAttribute('data-halo-theme'); + """ + + with sync_playwright() as p: + ctx = p.chromium.launch_persistent_context( + args.profile, + channel="msedge", + headless=True, + viewport={"width": 1600, "height": 950}, + ) + ctx.add_init_script(init_js) + page = ctx.pages[0] if ctx.pages else ctx.new_page() + page.goto(args.base + "/console/dashboard", wait_until="domcontentloaded") + try: + page.wait_for_load_state("networkidle", timeout=10000) + except Exception: + pass + try: + page.wait_for_selector("#app > *, .sidebar, .main-content", timeout=20000) + except Exception: + pass + time.sleep(2) + if "/login" in page.url: + print("SESSION_EXPIRED 登录态失效,请先运行 scripts/login-wait.py 登录") + ctx.close() + return 3 + + routes = [] + try: + routes = page.evaluate( + """() => { + const app = document.querySelector('#app').__vue_app__; + const router = app.config.globalProperties.$router; + return router.getRoutes().map(r => r.path); + }""" + ) + except Exception: + routes = page.evaluate( + """() => [...new Set([...document.querySelectorAll('a[href]')] + .map(a => a.getAttribute('href')) + .filter(h => h && h.startsWith('/console')))]""" + ) + routes = sorted( + { + r if r.startswith("/console") else "/console" + r + for r in routes + if r.startswith("/") and ":" not in r and r not in ("/", "/console") + } + ) + editor = [r for r in routes if "editor" in r] + routes = [r for r in routes if "editor" not in r] + editor[:1] + if args.pages: + wanted = [r.strip() for r in args.pages.split(",") if r.strip()] + routes = [r for r in wanted if r.startswith("/console")] + + print(f"模式={args.mode} 待扫描路由数={len(routes)}", flush=True) + all_results = {} + for route in routes: + try: + last_error = None + for attempt in range(3): + try: + page.goto(args.base + route, wait_until="domcontentloaded") + last_error = None + break + except Exception as e: + last_error = e + print(f"{route}: 第 {attempt + 1} 次导航失败,稍后重试", flush=True) + time.sleep(8) + if last_error: + raise last_error + + try: + page.wait_for_load_state("networkidle", timeout=8000) + except Exception: + pass + # 限流时 Halo bundle 可能加载较慢,确保应用渲染完成再扫描 + page.wait_for_selector("#app > *, .sidebar, .main-content", timeout=20000) + if args.local_css: + page.add_style_tag(path=args.local_css) + time.sleep(2) + if args.mode == "dark": + page.evaluate("document.documentElement.setAttribute('data-halo-theme','dark')") + else: + page.evaluate("document.documentElement.removeAttribute('data-halo-theme')") + time.sleep(0.3) + items = page.evaluate(SCAN_JS) + all_results[route] = items + page.screenshot(path=str(shots / (slug(route) + ".png"))) + print(f"{route}: {len(items)} 处疑似残留", flush=True) + except Exception as e: + all_results[route] = {"error": str(e)[:200]} + print(f"{route}: 扫描失败 {e}", flush=True) + + out_json = output / "scan-results.json" + out_json.write_text(json.dumps(all_results, ensure_ascii=False, indent=2), encoding="utf-8") + ctx.close() + + total = sum( + len(v) for v in all_results.values() if isinstance(v, list) + ) + dirty = sum(1 for v in all_results.values() if isinstance(v, list) and v) + print(f"DONE 残留条目={total} 残留路由={dirty} 结果文件={out_json}", flush=True) + if args.assert_zero and dirty: + return 1 + return 0 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/scripts/verify-toggle.py b/scripts/verify-toggle.py new file mode 100644 index 0000000..1416aec --- /dev/null +++ b/scripts/verify-toggle.py @@ -0,0 +1,76 @@ +"""验证侧边栏主题切换:点击后检查 data-halo-theme、localStorage 与 Monaco 主题同步。 + +部署新版插件后运行可获得完整结果;预部署阶段 Monaco 断言会提示跳过。 +""" +import argparse +import pathlib +import sys +import time + +from playwright.sync_api import sync_playwright + +DEFAULT_PROFILE = pathlib.Path(__file__).parent / ".browser-profile" +BASE = "https://blog.liuhangyv.top" + + +def main() -> int: + parser = argparse.ArgumentParser(description="验证主题切换运行时行为") + parser.add_argument("--base", default=BASE) + parser.add_argument("--profile", default=str(DEFAULT_PROFILE)) + args = parser.parse_args() + + with sync_playwright() as p: + ctx = p.chromium.launch_persistent_context( + args.profile, channel="msedge", headless=True, + viewport={"width": 1600, "height": 950}, + ) + page = ctx.pages[0] if ctx.pages else ctx.new_page() + page.goto(args.base + "/console/dashboard", wait_until="domcontentloaded") + try: + page.wait_for_load_state("networkidle", timeout=10000) + except Exception: + pass + time.sleep(3) + if "/login" in page.url: + print("SESSION_EXPIRED 请先运行 scripts/login-wait.py") + ctx.close() + return 3 + + toggle = page.locator(".theme-toggle") + toggle.wait_for(state="visible", timeout=15000) + before = page.evaluate("() => ({ attr: document.documentElement.getAttribute('data-halo-theme'), stored: localStorage.getItem('halo-dark-mode-theme') })") + toggle.click() + time.sleep(0.5) + after = page.evaluate("() => ({ attr: document.documentElement.getAttribute('data-halo-theme'), stored: localStorage.getItem('halo-dark-mode-theme') })") + toggle.click() + time.sleep(0.5) + restored = page.evaluate("() => ({ attr: document.documentElement.getAttribute('data-halo-theme'), stored: localStorage.getItem('halo-dark-mode-theme') })") + + attr_flipped = before["attr"] != after["attr"] and before["attr"] == restored["attr"] + stored_flipped = before["stored"] != after["stored"] and before["stored"] == restored["stored"] + print(f"初始: {before}") + print(f"切换: {after}") + print(f"还原: {restored}") + print(f"属性翻转: {'PASS' if attr_flipped else 'FAIL'} 存储翻转: {'PASS' if stored_flipped else 'FAIL'}") + + page.goto(args.base + "/console/log-viewer", wait_until="domcontentloaded") + try: + page.wait_for_load_state("networkidle", timeout=6000) + except Exception: + pass + time.sleep(2) + has_monaco = page.locator(".monaco-editor").count() > 0 + if has_monaco: + dark_attr = page.evaluate("document.documentElement.getAttribute('data-halo-theme')") == "dark" + theme_cls = page.evaluate("() => { const el = document.querySelector('.monaco-editor'); return el ? el.className : '' }") + monaco_dark = "vs-dark" in theme_cls + print(f"Monaco 实例: 存在 | 属性dark={dark_attr} | 主题类含vs-dark={monaco_dark} | class={theme_cls[:60]}") + print(f"Monaco 主题同步: {'PASS' if (dark_attr and monaco_dark) or (not dark_attr and not monaco_dark) else 'FAIL(可能部署的还是旧版 JS)'}") + else: + print("Monaco 实例: 未检测到,跳过主题断言(日志页面可能未加载或路由不同)") + ctx.close() + return 0 if attr_flipped and stored_flipped else 1 + + +if __name__ == "__main__": + sys.exit(main()) \ No newline at end of file diff --git a/src/main/java/run/halo/darkmode/DarkModePlugin.java b/src/main/java/run/halo/darkmode/DarkModePlugin.java index 9955eda..3ab1522 100644 --- a/src/main/java/run/halo/darkmode/DarkModePlugin.java +++ b/src/main/java/run/halo/darkmode/DarkModePlugin.java @@ -10,7 +10,7 @@ import run.halo.app.plugin.PluginContext; *

Only one main class extending {@link BasePlugin} is allowed per plugin.

* * @author LHY - * @since 1.0.1 + * @since 1.0.3 */ @Component public class DarkModePlugin extends BasePlugin { diff --git a/ui/build.gradle b/ui/build.gradle index f97bde3..1bd34ea 100644 --- a/ui/build.gradle +++ b/ui/build.gradle @@ -5,6 +5,11 @@ plugins { group 'run.halo.darkmode.ui' +node { + // 使用系统安装的 Node.js,避免 Gradle 下载的本地 Node 目录缺失时失败 + download = false +} + tasks.register('pnpmBuild', PnpmTask) { group = 'build' description = 'Build the UI project using pnpm' diff --git a/ui/package.json b/ui/package.json index 8827382..46ef037 100644 --- a/ui/package.json +++ b/ui/package.json @@ -23,10 +23,10 @@ "@halo-dev/ui-shared": "^2.25.1", "axios": "^1.13.5", "canvas-confetti": "^1.9.3", + "darkreader": "file:./vendor/darkreader", "vue": "^3.5.28" }, "devDependencies": { - "vite": "^8.0.16", "@halo-dev/ui-plugin-bundler-kit": "^2.25.1", "@iconify-json/ri": "^1.2.10", "@tsconfig/node20": "^20.1.6", @@ -41,6 +41,7 @@ "eslint": "^9.29.0", "eslint-plugin-oxlint": "^0.16.12", "eslint-plugin-vue": "~10.0.1", + "jiti": "^2.7.0", "jsdom": "^26.1.0", "npm-run-all2": "^7.0.2", "oxlint": "^0.16.12", @@ -48,6 +49,7 @@ "sass": "^1.89.2", "typescript": "~5.8.3", "unplugin-icons": "^23.0.1", + "vite": "^8.0.16", "vitest": "^4.1.0", "vue-tsc": "^3.3.3" }, diff --git a/ui/pnpm-lock.yaml b/ui/pnpm-lock.yaml index e28b0d0..302a28d 100644 --- a/ui/pnpm-lock.yaml +++ b/ui/pnpm-lock.yaml @@ -13,23 +13,26 @@ importers: version: 2.25.2(axios@1.19.0) '@halo-dev/components': specifier: ^2.25.1 - version: 2.25.2(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3)) + version: 2.25.2(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3)) '@halo-dev/ui-shared': specifier: ^2.25.1 - version: 2.25.2(@tiptap/pm@3.29.2)(axios@1.19.0)(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3)) + version: 2.25.2(@tiptap/pm@3.29.2)(axios@1.19.0)(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3)) axios: specifier: ^1.13.5 version: 1.19.0 canvas-confetti: specifier: ^1.9.3 version: 1.9.4 + darkreader: + specifier: file:./vendor/darkreader + version: file:vendor/darkreader vue: specifier: ^3.5.28 version: 3.5.41(typescript@5.8.3) devDependencies: '@halo-dev/ui-plugin-bundler-kit': specifier: ^2.25.1 - version: 2.25.2(@rsbuild/core@2.1.10)(@rsbuild/plugin-vue@2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3)))(@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(axios@1.19.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)) + version: 2.25.2(@rsbuild/core@2.1.10)(@rsbuild/plugin-vue@2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3)))(@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(axios@1.19.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)) '@iconify-json/ri': specifier: ^1.2.10 version: 1.2.10 @@ -47,13 +50,13 @@ importers: version: 24.13.3 '@vitest/eslint-plugin': specifier: ^1.2.7 - version: 1.6.26(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3)(vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))) + version: 1.6.26(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)(vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))) '@vue/eslint-config-prettier': specifier: ^10.2.0 - version: 10.2.0(eslint@9.39.5)(prettier@3.9.6) + version: 10.2.0(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.6) '@vue/eslint-config-typescript': specifier: ^14.5.1 - version: 14.9.0(eslint-plugin-vue@10.0.1(eslint@9.39.5)(vue-eslint-parser@10.4.1(eslint@9.39.5)))(eslint@9.39.5)(typescript@5.8.3) + version: 14.9.0(eslint-plugin-vue@10.0.1(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0))))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) '@vue/test-utils': specifier: ^2.4.6 version: 2.4.11(@vue/compiler-dom@3.5.41)(@vue/server-renderer@3.5.41)(vue@3.5.41(typescript@5.8.3)) @@ -62,13 +65,16 @@ importers: version: 0.7.0(typescript@5.8.3)(vue@3.5.41(typescript@5.8.3)) eslint: specifier: ^9.29.0 - version: 9.39.5 + version: 9.39.5(jiti@2.7.0) eslint-plugin-oxlint: specifier: ^0.16.12 version: 0.16.12 eslint-plugin-vue: specifier: ~10.0.1 - version: 10.0.1(eslint@9.39.5)(vue-eslint-parser@10.4.1(eslint@9.39.5)) + version: 10.0.1(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0))) + jiti: + specifier: ^2.7.0 + version: 2.7.0 jsdom: specifier: ^26.1.0 version: 26.1.0 @@ -92,10 +98,10 @@ importers: version: 23.0.1(@vue/compiler-sfc@3.5.41) vite: specifier: ^8.0.16 - version: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0) + version: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0) vitest: specifier: ^4.1.0 - version: 4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)) + version: 4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)) vue-tsc: specifier: ^3.3.3 version: 3.3.9(typescript@5.8.3) @@ -1079,6 +1085,9 @@ packages: csstype@3.2.3: resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==} + darkreader@file:vendor/darkreader: + resolution: {directory: vendor/darkreader, type: directory} + data-urls@5.0.0: resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} engines: {node: '>=18'} @@ -1454,6 +1463,10 @@ packages: jackspeak@3.4.3: resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==} + jiti@2.7.0: + resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==} + hasBin: true + js-beautify@1.15.4: resolution: {integrity: sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==} engines: {node: '>=14'} @@ -2427,9 +2440,9 @@ snapshots: tslib: 2.8.1 optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5)': + '@eslint-community/eslint-utils@4.10.1(eslint@9.39.5(jiti@2.7.0))': dependencies: - eslint: 9.39.5 + eslint: 9.39.5(jiti@2.7.0) eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} @@ -2488,33 +2501,33 @@ snapshots: axios: 1.19.0 qs: 6.15.3 - '@halo-dev/components@2.25.2(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))': + '@halo-dev/components@2.25.2(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))': dependencies: floating-vue: 5.2.2(vue@3.5.41(typescript@5.8.3)) vue: 3.5.41(typescript@5.8.3) - vue-router: 5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)) + vue-router: 5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)) transitivePeerDependencies: - '@nuxt/kit' - '@halo-dev/ui-plugin-bundler-kit@2.25.2(@rsbuild/core@2.1.10)(@rsbuild/plugin-vue@2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3)))(@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(axios@1.19.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))': + '@halo-dev/ui-plugin-bundler-kit@2.25.2(@rsbuild/core@2.1.10)(@rsbuild/plugin-vue@2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3)))(@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(axios@1.19.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))': dependencies: '@halo-dev/api-client': 2.25.2(axios@1.19.0) '@rsbuild/core': 2.1.10 '@rsbuild/plugin-vue': 2.0.1(@rsbuild/core@2.1.10)(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(vue@3.5.41(typescript@5.8.3)) - '@vitejs/plugin-vue': 6.0.8(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)) + '@vitejs/plugin-vue': 6.0.8(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)) js-yaml: 4.3.1 semver: 7.8.5 - vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0) + vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0) transitivePeerDependencies: - axios - '@halo-dev/ui-shared@2.25.2(@tiptap/pm@3.29.2)(axios@1.19.0)(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))': + '@halo-dev/ui-shared@2.25.2(@tiptap/pm@3.29.2)(axios@1.19.0)(vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)))(vue@3.5.41(typescript@5.8.3))': dependencies: '@halo-dev/api-client': 2.25.2(axios@1.19.0) '@tiptap/core': 3.29.2(@tiptap/pm@3.29.2) mitt: 3.0.1 vue: 3.5.41(typescript@5.8.3) - vue-router: 5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)) + vue-router: 5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)) transitivePeerDependencies: - '@tiptap/pm' - axios @@ -2870,15 +2883,15 @@ snapshots: '@types/tough-cookie@4.0.5': {} - '@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3)': + '@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)': dependencies: '@eslint-community/regexpp': 4.12.2 - '@typescript-eslint/parser': 8.66.0(eslint@9.39.5)(typescript@5.8.3) + '@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) '@typescript-eslint/scope-manager': 8.66.0 - '@typescript-eslint/type-utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3) - '@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3) + '@typescript-eslint/type-utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) + '@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.66.0 - eslint: 9.39.5 + eslint: 9.39.5(jiti@2.7.0) ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@5.8.3) @@ -2886,14 +2899,14 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3)': + '@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)': dependencies: '@typescript-eslint/scope-manager': 8.66.0 '@typescript-eslint/types': 8.66.0 '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3) '@typescript-eslint/visitor-keys': 8.66.0 debug: 4.4.3 - eslint: 9.39.5 + eslint: 9.39.5(jiti@2.7.0) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -2916,13 +2929,13 @@ snapshots: dependencies: typescript: 5.8.3 - '@typescript-eslint/type-utils@8.66.0(eslint@9.39.5)(typescript@5.8.3)': + '@typescript-eslint/type-utils@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)': dependencies: '@typescript-eslint/types': 8.66.0 '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3) + '@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) debug: 4.4.3 - eslint: 9.39.5 + eslint: 9.39.5(jiti@2.7.0) ts-api-utils: 2.5.0(typescript@5.8.3) typescript: 5.8.3 transitivePeerDependencies: @@ -2945,13 +2958,13 @@ snapshots: transitivePeerDependencies: - supports-color - '@typescript-eslint/utils@8.66.0(eslint@9.39.5)(typescript@5.8.3)': + '@typescript-eslint/utils@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)': dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)) '@typescript-eslint/scope-manager': 8.66.0 '@typescript-eslint/types': 8.66.0 '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3) - eslint: 9.39.5 + eslint: 9.39.5(jiti@2.7.0) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -2961,21 +2974,21 @@ snapshots: '@typescript-eslint/types': 8.66.0 eslint-visitor-keys: 5.0.1 - '@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3))': + '@vitejs/plugin-vue@6.0.8(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0) + vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0) vue: 3.5.41(typescript@5.8.3) - '@vitest/eslint-plugin@1.6.26(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3)(vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)))': + '@vitest/eslint-plugin@1.6.26(@typescript-eslint/eslint-plugin@8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)(vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)))': dependencies: '@typescript-eslint/scope-manager': 8.66.0 - '@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3) - eslint: 9.39.5 + '@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) + eslint: 9.39.5(jiti@2.7.0) optionalDependencies: - '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) typescript: 5.8.3 - vitest: 4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)) + vitest: 4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)) transitivePeerDependencies: - supports-color @@ -2988,13 +3001,13 @@ snapshots: chai: 6.2.2 tinyrainbow: 3.1.1 - '@vitest/mocker@4.1.10(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))': + '@vitest/mocker@4.1.10(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: - vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0) + vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0) '@vitest/pretty-format@4.1.10': dependencies: @@ -3085,23 +3098,23 @@ snapshots: '@vue/devtools-shared@8.2.1': {} - '@vue/eslint-config-prettier@10.2.0(eslint@9.39.5)(prettier@3.9.6)': + '@vue/eslint-config-prettier@10.2.0(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.6)': dependencies: - eslint: 9.39.5 - eslint-config-prettier: 10.1.8(eslint@9.39.5) - eslint-plugin-prettier: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.6) + eslint: 9.39.5(jiti@2.7.0) + eslint-config-prettier: 10.1.8(eslint@9.39.5(jiti@2.7.0)) + eslint-plugin-prettier: 5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.6) prettier: 3.9.6 transitivePeerDependencies: - '@types/eslint' - '@vue/eslint-config-typescript@14.9.0(eslint-plugin-vue@10.0.1(eslint@9.39.5)(vue-eslint-parser@10.4.1(eslint@9.39.5)))(eslint@9.39.5)(typescript@5.8.3)': + '@vue/eslint-config-typescript@14.9.0(eslint-plugin-vue@10.0.1(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0))))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3)': dependencies: - '@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3) - eslint: 9.39.5 - eslint-plugin-vue: 10.0.1(eslint@9.39.5)(vue-eslint-parser@10.4.1(eslint@9.39.5)) + '@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) + eslint: 9.39.5(jiti@2.7.0) + eslint-plugin-vue: 10.0.1(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0))) fast-glob: 3.3.3 - typescript-eslint: 8.66.0(eslint@9.39.5)(typescript@5.8.3) - vue-eslint-parser: 10.4.1(eslint@9.39.5) + typescript-eslint: 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) + vue-eslint-parser: 10.4.1(eslint@9.39.5(jiti@2.7.0)) optionalDependencies: typescript: 5.8.3 transitivePeerDependencies: @@ -3307,6 +3320,8 @@ snapshots: csstype@3.2.3: {} + darkreader@file:vendor/darkreader: {} + data-urls@5.0.0: dependencies: whatwg-mimetype: 4.0.0 @@ -3366,32 +3381,32 @@ snapshots: escape-string-regexp@4.0.0: {} - eslint-config-prettier@10.1.8(eslint@9.39.5): + eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)): dependencies: - eslint: 9.39.5 + eslint: 9.39.5(jiti@2.7.0) eslint-plugin-oxlint@0.16.12: dependencies: jsonc-parser: 3.3.1 - eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5))(eslint@9.39.5)(prettier@3.9.6): + eslint-plugin-prettier@5.5.6(eslint-config-prettier@10.1.8(eslint@9.39.5(jiti@2.7.0)))(eslint@9.39.5(jiti@2.7.0))(prettier@3.9.6): dependencies: - eslint: 9.39.5 + eslint: 9.39.5(jiti@2.7.0) prettier: 3.9.6 prettier-linter-helpers: 1.0.1 synckit: 0.11.13 optionalDependencies: - eslint-config-prettier: 10.1.8(eslint@9.39.5) + eslint-config-prettier: 10.1.8(eslint@9.39.5(jiti@2.7.0)) - eslint-plugin-vue@10.0.1(eslint@9.39.5)(vue-eslint-parser@10.4.1(eslint@9.39.5)): + eslint-plugin-vue@10.0.1(eslint@9.39.5(jiti@2.7.0))(vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0))): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) - eslint: 9.39.5 + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)) + eslint: 9.39.5(jiti@2.7.0) natural-compare: 1.4.0 nth-check: 2.1.1 postcss-selector-parser: 6.1.4 semver: 7.8.5 - vue-eslint-parser: 10.4.1(eslint@9.39.5) + vue-eslint-parser: 10.4.1(eslint@9.39.5(jiti@2.7.0)) xml-name-validator: 4.0.0 eslint-scope@8.4.0: @@ -3412,9 +3427,9 @@ snapshots: eslint-visitor-keys@5.0.1: {} - eslint@9.39.5: + eslint@9.39.5(jiti@2.7.0): dependencies: - '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5) + '@eslint-community/eslint-utils': 4.10.1(eslint@9.39.5(jiti@2.7.0)) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.21.2 '@eslint/config-helpers': 0.4.2 @@ -3448,6 +3463,8 @@ snapshots: minimatch: 3.1.5 natural-compare: 1.4.0 optionator: 0.9.4 + optionalDependencies: + jiti: 2.7.0 transitivePeerDependencies: - supports-color @@ -3676,6 +3693,8 @@ snapshots: optionalDependencies: '@pkgjs/parseargs': 0.11.0 + jiti@2.7.0: {} + js-beautify@1.15.4: dependencies: config-chain: 1.1.13 @@ -4259,13 +4278,13 @@ snapshots: dependencies: prelude-ls: 1.2.1 - typescript-eslint@8.66.0(eslint@9.39.5)(typescript@5.8.3): + typescript-eslint@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3): dependencies: - '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5)(typescript@5.8.3))(eslint@9.39.5)(typescript@5.8.3) - '@typescript-eslint/parser': 8.66.0(eslint@9.39.5)(typescript@5.8.3) + '@typescript-eslint/eslint-plugin': 8.66.0(@typescript-eslint/parser@8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3))(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) + '@typescript-eslint/parser': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) '@typescript-eslint/typescript-estree': 8.66.0(typescript@5.8.3) - '@typescript-eslint/utils': 8.66.0(eslint@9.39.5)(typescript@5.8.3) - eslint: 9.39.5 + '@typescript-eslint/utils': 8.66.0(eslint@9.39.5(jiti@2.7.0))(typescript@5.8.3) + eslint: 9.39.5(jiti@2.7.0) typescript: 5.8.3 transitivePeerDependencies: - supports-color @@ -4298,7 +4317,7 @@ snapshots: picomatch: 4.0.5 webpack-virtual-modules: 0.6.2 - unplugin@3.3.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)): + unplugin@3.3.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)): dependencies: '@jridgewell/remapping': 2.3.5 picomatch: 4.0.5 @@ -4306,7 +4325,7 @@ snapshots: optionalDependencies: '@rspack/core': 2.1.8(@swc/helpers@0.5.23) rolldown: 1.2.3 - vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0) + vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0) uri-js@4.4.1: dependencies: @@ -4314,7 +4333,7 @@ snapshots: util-deprecate@1.0.2: {} - vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0): + vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.5 @@ -4324,13 +4343,14 @@ snapshots: optionalDependencies: '@types/node': 24.13.3 fsevents: 2.3.3 + jiti: 2.7.0 sass: 1.102.0 yaml: 2.9.0 - vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)): + vitest@4.1.10(@types/node@24.13.3)(jsdom@26.1.0)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 - '@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)) + '@vitest/mocker': 4.1.10(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 @@ -4347,7 +4367,7 @@ snapshots: tinyexec: 1.3.0 tinyglobby: 0.2.17 tinyrainbow: 3.1.1 - vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0) + vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0) why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 24.13.3 @@ -4359,10 +4379,10 @@ snapshots: vue-component-type-helpers@3.3.9: {} - vue-eslint-parser@10.4.1(eslint@9.39.5): + vue-eslint-parser@10.4.1(eslint@9.39.5(jiti@2.7.0)): dependencies: debug: 4.4.3 - eslint: 9.39.5 + eslint: 9.39.5(jiti@2.7.0) eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 espree: 11.2.0 @@ -4375,7 +4395,7 @@ snapshots: dependencies: vue: 3.5.41(typescript@5.8.3) - vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)): + vue-router@5.2.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(@vue/compiler-sfc@3.5.41)(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0))(vue@3.5.41(typescript@5.8.3)): dependencies: '@babel/generator': 8.0.0 '@vue-macros/common': 3.1.4(vue@3.5.41(typescript@5.8.3)) @@ -4392,13 +4412,13 @@ snapshots: picomatch: 4.0.5 scule: 1.3.0 tinyglobby: 0.2.17 - unplugin: 3.3.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0)) + unplugin: 3.3.0(@rspack/core@2.1.8(@swc/helpers@0.5.23))(rolldown@1.2.3)(vite@8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0)) unplugin-utils: 0.3.2 vue: 3.5.41(typescript@5.8.3) yaml: 2.9.0 optionalDependencies: '@vue/compiler-sfc': 3.5.41 - vite: 8.2.0(@types/node@24.13.3)(sass@1.102.0)(yaml@2.9.0) + vite: 8.2.0(@types/node@24.13.3)(jiti@2.7.0)(sass@1.102.0)(yaml@2.9.0) transitivePeerDependencies: - '@farmfe/core' - '@rspack/core' diff --git a/ui/src/darkreader-engine.ts b/ui/src/darkreader-engine.ts new file mode 100644 index 0000000..df36d2a --- /dev/null +++ b/ui/src/darkreader-engine.ts @@ -0,0 +1,48 @@ +import { disable, enable } from 'darkreader' +import { watch } from 'vue' +import { useDarkMode } from './composables/useDarkMode' + +/** + * Dark Reader 引擎参数。 + * brightness/contrast 保持默认观感,背景色与现有 Halo 暗色变量接近, + * 让 Dark Reader 补全第三方插件页面时不会显得突兀。 + */ +const DARK_READER_THEME = { + brightness: 100, + contrast: 90, + grayscale: 0, + sepia: 0, + darkSchemeBackgroundColor: '#181b20', + darkSchemeTextColor: '#e8e6e3', + scrollbarColor: '#3a3f4a', + selectionColor: '#2f6f7a', + styleSystemControls: true, +} as const + +let initialized = false + +/** + * 初始化 Dark Reader 通用暗色引擎。 + * 监听 useDarkMode 的 isDark 状态,深色时启用,浅色时关闭。 + */ +export function initDarkReaderEngine(): void { + if (initialized) return + initialized = true + + const { isDark } = useDarkMode() + watch( + isDark, + (dark) => { + try { + if (dark) { + enable(DARK_READER_THEME) + } else { + disable() + } + } catch (error) { + console.error('[dark-mode] Dark Reader 引擎异常', error) + } + }, + { immediate: true }, + ) +} diff --git a/ui/src/index.ts b/ui/src/index.ts index 7e513a1..92c48a1 100644 --- a/ui/src/index.ts +++ b/ui/src/index.ts @@ -2,10 +2,14 @@ import { definePlugin } from '@halo-dev/ui-shared' import { IconPalette } from '@halo-dev/components' import { markRaw } from 'vue' import './styles/index.css' +import { initDarkReaderEngine } from './darkreader-engine' import { injectThemeToggle } from './injector' +import { initMonacoThemeSync } from './monaco-theme' -// 在插件加载后将切换器注入到侧边栏 +// 在插件加载后将切换器注入到侧边栏,并让 Monaco 跟随主题 injectThemeToggle() +initMonacoThemeSync() +initDarkReaderEngine() export default definePlugin({ components: {}, diff --git a/ui/src/monaco-theme.ts b/ui/src/monaco-theme.ts new file mode 100644 index 0000000..0845062 --- /dev/null +++ b/ui/src/monaco-theme.ts @@ -0,0 +1,43 @@ +/** + * 让 Monaco Editor(日志查看器)跟随 Halo 暗色模式。 + * 优先调用 Monaco 官方主题 API,CSS 兜底见 styles/overrides/editor.css。 + */ + +type MonacoLike = { + editor?: { + setTheme: (name: string) => void + } +} + +const DARK_THEME = 'vs-dark' +const LIGHT_THEME = 'vs' + +function getMonaco(): MonacoLike | undefined { + return (window as unknown as { monaco?: MonacoLike }).monaco +} + +function applyMonacoTheme(): void { + const monaco = getMonaco() + if (!monaco?.editor?.setTheme) return + const isDark = document.documentElement.getAttribute('data-halo-theme') === 'dark' + monaco.editor.setTheme(isDark ? DARK_THEME : LIGHT_THEME) +} + +/** 初始化 Monaco 主题同步:立即应用,并监听属性变化与实例懒加载。 */ +export function initMonacoThemeSync(): void { + applyMonacoTheme() + + const attributeObserver = new MutationObserver(() => applyMonacoTheme()) + attributeObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: ['data-halo-theme'], + }) + + // 日志页面可能懒加载 Monaco,实例渲染后再补一次 + const instanceObserver = new MutationObserver(() => { + if (document.querySelector('.monaco-editor')) { + applyMonacoTheme() + } + }) + instanceObserver.observe(document.body, { childList: true, subtree: true }) +} \ No newline at end of file diff --git a/ui/src/styles/index.css b/ui/src/styles/index.css index e1b67bc..14ceabf 100644 --- a/ui/src/styles/index.css +++ b/ui/src/styles/index.css @@ -6,6 +6,7 @@ @import './variables.css'; @import './overrides/halo-core.css'; @import './overrides/plugin-pages.css'; +@import './overrides/uno-fallback.css'; @import './overrides/utilities.css'; @import './overrides/layout.css'; @import './overrides/components.css'; diff --git a/ui/src/styles/overrides/components.css b/ui/src/styles/overrides/components.css index f7fb4c6..195869b 100644 --- a/ui/src/styles/overrides/components.css +++ b/ui/src/styles/overrides/components.css @@ -1,73 +1,21 @@ /* ============================================================ - Halo Dark Mode — @halo-dev/components 组件库覆盖 - 覆盖 Halo 组件库中的 VCard, VModal, VDropdown, VTag 等 + Halo Dark Mode — Halo 组件库覆盖(已清理) + 已删除 Halo 2.25 中不存在的 Vuetify 类(.v-card 等), + 真实 DOM 类覆盖请优先维护 halo-core.css。 ============================================================ */ [data-halo-theme="dark"] { - /* ===== 卡片 VCard ===== */ - .v-card, - [class*="v-card"] { - background-color: var(--halo-bg-card); - border-color: var(--halo-border-base); - color: var(--halo-text-primary); - } - - /* ===== 模态框 VModal ===== */ - .v-modal, - .modal-container, - [class*="modal"] { - background-color: var(--halo-bg-card); - color: var(--halo-text-primary); - } - - /* ===== 下拉菜单 VDropdown ===== */ - .v-dropdown, - .dropdown-menu, - [class*="dropdown"] { - background-color: var(--halo-bg-dropdown); - border-color: var(--halo-border-base); - color: var(--halo-text-primary); - } - - .v-dropdown-item:hover, - .dropdown-item:hover { - background-color: var(--halo-bg-hover); - } - - /* ===== 提示框 VTooltip ===== */ - .v-tooltip, - [class*="tooltip"] { - background-color: var(--halo-bg-tooltip); - color: var(--halo-text-inverse); - } - - /* ===== 标签/徽章 VTag, VBadge ===== */ - .v-tag, - [class*="tag"], - .v-badge, - [class*="badge"] { - background-color: var(--halo-tag-bg); - color: var(--halo-tag-text); - } - - /* ===== 按钮 ===== */ - .btn-default, - .btn-secondary, - button:not([class*="btn-primary"]):not([class*="btn-danger"]) { - background-color: var(--halo-bg-card); - color: var(--halo-text-primary); - border-color: var(--halo-border-base); - } - - .btn-default:hover, - .btn-secondary:hover { - background-color: var(--halo-bg-hover); + /* ===== 模态框(具体类 + !important,防被核心 scoped 样式覆盖) ===== */ + .modal-content, + .modal-header, + .modal-body, + .modal-footer { + background-color: var(--halo-bg-card) !important; + color: var(--halo-text-primary) !important; } /* ===== 表格 VTable ===== */ - table, - .v-table, - [class*="table"] { + table { background-color: var(--halo-bg-card); color: var(--halo-text-primary); } @@ -94,49 +42,29 @@ } /* ===== 分页 ===== */ - .pagination, - .v-pagination { + .pagination { color: var(--halo-text-secondary); } - .pagination .active, - .v-pagination .active { + .pagination .active { background-color: var(--halo-accent-primary); color: var(--halo-accent-primary-text); } /* ===== 面包屑 ===== */ - .breadcrumb, - .v-breadcrumb { + .breadcrumb { color: var(--halo-text-secondary); } - .breadcrumb a, - .v-breadcrumb a { + .breadcrumb a { color: var(--halo-text-link); } /* ===== Toast / 通知 ===== */ - .v-toast, .toast-notification, [class*="toast"] { background-color: var(--halo-bg-card); color: var(--halo-text-primary); border-color: var(--halo-border-base); } - - /* ===== 步骤条 ===== */ - .v-steps, - [class*="steps"] { - color: var(--halo-text-secondary); - } - - /* ===== 开关 VSwitch ===== */ - .v-switch-track { - background-color: var(--halo-bg-disabled); - } - - .v-switch-track[aria-checked="true"] { - background-color: var(--halo-accent-primary); - } -} +} \ No newline at end of file diff --git a/ui/src/styles/overrides/editor.css b/ui/src/styles/overrides/editor.css index 3bf697d..49c4eaa 100644 --- a/ui/src/styles/overrides/editor.css +++ b/ui/src/styles/overrides/editor.css @@ -232,3 +232,32 @@ background-color: oklch(30% 0.04 160 / 50%); } } +/* ===== Monaco Editor(日志查看器,JS 主题优先,此处仅兜底) ===== */ +html[data-halo-theme="dark"] .monaco-editor.vs, +html[data-halo-theme="dark"] .monaco-editor.vs .margin, +html[data-halo-theme="dark"] .monaco-editor.vs .lines-content, +html[data-halo-theme="dark"] .monaco-editor.vs .monaco-scrollable-element { + background-color: var(--halo-bg-input) !important; + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .monaco-editor.vs .line-numbers { + color: var(--halo-text-tertiary) !important; +} +html[data-halo-theme="dark"] .monaco-editor.vs .cursor { + border-color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .monaco-editor.vs .selected-text, +html[data-halo-theme="dark"] .monaco-editor.vs .view-overlays .selected-text { + background-color: oklch(30% 0.04 160 / 50%) !important; +} +html[data-halo-theme="dark"] .monaco-editor.vs .find-widget, +html[data-halo-theme="dark"] .monaco-editor.vs .monaco-hover { + background-color: var(--halo-bg-dropdown) !important; + color: var(--halo-text-primary) !important; +} + +/* ===== 编辑器链接(覆盖通用链接色规则) ===== */ +html[data-halo-theme="dark"] .ProseMirror a, +html[data-halo-theme="dark"] .tiptap a { + color: var(--halo-text-link) !important; +} \ No newline at end of file diff --git a/ui/src/styles/overrides/forms.css b/ui/src/styles/overrides/forms.css index f569f61..ec3409a 100644 --- a/ui/src/styles/overrides/forms.css +++ b/ui/src/styles/overrides/forms.css @@ -87,3 +87,16 @@ border-color: var(--halo-border-base); } } +/* ===== 强表单覆盖(scoped 哈希类特异性更高,需 html 前缀 + !important) ===== */ +html[data-halo-theme="dark"] input:not([type="checkbox"]):not([type="radio"]):not([type="color"]), +html[data-halo-theme="dark"] textarea, +html[data-halo-theme="dark"] select { + background-color: var(--halo-bg-input) !important; + color: var(--halo-text-primary) !important; + border-color: var(--halo-border-input) !important; +} +html[data-halo-theme="dark"] input:not([type="checkbox"]):not([type="radio"]):not([type="color"])::placeholder, +html[data-halo-theme="dark"] textarea::placeholder, +html[data-halo-theme="dark"] select::placeholder { + color: var(--halo-text-tertiary) !important; +} \ No newline at end of file diff --git a/ui/src/styles/overrides/halo-core.css b/ui/src/styles/overrides/halo-core.css index 6a36d52..b6fa177 100644 --- a/ui/src/styles/overrides/halo-core.css +++ b/ui/src/styles/overrides/halo-core.css @@ -1,11 +1,14 @@ +/* ===== 根元素背景(html 自身携带属性,需独立选择器) ===== */ +html[data-halo-theme="dark"] { + background-color: var(--halo-bg-body) !important; +} /* ============================================================ Halo Dark Mode — Halo 核心 BEM 类覆盖 基于 Halo 2.25 真实 DOM 扫描(demo.halocms.site)得出的选择器 ============================================================ */ [data-halo-theme="dark"] { - /* ===== 页面根背景(body 溢出时兜底) ===== */ - html, + /* ===== 页面根背景(body 溢出时兜底,html 见文件顶部独立规则) ===== */ body { background-color: var(--halo-bg-body) !important; color: var(--halo-text-primary); @@ -100,14 +103,6 @@ .entity-field-description { color: var(--halo-text-secondary); } - .entity-field-title a, - .entity-field-title-body a { - color: var(--halo-text-primary); - } - .entity-field-title a:hover, - .entity-field-title-body a:hover { - color: var(--halo-text-link); - } /* ===== 分页 ===== */ .pagination { @@ -131,8 +126,7 @@ /* ===== 标签/徽章(BEM) ===== */ .tag-wrapper, .tag-default, - .tag-pill, - [class*="tag-"] { + .tag-pill { background-color: var(--halo-bg-hover); color: var(--halo-text-secondary); } @@ -206,10 +200,10 @@ border-color: var(--halo-border-light); } .description-item__label { - color: var(--halo-text-secondary); + color: var(--halo-text-secondary) !important; } .description-item__content { - color: var(--halo-text-primary); + color: var(--halo-text-primary) !important; } /* ===== 已选指示器 ===== */ @@ -265,8 +259,50 @@ border-color: var(--halo-border-base); } - /* ===== 通用链接 ===== */ - a:not([class]) { - color: var(--halo-text-link); - } + } +/* ===== 侧边栏激活菜单(Halo 核心 .menu-item-title.active) ===== */ +html[data-halo-theme="dark"] .menu-item-title.active, +html[data-halo-theme="dark"] .menu-item-title.active:hover { + background-color: var(--halo-menu-item-active) !important; + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .menu-item-title.active::after { + background: var(--halo-accent-primary) !important; +} + +/* ===== 空状态标题 ===== */ +html[data-halo-theme="dark"] .empty-wrapper .empty-title { + color: var(--halo-text-secondary) !important; +} + +/* ===== 提示条(Alert) ===== */ +html[data-halo-theme="dark"] .alert-wrapper { + background-color: var(--halo-bg-card) !important; + border-color: var(--halo-border-base) !important; + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .alert-wrapper .alert-title, +html[data-halo-theme="dark"] .alert-wrapper .alert-description, +html[data-halo-theme="dark"] .alert-wrapper .alert-icon { + color: var(--halo-text-secondary) !important; +} +html[data-halo-theme="dark"] .alert-wrapper.alert-warning { + border-color: oklch(40% 0.12 85) !important; +} +html[data-halo-theme="dark"] .alert-wrapper.alert-error { + border-color: oklch(40% 0.17 25) !important; +} +html[data-halo-theme="dark"] .alert-wrapper.alert-success { + border-color: oklch(40% 0.14 150) !important; +} + +/* ===== 实体列表标题链接(覆盖通用链接色,hover 变链接色) ===== */ +html[data-halo-theme="dark"] .entity-field-title a, +html[data-halo-theme="dark"] .entity-field-title-body a { + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .entity-field-title a:hover, +html[data-halo-theme="dark"] .entity-field-title-body a:hover { + color: var(--halo-text-link) !important; +} \ No newline at end of file diff --git a/ui/src/styles/overrides/layout.css b/ui/src/styles/overrides/layout.css index a18d87d..ea9f456 100644 --- a/ui/src/styles/overrides/layout.css +++ b/ui/src/styles/overrides/layout.css @@ -55,8 +55,7 @@ } /* ===== 页面标题 ===== */ - .page-title, - .v-card-title { + .page-title { color: var(--halo-text-primary); } } diff --git a/ui/src/styles/overrides/plugin-pages.css b/ui/src/styles/overrides/plugin-pages.css index c4c7980..4ce8214 100644 --- a/ui/src/styles/overrides/plugin-pages.css +++ b/ui/src/styles/overrides/plugin-pages.css @@ -247,3 +247,137 @@ color: var(--halo-text-primary) !important; } } +/* ===== AstraHub / 心愿便签(共用 ah-* UI) ===== */ +html[data-halo-theme="dark"] .ah-card, +html[data-halo-theme="dark"] .ah-topbar, +html[data-halo-theme="dark"] .ah-topbar-search, +html[data-halo-theme="dark"] .ah-float-nav { + background-color: var(--halo-bg-card) !important; + border-color: var(--halo-border-base) !important; + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .ah-topbar-search { + background-color: var(--halo-bg-input) !important; +} +html[data-halo-theme="dark"] .ah-topbar-brand, +html[data-halo-theme="dark"] .planet-hero-title { + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .ah-topbar-search input { + background-color: transparent !important; + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .planet-links-more { + background-color: var(--halo-bg-card) !important; + color: var(--halo-text-secondary) !important; +} + +/* ===== RSS 订阅(links/rss) ===== */ +html[data-halo-theme="dark"] .subscription-panel, +html[data-halo-theme="dark"] .feed-main, +html[data-halo-theme="dark"] .feed-toolbar, +html[data-halo-theme="dark"] .feed-stream, +html[data-halo-theme="dark"] .feed-stream__footer { + background-color: var(--halo-bg-card) !important; + border-color: var(--halo-border-base) !important; + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .feed-status-tabs { + background-color: var(--halo-bg-hover) !important; + color: var(--halo-text-secondary) !important; +} +html[data-halo-theme="dark"] .feed-brief__title, +html[data-halo-theme="dark"] .feed-stream p, +html[data-halo-theme="dark"] .feed-stream a, +html[data-halo-theme="dark"] .subscription-panel a, +html[data-halo-theme="dark"] .subscription-panel span { + color: var(--halo-text-primary) !important; +} + +/* ===== 私密文章插件 ===== */ +html[data-halo-theme="dark"] .focus-card, +html[data-halo-theme="dark"] .overview-card, +html[data-halo-theme="dark"] .list-card, +html[data-halo-theme="dark"] .empty-state { + background-color: var(--halo-bg-card) !important; + border-color: var(--halo-border-base) !important; + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .overview-card h2, +html[data-halo-theme="dark"] .page-shell h2, +html[data-halo-theme="dark"] .overview-stats dd, +html[data-halo-theme="dark"] .overview-stats dt { + color: var(--halo-text-primary) !important; +} + +/* ===== AI 评论自动处理插件 ===== */ +html[data-halo-theme="dark"] .settings-container, +html[data-halo-theme="dark"] .setting-panel, +html[data-halo-theme="dark"] .tabs-wrap, +html[data-halo-theme="dark"] .sidebar-card, +html[data-halo-theme="dark"] .list-empty, +html[data-halo-theme="dark"] .reply-card { + background-color: var(--halo-bg-card) !important; + border-color: var(--halo-border-base) !important; + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .form-row, +html[data-halo-theme="dark"] .list-col, +html[data-halo-theme="dark"] .panel-header, +html[data-halo-theme="dark"] .card-footer { + background-color: var(--halo-bg-hover) !important; + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .section-header__text h3, +html[data-halo-theme="dark"] .sidebar-header h4, +html[data-halo-theme="dark"] .panel-header h3, +html[data-halo-theme="dark"] .reply-card h3 { + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .wake-word-tag { + background-color: var(--halo-bg-hover) !important; + color: var(--halo-text-secondary) !important; +} + +/* ===== 数据工坊 / 代码注入器等工具页语义容器 ===== */ +html[data-halo-theme="dark"] .data-studio-card-body, +html[data-halo-theme="dark"] .injector-view-card-body, +html[data-halo-theme="dark"] .injector-editor-container { + background-color: var(--halo-bg-card) !important; + border-color: var(--halo-border-base) !important; + color: var(--halo-text-primary) !important; +} + +/* ===== 示例插件页 ===== */ +html[data-halo-theme="dark"] main section#plugin-starter { + background-color: var(--halo-bg-body) !important; + color: var(--halo-text-primary) !important; +} + +/* ===== 友链卡片徽章 ===== */ +html[data-halo-theme="dark"] .link-badge { + background-color: var(--halo-bg-hover) !important; + color: var(--halo-text-primary) !important; + border-color: var(--halo-border-base) !important; +} +/* ===== 日程日历插件(time-column 布局) ===== */ +html[data-halo-theme="dark"] .time-column__header, +html[data-halo-theme="dark"] .time-column__body, +html[data-halo-theme="dark"] [class*="time-column"] { + background-color: var(--halo-bg-card) !important; + color: var(--halo-text-primary) !important; + border-color: var(--halo-border-base) !important; +} +html[data-halo-theme="dark"] .entry-card-header__title { + color: var(--halo-text-primary) !important; +} + +/* ===== 存储工具箱补充类 ===== */ +html[data-halo-theme="dark"] .stat-value, +html[data-halo-theme="dark"] .stat-card .card-title, +html[data-halo-theme="dark"] .panel-card .card-title { + color: var(--halo-text-primary) !important; +} +html[data-halo-theme="dark"] .bar-track { + background-color: var(--halo-bg-disabled) !important; +} \ No newline at end of file diff --git a/ui/src/styles/overrides/uno-fallback.css b/ui/src/styles/overrides/uno-fallback.css new file mode 100644 index 0000000..844a909 --- /dev/null +++ b/ui/src/styles/overrides/uno-fallback.css @@ -0,0 +1,34 @@ +/* ============================================================ + Halo Dark Mode — UnoCSS 哈希类兜底 + Halo 2.25 中 UnoCSS 同时生成 uno-* / i-* 哈希类, + 这些类随构建变化,只能按结构定位兜底,不写死哈希值。 + 注意:本文件规则刻意不加 !important,便于后续语义规则覆盖。 + ============================================================ */ + +/* 文字兜底:只修正文本元素,豁免表单控件 */ +html[data-halo-theme="dark"] main [class*="uno-"]:not(input):not(select):not(textarea) { + color: var(--halo-text-primary); +} + +/* 块级容器背景兜底:豁免交互元素与表单控件 */ +html[data-halo-theme="dark"] main [class*="uno-"]:not(button):not(a):not(input):not(select):not(textarea):not(label) { + background-color: var(--halo-bg-card); + border-color: var(--halo-border-base); +} + +/* 页面级容器使用内容区背景 */ +html[data-halo-theme="dark"] main > [class*="uno-"] { + background-color: var(--halo-bg-content); +} + +/* 卡片头/卡片体内的 uno-* 区块使用 hover 级背景 */ +html[data-halo-theme="dark"] .card-header [class*="uno-"], +html[data-halo-theme="dark"] .card-body [class*="uno-"] { + background-color: var(--halo-bg-hover); + border-color: var(--halo-border-light); +} + +/* 旧版 i-* 哈希:只做文字兜底,避免给图标类设置背景 */ +html[data-halo-theme="dark"] main [class*="i-"]:not(input):not(select):not(textarea) { + color: var(--halo-text-primary); +} \ No newline at end of file diff --git a/ui/src/styles/overrides/utilities.css b/ui/src/styles/overrides/utilities.css index 83bdfa7..fca3ee3 100644 --- a/ui/src/styles/overrides/utilities.css +++ b/ui/src/styles/overrides/utilities.css @@ -62,10 +62,7 @@ } /* ===== 链接 ===== */ - a:not([class*="text-"]), - .hover\:text-gray-900:hover, - .hover\:text-gray-800:hover, - .hover\:text-gray-700:hover { + html[data-halo-theme="dark"] a:not([class*="text-"]) { color: var(--halo-text-link); } diff --git a/ui/vendor/darkreader/LICENSE b/ui/vendor/darkreader/LICENSE new file mode 100644 index 0000000..73e15f6 --- /dev/null +++ b/ui/vendor/darkreader/LICENSE @@ -0,0 +1,23 @@ +MIT License + +Copyright (c) 2026 Dark Reader Ltd. + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/ui/vendor/darkreader/darkreader.js b/ui/vendor/darkreader/darkreader.js new file mode 100644 index 0000000..a481ea0 --- /dev/null +++ b/ui/vendor/darkreader/darkreader.js @@ -0,0 +1,9366 @@ +/** + * Dark Reader v4.9.129 + * https://darkreader.org/ + */ + +(function (global, factory) { + typeof exports === "object" && typeof module !== "undefined" + ? factory(exports) + : typeof define === "function" && define.amd + ? define(["exports"], factory) + : ((global = + typeof globalThis !== "undefined" + ? globalThis + : global || self), + factory((global.DarkReader = {}))); +})(this, function (exports) { + "use strict"; + + var MessageTypeUItoBG; + (function (MessageTypeUItoBG) { + MessageTypeUItoBG["GET_DATA"] = "ui-bg-get-data"; + MessageTypeUItoBG["GET_DEVTOOLS_DATA"] = "ui-bg-get-devtools-data"; + MessageTypeUItoBG["SUBSCRIBE_TO_CHANGES"] = + "ui-bg-subscribe-to-changes"; + MessageTypeUItoBG["UNSUBSCRIBE_FROM_CHANGES"] = + "ui-bg-unsubscribe-from-changes"; + MessageTypeUItoBG["CHANGE_SETTINGS"] = "ui-bg-change-settings"; + MessageTypeUItoBG["SET_THEME"] = "ui-bg-set-theme"; + MessageTypeUItoBG["TOGGLE_ACTIVE_TAB"] = "ui-bg-toggle-active-tab"; + MessageTypeUItoBG["MARK_NEWS_AS_READ"] = "ui-bg-mark-news-as-read"; + MessageTypeUItoBG["MARK_NEWS_AS_DISPLAYED"] = + "ui-bg-mark-news-as-displayed"; + MessageTypeUItoBG["LOAD_CONFIG"] = "ui-bg-load-config"; + MessageTypeUItoBG["APPLY_DEV_FIXES"] = "ui-bg-apply-dev-fixes"; + MessageTypeUItoBG["RESET_DEV_FIXES"] = "ui-bg-reset-dev-fixes"; + MessageTypeUItoBG["START_ACTIVATION"] = "ui-bg-start-activation"; + MessageTypeUItoBG["RESET_ACTIVATION"] = "ui-bg-reset-activation"; + MessageTypeUItoBG["COLOR_SCHEME_CHANGE"] = "ui-bg-color-scheme-change"; + MessageTypeUItoBG["HIDE_HIGHLIGHTS"] = "ui-bg-hide-highlights"; + })(MessageTypeUItoBG || (MessageTypeUItoBG = {})); + var MessageTypeBGtoUI; + (function (MessageTypeBGtoUI) { + MessageTypeBGtoUI["CHANGES"] = "bg-ui-changes"; + })(MessageTypeBGtoUI || (MessageTypeBGtoUI = {})); + var DebugMessageTypeBGtoUI; + (function (DebugMessageTypeBGtoUI) { + DebugMessageTypeBGtoUI["CSS_UPDATE"] = "debug-bg-ui-css-update"; + DebugMessageTypeBGtoUI["UPDATE"] = "debug-bg-ui-update"; + })(DebugMessageTypeBGtoUI || (DebugMessageTypeBGtoUI = {})); + var MessageTypeBGtoCS; + (function (MessageTypeBGtoCS) { + MessageTypeBGtoCS["ADD_CSS_FILTER"] = "bg-cs-add-css-filter"; + MessageTypeBGtoCS["ADD_DYNAMIC_THEME"] = "bg-cs-add-dynamic-theme"; + MessageTypeBGtoCS["ADD_STATIC_THEME"] = "bg-cs-add-static-theme"; + MessageTypeBGtoCS["ADD_SVG_FILTER"] = "bg-cs-add-svg-filter"; + MessageTypeBGtoCS["CLEAN_UP"] = "bg-cs-clean-up"; + MessageTypeBGtoCS["FETCH_RESPONSE"] = "bg-cs-fetch-response"; + MessageTypeBGtoCS["UNSUPPORTED_SENDER"] = "bg-cs-unsupported-sender"; + })(MessageTypeBGtoCS || (MessageTypeBGtoCS = {})); + var DebugMessageTypeBGtoCS; + (function (DebugMessageTypeBGtoCS) { + DebugMessageTypeBGtoCS["RELOAD"] = "debug-bg-cs-reload"; + })(DebugMessageTypeBGtoCS || (DebugMessageTypeBGtoCS = {})); + var MessageTypeCStoBG; + (function (MessageTypeCStoBG) { + MessageTypeCStoBG["COLOR_SCHEME_CHANGE"] = "cs-bg-color-scheme-change"; + MessageTypeCStoBG["DARK_THEME_DETECTED"] = "cs-bg-dark-theme-detected"; + MessageTypeCStoBG["DARK_THEME_NOT_DETECTED"] = + "cs-bg-dark-theme-not-detected"; + MessageTypeCStoBG["FETCH"] = "cs-bg-fetch"; + MessageTypeCStoBG["DOCUMENT_CONNECT"] = "cs-bg-document-connect"; + MessageTypeCStoBG["DOCUMENT_FORGET"] = "cs-bg-document-forget"; + MessageTypeCStoBG["DOCUMENT_FREEZE"] = "cs-bg-document-freeze"; + MessageTypeCStoBG["DOCUMENT_RESUME"] = "cs-bg-document-resume"; + })(MessageTypeCStoBG || (MessageTypeCStoBG = {})); + var DebugMessageTypeCStoBG; + (function (DebugMessageTypeCStoBG) { + DebugMessageTypeCStoBG["LOG"] = "debug-cs-bg-log"; + })(DebugMessageTypeCStoBG || (DebugMessageTypeCStoBG = {})); + var MessageTypeCStoUI; + (function (MessageTypeCStoUI) { + MessageTypeCStoUI["EXPORT_CSS_RESPONSE"] = "cs-ui-export-css-response"; + })(MessageTypeCStoUI || (MessageTypeCStoUI = {})); + var MessageTypeUItoCS; + (function (MessageTypeUItoCS) { + MessageTypeUItoCS["EXPORT_CSS"] = "ui-cs-export-css"; + })(MessageTypeUItoCS || (MessageTypeUItoCS = {})); + + const isNavigatorDefined = typeof navigator !== "undefined"; + const userAgent = isNavigatorDefined + ? navigator.userAgentData && + Array.isArray(navigator.userAgentData.brands) + ? navigator.userAgentData.brands + .map( + (brand) => `${brand.brand.toLowerCase()} ${brand.version}` + ) + .join(" ") + : navigator.userAgent.toLowerCase() + : "some useragent"; + const platform = isNavigatorDefined + ? navigator.userAgentData && + typeof navigator.userAgentData.platform === "string" + ? navigator.userAgentData.platform.toLowerCase() + : navigator.platform.toLowerCase() + : "some platform"; + const isChromium = + userAgent.includes("chrome") || userAgent.includes("chromium"); + const isFirefox = + userAgent.includes("firefox") || + userAgent.includes("thunderbird") || + userAgent.includes("librewolf"); + const isSafari = userAgent.includes("safari") && !isChromium; + const isWindows = platform.startsWith("win"); + const isMacOS = platform.startsWith("mac"); + const isMobile = + isNavigatorDefined && navigator.userAgentData + ? navigator.userAgentData.mobile + : userAgent.includes("mobile") || false; + const isShadowDomSupported = typeof ShadowRoot === "function"; + const isMatchMediaChangeEventListenerSupported = + typeof MediaQueryList === "function" && + typeof MediaQueryList.prototype.addEventListener === "function"; + const isLayerRuleSupported = typeof CSSLayerBlockRule === "function"; + const isContainerRuleSupported = typeof CSSContainerRule === "function"; + (() => { + const m = userAgent.match(/chrom(?:e|ium)(?:\/| )([^ ]+)/); + if (m && m[1]) { + return m[1]; + } + return ""; + })(); + (() => { + const m = userAgent.match(/(?:firefox|librewolf)(?:\/| )([^ ]+)/); + if (m && m[1]) { + return m[1]; + } + return ""; + })(); + const isDefinedSelectorSupported = (() => { + try { + document.querySelector(":defined"); + return true; + } catch (err) { + return false; + } + })(); + const isCSSColorSchemePropSupported = (() => { + try { + if (typeof document === "undefined") { + return false; + } + const el = document.createElement("div"); + if (!el || typeof el.style !== "object") { + return false; + } + if (typeof el.style.colorScheme === "string") { + return true; + } + el.setAttribute("style", "color-scheme: dark"); + return el.style.colorScheme === "dark"; + } catch (e) { + return false; + } + })(); + + async function getOKResponse(url, mimeType, origin) { + const credentials = + origin && url.startsWith(`${origin}/`) ? undefined : "omit"; + const redirect = mimeType === "text/css" ? undefined : "error"; + const response = await fetch(url, { + cache: "force-cache", + credentials, + referrer: origin, + redirect + }); + if ( + isFirefox && + mimeType === "text/css" && + url.startsWith("moz-extension://") && + url.endsWith(".css") + ) { + return response; + } + const contentType = response.headers.get("Content-Type"); + if ( + mimeType && + !( + contentType === mimeType || + contentType?.startsWith(`${mimeType};`) + ) + ) { + throw new Error(`Mime type mismatch when loading ${url}`); + } + if ( + response.redirected && + response.url && + shouldIgnoreCors(new URL(response.url)) + ) { + throw new Error("Cross-origin limit reached"); + } + if (!response.ok) { + throw new Error( + `Unable to load ${url} ${response.status} ${response.statusText}` + ); + } + return response; + } + async function loadAsDataURL(url, mimeType) { + const response = await getOKResponse(url, mimeType); + return await readResponseAsDataURL(response); + } + async function loadAsBlob(url, mimeType) { + const response = await getOKResponse(url, mimeType); + return await response.blob(); + } + async function readResponseAsDataURL(response) { + const blob = await response.blob(); + const dataURL = await new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result); + reader.onerror = () => reject(reader.error); + reader.readAsDataURL(blob); + }); + return dataURL; + } + async function loadAsText(url, mimeType, origin) { + const response = await getOKResponse(url, mimeType, origin); + return await response.text(); + } + const MAX_CORS_HOSTS = 16; + const corsHosts = new Set(); + const checkedHosts = new Set(); + const localAliases = [ + "127-0-0-1.org.uk", + "42foo.com", + "domaincontrol.com", + "fbi.com", + "fuf.me", + "lacolhost.com", + "local.sisteminha.com", + "localfabriek.nl", + "localhost", + "localhst.co.uk", + "localmachine.info", + "localmachine.name", + "localtest.me", + "lvh.me", + "mouse-potato.com", + "nip.io", + "sslip.io", + "vcap.me", + "xip.io", + "yoogle.com" + ]; + const localSubDomains = [ + ".corp", + ".direct", + ".home", + ".internal", + ".intranet", + ".lan", + ".local", + ".localdomain", + ".test", + ".zz", + ...localAliases.map((alias) => `.${alias}`) + ]; + function isIPHost(hostname) { + if (hostname.startsWith("[")) { + return true; + } + const labels = hostname.split("."); + const last = labels[labels.length - 1]; + return /^(0x[0-9a-f]+|\d+)$/i.test(last); + } + function shouldIgnoreCors(url) { + const {host, port, protocol} = url; + const hostname = url.hostname.endsWith(".") + ? url.hostname.slice(0, -1) + : url.hostname; + if (!corsHosts.has(host)) { + corsHosts.add(host); + } + if (checkedHosts.has(host)) { + return false; + } + if ( + corsHosts.size >= MAX_CORS_HOSTS || + protocol !== "https:" || + port !== "" || + localAliases.includes(hostname) || + localSubDomains.some((sub) => hostname.endsWith(sub)) || + isIPHost(hostname) + ) { + return true; + } + checkedHosts.add(host); + return false; + } + + const throwCORSError = async (url) => { + return Promise.reject( + new Error( + [ + "Embedded Dark Reader cannot access a cross-origin resource", + url, + "Overview your URLs and CORS policies or use", + "`DarkReader.setFetchMethod(fetch: (url) => Promise))`.", + "See if using `DarkReader.setFetchMethod(window.fetch)`", + "before `DarkReader.enable()` works." + ].join(" ") + ) + ); + }; + let fetcher = throwCORSError; + function setFetchMethod$1(fetch) { + if (fetch) { + fetcher = fetch; + } else { + fetcher = throwCORSError; + } + } + async function callFetchMethod(url) { + return await fetcher(url); + } + + if (!window.chrome) { + window.chrome = {}; + } + if (!chrome.runtime) { + chrome.runtime = {}; + } + const messageListeners = new Set(); + async function sendMessage(...args) { + if (args[0] && args[0].type === MessageTypeCStoBG.FETCH) { + const {id} = args[0]; + try { + const {url, responseType} = args[0].data; + const response = await callFetchMethod(url); + let text; + if (responseType === "data-url") { + text = await readResponseAsDataURL(response); + } else { + text = await response.text(); + } + messageListeners.forEach((cb) => + cb({ + type: MessageTypeBGtoCS.FETCH_RESPONSE, + data: text, + error: null, + id + }) + ); + } catch (error) { + console.error(error); + messageListeners.forEach((cb) => + cb({ + type: MessageTypeBGtoCS.FETCH_RESPONSE, + data: null, + error, + id + }) + ); + } + } + } + function addMessageListener(callback) { + messageListeners.add(callback); + } + if (typeof chrome.runtime.sendMessage === "function") { + const nativeSendMessage = chrome.runtime.sendMessage; + chrome.runtime.sendMessage = (...args) => { + sendMessage(...args); + nativeSendMessage.apply(chrome.runtime, args); + }; + } else { + chrome.runtime.sendMessage = sendMessage; + } + if (!chrome.runtime.onMessage) { + chrome.runtime.onMessage = {}; + } + if (typeof chrome.runtime.onMessage.addListener === "function") { + const nativeAddListener = chrome.runtime.onMessage.addListener; + chrome.runtime.onMessage.addListener = (...args) => { + addMessageListener(args[0]); + nativeAddListener.apply(chrome.runtime.onMessage, args); + }; + } else { + chrome.runtime.onMessage.addListener = (...args) => + addMessageListener(args[0]); + } + + var ThemeEngine; + (function (ThemeEngine) { + ThemeEngine["cssFilter"] = "cssFilter"; + ThemeEngine["svgFilter"] = "svgFilter"; + ThemeEngine["staticTheme"] = "staticTheme"; + ThemeEngine["dynamicTheme"] = "dynamicTheme"; + })(ThemeEngine || (ThemeEngine = {})); + + var AutomationMode; + (function (AutomationMode) { + AutomationMode["NONE"] = ""; + AutomationMode["TIME"] = "time"; + AutomationMode["SYSTEM"] = "system"; + AutomationMode["LOCATION"] = "location"; + })(AutomationMode || (AutomationMode = {})); + + const DEFAULT_COLORS = { + darkScheme: { + background: "#181a1b", + text: "#e8e6e3" + }, + lightScheme: { + background: "#dcdad7", + text: "#181a1b" + } + }; + const DEFAULT_THEME = { + mode: 1, + brightness: 100, + contrast: 100, + grayscale: 0, + sepia: 0, + useFont: false, + fontFamily: isMacOS + ? "Helvetica Neue" + : isWindows + ? "Segoe UI" + : "Open Sans", + textStroke: 0, + engine: ThemeEngine.dynamicTheme, + stylesheet: "", + darkSchemeBackgroundColor: DEFAULT_COLORS.darkScheme.background, + darkSchemeTextColor: DEFAULT_COLORS.darkScheme.text, + lightSchemeBackgroundColor: DEFAULT_COLORS.lightScheme.background, + lightSchemeTextColor: DEFAULT_COLORS.lightScheme.text, + scrollbarColor: "", + selectionColor: "auto", + styleSystemControls: !isCSSColorSchemePropSupported, + lightColorScheme: "Default", + darkColorScheme: "Default", + immediateModify: false + }; + const filterModeSites = [ + "*.officeapps.live.com", + "*.sharepoint.com", + "docs.google.com", + "onedrive.live.com" + ]; + ({ + customThemes: filterModeSites.map((url) => { + const engine = ThemeEngine.cssFilter; + return { + url: [url], + theme: {...DEFAULT_THEME, engine}, + builtIn: true + }; + }), + automation: { + mode: AutomationMode.NONE + } + }); + + function getMatches(regex, input, group = 0) { + const matches = []; + let m; + while ((m = regex.exec(input))) { + matches.push(m[group]); + } + return matches; + } + function getMatchesWithOffsets(regex, input, group = 0) { + const matches = []; + let m; + while ((m = regex.exec(input))) { + matches.push({text: m[group], offset: m.index}); + } + return matches; + } + function getHashCode(text) { + const len = text.length; + let hash = 0; + for (let i = 0; i < len; i++) { + const c = text.charCodeAt(i); + hash = ((hash << 5) - hash + c) & 4294967295; + } + return hash; + } + function escapeRegExpSpecialChars(input) { + return input.replaceAll(/[\^$.*+?\(\)\[\]{}|\-\\]/g, "\\$&"); + } + function getParenthesesRange(input, searchStartIndex = 0) { + return getOpenCloseRange(input, searchStartIndex, "(", ")", []); + } + function getOpenCloseRange( + input, + searchStartIndex, + openToken, + closeToken, + excludeRanges + ) { + let indexOf; + if (excludeRanges.length === 0) { + indexOf = (token, pos) => input.indexOf(token, pos); + } else { + indexOf = (token, pos) => + indexOfExcluding(input, token, pos, excludeRanges); + } + const {length} = input; + let depth = 0; + let firstOpenIndex = -1; + for (let i = searchStartIndex; i < length; i++) { + if (depth === 0) { + const openIndex = indexOf(openToken, i); + if (openIndex < 0) { + break; + } + firstOpenIndex = openIndex; + depth++; + i = openIndex; + } else { + const closeIndex = indexOf(closeToken, i); + if (closeIndex < 0) { + break; + } + const openIndex = indexOf(openToken, i); + if (openIndex < 0 || closeIndex <= openIndex) { + depth--; + if (depth === 0) { + return {start: firstOpenIndex, end: closeIndex + 1}; + } + i = closeIndex; + } else { + depth++; + i = openIndex; + } + } + } + return null; + } + function indexOfExcluding(input, search, position, excludeRanges) { + const i = input.indexOf(search, position); + const exclusion = excludeRanges.find((r) => i >= r.start && i < r.end); + if (exclusion) { + return indexOfExcluding( + input, + search, + exclusion.end, + excludeRanges + ); + } + return i; + } + function splitExcluding(input, separator, excludeRanges) { + const parts = []; + let commaIndex = -1; + let currIndex = 0; + while ( + (commaIndex = indexOfExcluding( + input, + separator, + currIndex, + excludeRanges + )) >= 0 + ) { + parts.push(input.substring(currIndex, commaIndex).trim()); + currIndex = commaIndex + 1; + } + parts.push(input.substring(currIndex).trim()); + return parts; + } + + let anchor; + const parsedURLCache = new Map(); + function fixBaseURL($url) { + if (!anchor) { + anchor = document.createElement("a"); + } + anchor.href = $url; + return anchor.href; + } + function parseURL($url, $base = null) { + const key = `${$url}${$base ? `;${$base}` : ""}`; + if (parsedURLCache.has(key)) { + return parsedURLCache.get(key); + } + if ($base) { + const parsedURL = new URL($url, fixBaseURL($base)); + parsedURLCache.set(key, parsedURL); + return parsedURL; + } + const parsedURL = new URL(fixBaseURL($url)); + parsedURLCache.set($url, parsedURL); + return parsedURL; + } + function getAbsoluteURL($base, $relative) { + if ($relative.match(/^data\\?\:/)) { + return $relative; + } + if (/^\/\//.test($relative)) { + return `${location.protocol}${$relative}`; + } + const b = parseURL($base); + const a = parseURL($relative, b.href); + return a.href; + } + function isRelativeHrefOnAbsolutePath(href) { + if (href.startsWith("data:")) { + return true; + } + const url = parseURL(href); + if (url.protocol !== location.protocol) { + return false; + } + if (url.hostname !== location.hostname) { + return false; + } + if (url.port !== location.port) { + return false; + } + return url.pathname === location.pathname; + } + + const excludedSelectors = [ + "pre", + "pre *", + "code", + '[aria-hidden="true"]', + '[class*="fa-"]', + ".fa", + ".fab", + ".fad", + ".fal", + ".far", + ".fas", + ".fass", + ".fasr", + ".fat", + ".icofont", + '[style*="font-"]', + '[class*="icon"]', + '[class*="Icon"]', + '[class*="symbol"]', + '[class*="Symbol"]', + ".glyphicon", + '[class*="material-symbol"]', + '[class*="material-icon"]', + "mu", + '[class*="mu-"]', + ".typcn", + '[class*="vjs-"]' + ]; + function createTextStyle(config) { + const lines = []; + lines.push(`*:not(${excludedSelectors.join(", ")}) {`); + if (config.useFont && config.fontFamily) { + lines.push(` font-family: ${config.fontFamily} !important;`); + } + if (config.textStroke > 0) { + lines.push( + ` -webkit-text-stroke: ${config.textStroke}px !important;` + ); + lines.push(` text-stroke: ${config.textStroke}px !important;`); + } + lines.push("}"); + return lines.join("\n"); + } + + function isArrayLike(items) { + return items.length != null; + } + function forEach(items, iterator) { + if (isArrayLike(items)) { + for (let i = 0, len = items.length; i < len; i++) { + iterator(items[i]); + } + } else { + for (const item of items) { + iterator(item); + } + } + } + function push(array, addition) { + forEach(addition, (a) => array.push(a)); + } + function toArray(items) { + const results = []; + for (let i = 0, len = items.length; i < len; i++) { + results.push(items[i]); + } + return results; + } + + function scale(x, inLow, inHigh, outLow, outHigh) { + return ((x - inLow) * (outHigh - outLow)) / (inHigh - inLow) + outLow; + } + function clamp(x, min, max) { + return Math.min(max, Math.max(min, x)); + } + function multiplyMatrices(m1, m2) { + const result = []; + for (let i = 0, len = m1.length; i < len; i++) { + result[i] = []; + for (let j = 0, len2 = m2[0].length; j < len2; j++) { + let sum = 0; + for (let k = 0, len3 = m1[0].length; k < len3; k++) { + sum += m1[i][k] * m2[k][j]; + } + result[i][j] = sum; + } + } + return result; + } + + function createFilterMatrix(config) { + let m = Matrix.identity(); + if (config.sepia !== 0) { + m = multiplyMatrices(m, Matrix.sepia(config.sepia / 100)); + } + if (config.grayscale !== 0) { + m = multiplyMatrices(m, Matrix.grayscale(config.grayscale / 100)); + } + if (config.contrast !== 100) { + m = multiplyMatrices(m, Matrix.contrast(config.contrast / 100)); + } + if (config.brightness !== 100) { + m = multiplyMatrices(m, Matrix.brightness(config.brightness / 100)); + } + if (config.mode === 1) { + m = multiplyMatrices(m, Matrix.invertNHue()); + } + return m; + } + function applyColorMatrix([r, g, b], matrix) { + const rgb = [[r / 255], [g / 255], [b / 255], [1], [1]]; + const result = multiplyMatrices(matrix, rgb); + return [0, 1, 2].map((i) => + clamp(Math.round(result[i][0] * 255), 0, 255) + ); + } + const Matrix = { + identity() { + return [ + [1, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + invertNHue() { + return [ + [0.333, -0.667, -0.667, 0, 1], + [-0.667, 0.333, -0.667, 0, 1], + [-0.667, -0.667, 0.333, 0, 1], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + brightness(v) { + return [ + [v, 0, 0, 0, 0], + [0, v, 0, 0, 0], + [0, 0, v, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + contrast(v) { + const t = (1 - v) / 2; + return [ + [v, 0, 0, 0, t], + [0, v, 0, 0, t], + [0, 0, v, 0, t], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + sepia(v) { + return [ + [ + 0.393 + 0.607 * (1 - v), + 0.769 - 0.769 * (1 - v), + 0.189 - 0.189 * (1 - v), + 0, + 0 + ], + [ + 0.349 - 0.349 * (1 - v), + 0.686 + 0.314 * (1 - v), + 0.168 - 0.168 * (1 - v), + 0, + 0 + ], + [ + 0.272 - 0.272 * (1 - v), + 0.534 - 0.534 * (1 - v), + 0.131 + 0.869 * (1 - v), + 0, + 0 + ], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + grayscale(v) { + return [ + [ + 0.2126 + 0.7874 * (1 - v), + 0.7152 - 0.7152 * (1 - v), + 0.0722 - 0.0722 * (1 - v), + 0, + 0 + ], + [ + 0.2126 - 0.2126 * (1 - v), + 0.7152 + 0.2848 * (1 - v), + 0.0722 - 0.0722 * (1 - v), + 0, + 0 + ], + [ + 0.2126 - 0.2126 * (1 - v), + 0.7152 - 0.7152 * (1 - v), + 0.0722 + 0.9278 * (1 - v), + 0, + 0 + ], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + } + }; + + var FilterMode; + (function (FilterMode) { + FilterMode[(FilterMode["light"] = 0)] = "light"; + FilterMode[(FilterMode["dark"] = 1)] = "dark"; + })(FilterMode || (FilterMode = {})); + function getCSSFilterValue(config) { + const filters = []; + if (config.mode === FilterMode.dark) { + filters.push("invert(100%) hue-rotate(180deg)"); + } + if (config.brightness !== 100) { + filters.push(`brightness(${config.brightness}%)`); + } + if (config.contrast !== 100) { + filters.push(`contrast(${config.contrast}%)`); + } + if (config.grayscale !== 0) { + filters.push(`grayscale(${config.grayscale}%)`); + } + if (config.sepia !== 0) { + filters.push(`sepia(${config.sepia}%)`); + } + if (filters.length === 0) { + return null; + } + return filters.join(" "); + } + + function evalMath(expression) { + const rpnStack = []; + const workingStack = []; + let lastToken; + for (let i = 0, len = expression.length; i < len; i++) { + const token = expression[i]; + if (!token || token === " ") { + continue; + } + if (operators.has(token)) { + const op = operators.get(token); + while (workingStack.length) { + const currentOp = operators.get(workingStack[0]); + if (!currentOp) { + break; + } + if (op.lessOrEqualThan(currentOp)) { + rpnStack.push(workingStack.shift()); + } else { + break; + } + } + workingStack.unshift(token); + } else if (!lastToken || operators.has(lastToken)) { + rpnStack.push(token); + } else { + rpnStack[rpnStack.length - 1] += token; + } + lastToken = token; + } + rpnStack.push(...workingStack); + const stack = []; + for (let i = 0, len = rpnStack.length; i < len; i++) { + const op = operators.get(rpnStack[i]); + if (op) { + const args = stack.splice(0, 2); + stack.push(op.exec(args[1], args[0])); + } else { + stack.unshift(parseFloat(rpnStack[i])); + } + } + return stack[0]; + } + class Operator { + constructor(precedence, method) { + this.precendce = precedence; + this.execMethod = method; + } + exec(left, right) { + return this.execMethod(left, right); + } + lessOrEqualThan(op) { + return this.precendce <= op.precendce; + } + } + const operators = new Map([ + ["+", new Operator(1, (left, right) => left + right)], + ["-", new Operator(1, (left, right) => left - right)], + ["*", new Operator(2, (left, right) => left * right)], + ["/", new Operator(2, (left, right) => left / right)] + ]); + + const isSystemDarkModeEnabled = () => + matchMedia("(prefers-color-scheme: dark)").matches; + + const hslaParseCache = new Map(); + const rgbaParseCache = new Map(); + function parseColorWithCache($color) { + $color = $color.trim(); + const key = $color; + if (rgbaParseCache.has(key)) { + return rgbaParseCache.get(key); + } + if ($color.includes("calc(")) { + $color = lowerCalcExpression($color); + } + const color = parse($color); + rgbaParseCache.set(key, color); + return color; + } + function parseToHSLWithCache(color) { + if (hslaParseCache.has(color)) { + return hslaParseCache.get(color); + } + const rgb = parseColorWithCache(color); + if (!rgb) { + return null; + } + const hsl = rgbToHSL(rgb); + hslaParseCache.set(color, hsl); + return hsl; + } + function clearColorCache() { + hslaParseCache.clear(); + rgbaParseCache.clear(); + } + function hslToRGB({h, s, l, a = 1}) { + if (s === 0) { + const [r, b, g] = [l, l, l].map((x) => Math.round(x * 255)); + return {r, g, b, a}; + } + const c = (1 - Math.abs(2 * l - 1)) * s; + const x = c * (1 - Math.abs(((h / 60) % 2) - 1)); + const m = l - c / 2; + const [r, g, b] = ( + h < 60 + ? [c, x, 0] + : h < 120 + ? [x, c, 0] + : h < 180 + ? [0, c, x] + : h < 240 + ? [0, x, c] + : h < 300 + ? [x, 0, c] + : [c, 0, x] + ).map((n) => Math.round((n + m) * 255)); + return {r, g, b, a}; + } + function rgbToHSL({r: r255, g: g255, b: b255, a = 1}) { + const r = r255 / 255; + const g = g255 / 255; + const b = b255 / 255; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const c = max - min; + const l = (max + min) / 2; + if (c === 0) { + return {h: 0, s: 0, l, a}; + } + let h = + (max === r + ? ((g - b) / c) % 6 + : max === g + ? (b - r) / c + 2 + : (r - g) / c + 4) * 60; + if (h < 0) { + h += 360; + } + const s = c / (1 - Math.abs(2 * l - 1)); + return {h, s, l, a}; + } + function toFixed(n, digits = 0) { + const fixed = n.toFixed(digits); + if (digits === 0) { + return fixed; + } + const dot = fixed.indexOf("."); + if (dot >= 0) { + const zerosMatch = fixed.match(/0+$/); + if (zerosMatch) { + if (zerosMatch.index === dot + 1) { + return fixed.substring(0, dot); + } + return fixed.substring(0, zerosMatch.index); + } + } + return fixed; + } + function rgbToString(rgb) { + const {r, g, b, a} = rgb; + if (a != null && a < 1) { + return `rgba(${toFixed(r)}, ${toFixed(g)}, ${toFixed(b)}, ${toFixed(a, 2)})`; + } + return `rgb(${toFixed(r)}, ${toFixed(g)}, ${toFixed(b)})`; + } + function rgbToHexString({r, g, b, a}) { + return `#${(a != null && a < 1 + ? [r, g, b, Math.round(a * 255)] + : [r, g, b] + ) + .map((x) => { + return `${x < 16 ? "0" : ""}${x.toString(16)}`; + }) + .join("")}`; + } + function hslToString(hsl) { + const {h, s, l, a} = hsl; + if (a != null && a < 1) { + return `hsla(${toFixed(h)}, ${toFixed(s * 100)}%, ${toFixed(l * 100)}%, ${toFixed(a, 2)})`; + } + return `hsl(${toFixed(h)}, ${toFixed(s * 100)}%, ${toFixed(l * 100)}%)`; + } + const rgbMatch = /^rgba?\([^\(\)]+\)$/; + const hslMatch = /^hsla?\([^\(\)]+\)$/; + const hexMatch = /^#[0-9a-f]+$/i; + const supportedColorFuncs = [ + "color", + "color-mix", + "hwb", + "lab", + "lch", + "oklab", + "oklch" + ]; + function parse($color) { + const c = $color.trim().toLowerCase(); + if (c.includes("(from ")) { + if (c.indexOf("(from") !== c.lastIndexOf("(from")) { + return null; + } + return domParseColor(c); + } + if (c.match(rgbMatch)) { + if (c.startsWith("rgb(#") || c.startsWith("rgba(#")) { + if (c.lastIndexOf("rgb") > 0) { + return null; + } + return domParseColor(c); + } + return parseRGB(c); + } + if (c.match(hslMatch)) { + return parseHSL(c); + } + if (c.match(hexMatch)) { + return parseHex(c); + } + if (knownColors.has(c)) { + return getColorByName(c); + } + if (systemColors.has(c)) { + return getSystemColor(c); + } + if (c === "transparent") { + return {r: 0, g: 0, b: 0, a: 0}; + } + if ( + c.endsWith(")") && + supportedColorFuncs.some( + (fn) => + c.startsWith(fn) && + c[fn.length] === "(" && + c.lastIndexOf(fn) === 0 + ) + ) { + return domParseColor(c); + } + if (c.startsWith("light-dark(") && c.endsWith(")")) { + const match = c.match( + /^light-dark\(\s*([a-z]+(\(.*\))?),\s*([a-z]+(\(.*\))?)\s*\)$/ + ); + if (match) { + const schemeColor = isSystemDarkModeEnabled() + ? match[3] + : match[1]; + return parse(schemeColor); + } + } + return null; + } + const C_0 = "0".charCodeAt(0); + const C_9 = "9".charCodeAt(0); + const C_e = "e".charCodeAt(0); + const C_DOT = ".".charCodeAt(0); + const C_PLUS = "+".charCodeAt(0); + const C_MINUS = "-".charCodeAt(0); + const C_SPACE = " ".charCodeAt(0); + const C_COMMA = ",".charCodeAt(0); + const C_SLASH = "/".charCodeAt(0); + const C_PERCENT = "%".charCodeAt(0); + function getNumbersFromString(input, range, units) { + const numbers = []; + const searchStart = input.indexOf("(") + 1; + const searchEnd = input.length - 1; + let numStart = -1; + let unitStart = -1; + const push = (matchEnd) => { + const numEnd = unitStart > -1 ? unitStart : matchEnd; + const $num = input.slice(numStart, numEnd); + let n = parseFloat($num); + const r = range[numbers.length]; + if (unitStart > -1) { + const unit = input.slice(unitStart, matchEnd); + const u = units[unit]; + if (u != null) { + n *= r / u; + } + } + if (r > 1) { + n = Math.round(n); + } + numbers.push(n); + numStart = -1; + unitStart = -1; + }; + for (let i = searchStart; i < searchEnd; i++) { + const c = input.charCodeAt(i); + const isNumChar = + (c >= C_0 && c <= C_9) || + c === C_DOT || + c === C_PLUS || + c === C_MINUS || + c === C_e; + const isDelimiter = c === C_SPACE || c === C_COMMA || c === C_SLASH; + if (isNumChar) { + if (numStart === -1) { + numStart = i; + } + } else if (numStart > -1) { + if (isDelimiter) { + push(i); + } else if (unitStart === -1) { + unitStart = i; + } + } + } + if (numStart > -1) { + push(searchEnd); + } + return numbers; + } + const rgbRange = [255, 255, 255, 1]; + const rgbUnits = {"%": 100}; + function getRGBValues(input) { + const CHAR_CODE_0 = 48; + const length = input.length; + let i = 0; + let digitsCount = 0; + let digitSequence = false; + let floatDigitsCount = -1; + let delimiter = C_SPACE; + let channel = -1; + let result = null; + while (i < length) { + const c = input.charCodeAt(i); + if ((c >= C_0 && c <= C_9) || c === C_DOT) { + if (!digitSequence) { + digitSequence = true; + digitsCount = 0; + floatDigitsCount = -1; + channel++; + if (channel === 3 && result) { + result[3] = 0; + } + if (channel > 3) { + return null; + } + } + if (c === C_DOT) { + if (floatDigitsCount > 0) { + return null; + } + floatDigitsCount = 0; + } else { + const d = c - CHAR_CODE_0; + if (!result) { + result = [0, 0, 0, 1]; + } + if (floatDigitsCount > -1) { + floatDigitsCount++; + result[channel] += d / 10 ** floatDigitsCount; + } else { + digitsCount++; + if (digitsCount > 3) { + return null; + } + result[channel] = result[channel] * 10 + d; + } + } + } else if (c === C_PERCENT) { + if ( + channel < 0 || + channel > 3 || + delimiter !== C_SPACE || + !result + ) { + return null; + } + result[channel] = + channel < 3 + ? Math.round((result[channel] * 255) / 100) + : result[channel] / 100; + digitSequence = false; + } else { + digitSequence = false; + if (c === C_SPACE) { + if (channel === 0) { + delimiter = c; + } + } else if (c === C_COMMA) { + if (channel === -1) { + return null; + } + delimiter = C_COMMA; + } else if (c === C_SLASH) { + if (channel !== 2 || delimiter !== C_SPACE) { + return null; + } + } else { + return null; + } + } + i++; + } + if (channel < 2 || channel > 3) { + return null; + } + return result; + } + function parseRGB($rgb) { + const [r, g, b, a = 1] = getNumbersFromString($rgb, rgbRange, rgbUnits); + if (r == null || g == null || b == null || a == null) { + return null; + } + return {r, g, b, a}; + } + const hslRange = [360, 1, 1, 1]; + const hslUnits = {"%": 100, "deg": 360, "rad": 2 * Math.PI, "turn": 1}; + function parseHSL($hsl) { + const [h, s, l, a = 1] = getNumbersFromString($hsl, hslRange, hslUnits); + if (h == null || s == null || l == null || a == null) { + return null; + } + return hslToRGB({h, s, l, a}); + } + const C_A = "A".charCodeAt(0); + const C_F = "F".charCodeAt(0); + const C_a = "a".charCodeAt(0); + const C_f = "f".charCodeAt(0); + function parseHex($hex) { + const length = $hex.length; + const digitCount = length - 1; + const isShort = digitCount === 3 || digitCount === 4; + const isLong = digitCount === 6 || digitCount === 8; + if (!isShort && !isLong) { + return null; + } + const hex = (i) => { + const c = $hex.charCodeAt(i); + if (c >= C_A && c <= C_F) { + return c + 10 - C_A; + } + if (c >= C_a && c <= C_f) { + return c + 10 - C_a; + } + return c - C_0; + }; + let r; + let g; + let b; + let a = 1; + if (isShort) { + r = hex(1) * 17; + g = hex(2) * 17; + b = hex(3) * 17; + if (digitCount === 4) { + a = (hex(4) * 17) / 255; + } + } else { + r = hex(1) * 16 + hex(2); + g = hex(3) * 16 + hex(4); + b = hex(5) * 16 + hex(6); + if (digitCount === 8) { + a = (hex(7) * 16 + hex(8)) / 255; + } + } + return {r, g, b, a}; + } + function getColorByName($color) { + const n = knownColors.get($color); + return { + r: (n >> 16) & 255, + g: (n >> 8) & 255, + b: (n >> 0) & 255, + a: 1 + }; + } + function getSystemColor($color) { + const n = systemColors.get($color); + return { + r: (n >> 16) & 255, + g: (n >> 8) & 255, + b: (n >> 0) & 255, + a: 1 + }; + } + function lowerCalcExpression(color) { + let searchIndex = 0; + const replaceBetweenIndices = (start, end, replacement) => { + color = + color.substring(0, start) + replacement + color.substring(end); + }; + while ((searchIndex = color.indexOf("calc(")) !== -1) { + const range = getParenthesesRange(color, searchIndex); + if (!range) { + break; + } + let slice = color.slice(range.start + 1, range.end - 1); + const includesPercentage = slice.includes("%"); + slice = slice.split("%").join(""); + const output = Math.round(evalMath(slice)); + replaceBetweenIndices( + range.start - 4, + range.end, + output + (includesPercentage ? "%" : "") + ); + } + return color; + } + const knownColors = new Map( + Object.entries({ + aliceblue: 0xf0f8ff, + antiquewhite: 0xfaebd7, + aqua: 0x00ffff, + aquamarine: 0x7fffd4, + azure: 0xf0ffff, + beige: 0xf5f5dc, + bisque: 0xffe4c4, + black: 0x000000, + blanchedalmond: 0xffebcd, + blue: 0x0000ff, + blueviolet: 0x8a2be2, + brown: 0xa52a2a, + burlywood: 0xdeb887, + cadetblue: 0x5f9ea0, + chartreuse: 0x7fff00, + chocolate: 0xd2691e, + coral: 0xff7f50, + cornflowerblue: 0x6495ed, + cornsilk: 0xfff8dc, + crimson: 0xdc143c, + cyan: 0x00ffff, + darkblue: 0x00008b, + darkcyan: 0x008b8b, + darkgoldenrod: 0xb8860b, + darkgray: 0xa9a9a9, + darkgrey: 0xa9a9a9, + darkgreen: 0x006400, + darkkhaki: 0xbdb76b, + darkmagenta: 0x8b008b, + darkolivegreen: 0x556b2f, + darkorange: 0xff8c00, + darkorchid: 0x9932cc, + darkred: 0x8b0000, + darksalmon: 0xe9967a, + darkseagreen: 0x8fbc8f, + darkslateblue: 0x483d8b, + darkslategray: 0x2f4f4f, + darkslategrey: 0x2f4f4f, + darkturquoise: 0x00ced1, + darkviolet: 0x9400d3, + deeppink: 0xff1493, + deepskyblue: 0x00bfff, + dimgray: 0x696969, + dimgrey: 0x696969, + dodgerblue: 0x1e90ff, + firebrick: 0xb22222, + floralwhite: 0xfffaf0, + forestgreen: 0x228b22, + fuchsia: 0xff00ff, + gainsboro: 0xdcdcdc, + ghostwhite: 0xf8f8ff, + gold: 0xffd700, + goldenrod: 0xdaa520, + gray: 0x808080, + grey: 0x808080, + green: 0x008000, + greenyellow: 0xadff2f, + honeydew: 0xf0fff0, + hotpink: 0xff69b4, + indianred: 0xcd5c5c, + indigo: 0x4b0082, + ivory: 0xfffff0, + khaki: 0xf0e68c, + lavender: 0xe6e6fa, + lavenderblush: 0xfff0f5, + lawngreen: 0x7cfc00, + lemonchiffon: 0xfffacd, + lightblue: 0xadd8e6, + lightcoral: 0xf08080, + lightcyan: 0xe0ffff, + lightgoldenrodyellow: 0xfafad2, + lightgray: 0xd3d3d3, + lightgrey: 0xd3d3d3, + lightgreen: 0x90ee90, + lightpink: 0xffb6c1, + lightsalmon: 0xffa07a, + lightseagreen: 0x20b2aa, + lightskyblue: 0x87cefa, + lightslategray: 0x778899, + lightslategrey: 0x778899, + lightsteelblue: 0xb0c4de, + lightyellow: 0xffffe0, + lime: 0x00ff00, + limegreen: 0x32cd32, + linen: 0xfaf0e6, + magenta: 0xff00ff, + maroon: 0x800000, + mediumaquamarine: 0x66cdaa, + mediumblue: 0x0000cd, + mediumorchid: 0xba55d3, + mediumpurple: 0x9370db, + mediumseagreen: 0x3cb371, + mediumslateblue: 0x7b68ee, + mediumspringgreen: 0x00fa9a, + mediumturquoise: 0x48d1cc, + mediumvioletred: 0xc71585, + midnightblue: 0x191970, + mintcream: 0xf5fffa, + mistyrose: 0xffe4e1, + moccasin: 0xffe4b5, + navajowhite: 0xffdead, + navy: 0x000080, + oldlace: 0xfdf5e6, + olive: 0x808000, + olivedrab: 0x6b8e23, + orange: 0xffa500, + orangered: 0xff4500, + orchid: 0xda70d6, + palegoldenrod: 0xeee8aa, + palegreen: 0x98fb98, + paleturquoise: 0xafeeee, + palevioletred: 0xdb7093, + papayawhip: 0xffefd5, + peachpuff: 0xffdab9, + peru: 0xcd853f, + pink: 0xffc0cb, + plum: 0xdda0dd, + powderblue: 0xb0e0e6, + purple: 0x800080, + rebeccapurple: 0x663399, + red: 0xff0000, + rosybrown: 0xbc8f8f, + royalblue: 0x4169e1, + saddlebrown: 0x8b4513, + salmon: 0xfa8072, + sandybrown: 0xf4a460, + seagreen: 0x2e8b57, + seashell: 0xfff5ee, + sienna: 0xa0522d, + silver: 0xc0c0c0, + skyblue: 0x87ceeb, + slateblue: 0x6a5acd, + slategray: 0x708090, + slategrey: 0x708090, + snow: 0xfffafa, + springgreen: 0x00ff7f, + steelblue: 0x4682b4, + tan: 0xd2b48c, + teal: 0x008080, + thistle: 0xd8bfd8, + tomato: 0xff6347, + turquoise: 0x40e0d0, + violet: 0xee82ee, + wheat: 0xf5deb3, + white: 0xffffff, + whitesmoke: 0xf5f5f5, + yellow: 0xffff00, + yellowgreen: 0x9acd32 + }) + ); + const systemColors = new Map( + Object.entries({ + "ActiveBorder": 0x3b99fc, + "ActiveCaption": 0x000000, + "AppWorkspace": 0xaaaaaa, + "Background": 0x6363ce, + "ButtonFace": 0xffffff, + "ButtonHighlight": 0xe9e9e9, + "ButtonShadow": 0x9fa09f, + "ButtonText": 0x000000, + "CaptionText": 0x000000, + "GrayText": 0x7f7f7f, + "Highlight": 0xb2d7ff, + "HighlightText": 0x000000, + "InactiveBorder": 0xffffff, + "InactiveCaption": 0xffffff, + "InactiveCaptionText": 0x000000, + "InfoBackground": 0xfbfcc5, + "InfoText": 0x000000, + "Menu": 0xf6f6f6, + "MenuText": 0xffffff, + "Scrollbar": 0xaaaaaa, + "ThreeDDarkShadow": 0x000000, + "ThreeDFace": 0xc0c0c0, + "ThreeDHighlight": 0xffffff, + "ThreeDLightShadow": 0xffffff, + "ThreeDShadow": 0x000000, + "Window": 0xececec, + "WindowFrame": 0xaaaaaa, + "WindowText": 0x000000, + "-webkit-focus-ring-color": 0xe59700 + }).map(([key, value]) => [key.toLowerCase(), value]) + ); + function getSRGBLightness(r, g, b) { + return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255; + } + let canvas$1; + let context$1; + function domParseColor($color) { + if (!context$1) { + canvas$1 = document.createElement("canvas"); + canvas$1.width = 1; + canvas$1.height = 1; + context$1 = canvas$1.getContext("2d", {willReadFrequently: true}); + } + context$1.fillStyle = $color; + context$1.fillRect(0, 0, 1, 1); + const d = context$1.getImageData(0, 0, 1, 1).data; + const color = `rgba(${d[0]}, ${d[1]}, ${d[2]}, ${(d[3] / 255).toFixed(2)})`; + return parseRGB(color); + } + + function throttle(callback) { + let pending = false; + let frameId = null; + let lastArgs; + const throttled = (...args) => { + lastArgs = args; + if (frameId) { + pending = true; + } else { + callback(...lastArgs); + frameId = requestAnimationFrame(() => { + frameId = null; + if (pending) { + callback(...lastArgs); + pending = false; + } + }); + } + }; + const cancel = () => { + cancelAnimationFrame(frameId); + pending = false; + frameId = null; + }; + return Object.assign(throttled, {cancel}); + } + function createAsyncTasksQueue() { + const tasks = []; + let frameId = null; + function runTasks() { + let task; + while ((task = tasks.shift())) { + task(); + } + frameId = null; + } + function add(task) { + tasks.push(task); + if (!frameId) { + frameId = requestAnimationFrame(runTasks); + } + } + function cancel() { + tasks.splice(0); + cancelAnimationFrame(frameId); + frameId = null; + } + return {add, cancel}; + } + + function hexify(number) { + return (number < 16 ? "0" : "") + number.toString(16); + } + function generateUID() { + if ("randomUUID" in crypto) { + const uuid = crypto.randomUUID(); + return ( + uuid.substring(0, 8) + + uuid.substring(9, 13) + + uuid.substring(14, 18) + + uuid.substring(19, 23) + + uuid.substring(24) + ); + } + if ("getRandomValues" in crypto) { + return Array.from(crypto.getRandomValues(new Uint8Array(16))) + .map((x) => hexify(x)) + .join(""); + } + return Math.floor(Math.random() * 2 ** 55).toString(36); + } + + let documentVisibilityListener = null; + let documentIsVisible_ = !document.hidden; + const listenerOptions = { + capture: true, + passive: true + }; + function watchForDocumentVisibility() { + document.addEventListener( + "visibilitychange", + documentVisibilityListener, + listenerOptions + ); + window.addEventListener( + "pageshow", + documentVisibilityListener, + listenerOptions + ); + window.addEventListener( + "focus", + documentVisibilityListener, + listenerOptions + ); + } + function stopWatchingForDocumentVisibility() { + document.removeEventListener( + "visibilitychange", + documentVisibilityListener, + listenerOptions + ); + window.removeEventListener( + "pageshow", + documentVisibilityListener, + listenerOptions + ); + window.removeEventListener( + "focus", + documentVisibilityListener, + listenerOptions + ); + } + function setDocumentVisibilityListener(callback) { + const alreadyWatching = Boolean(documentVisibilityListener); + documentVisibilityListener = () => { + if (!document.hidden) { + removeDocumentVisibilityListener(); + callback(); + documentIsVisible_ = true; + } + }; + if (!alreadyWatching) { + watchForDocumentVisibility(); + } + } + function removeDocumentVisibilityListener() { + stopWatchingForDocumentVisibility(); + documentVisibilityListener = null; + } + function documentIsVisible() { + return documentIsVisible_; + } + + function getDuration(time) { + let duration = 0; + if (time.seconds) { + duration += time.seconds * 1000; + } + if (time.minutes) { + duration += time.minutes * 60 * 1000; + } + if (time.hours) { + duration += time.hours * 60 * 60 * 1000; + } + if (time.days) { + duration += time.days * 24 * 60 * 60 * 1000; + } + return duration; + } + + function logInfo(...args) {} + function logWarn(...args) {} + + function removeNode(node) { + node && node.parentNode && node.parentNode.removeChild(node); + } + function watchForNodePosition(node, mode, onRestore = Function.prototype) { + const MAX_ATTEMPTS_COUNT = 10; + const RETRY_TIMEOUT = getDuration({seconds: 2}); + const ATTEMPTS_INTERVAL = getDuration({seconds: 10}); + let prevSibling = node.previousSibling; + let parent = node.parentNode; + if (!parent) { + throw new Error( + "Unable to watch for node position: parent element not found" + ); + } + if (mode === "prev-sibling" && !prevSibling) { + throw new Error( + "Unable to watch for node position: there is no previous sibling" + ); + } + let attempts = 0; + let start = null; + let timeoutId = null; + const restore = throttle(() => { + if (timeoutId) { + return; + } + attempts++; + const now = Date.now(); + if (start == null) { + start = now; + } else if (attempts >= MAX_ATTEMPTS_COUNT) { + if (now - start < ATTEMPTS_INTERVAL) { + logWarn( + `Node position watcher paused: retry in ${RETRY_TIMEOUT}ms`, + node, + prevSibling + ); + timeoutId = setTimeout(() => { + start = null; + attempts = 0; + timeoutId = null; + restore(); + }, RETRY_TIMEOUT); + return; + } + start = now; + attempts = 1; + } + if (mode === "head") { + if (prevSibling && prevSibling.parentNode !== parent) { + logWarn( + "Sibling moved, moving node to the head end", + node, + prevSibling, + parent + ); + prevSibling = document.head.lastChild; + } + } + if (mode === "prev-sibling") { + if (prevSibling.parentNode == null) { + logWarn( + "Unable to restore node position: sibling was removed", + node, + prevSibling, + parent + ); + stop(); + return; + } + if (prevSibling.parentNode !== parent) { + logWarn( + "Style was moved to another parent", + node, + prevSibling, + parent + ); + updateParent(prevSibling.parentNode); + } + } + if (mode === "head" && !parent.isConnected) { + parent = document.head; + } + logWarn("Restoring node position", node, prevSibling, parent); + parent.insertBefore( + node, + prevSibling && prevSibling.isConnected + ? prevSibling.nextSibling + : parent.firstChild + ); + observer.takeRecords(); + onRestore && onRestore(); + }); + const observer = new MutationObserver(() => { + if ( + (mode === "head" && + (node.parentNode !== parent || + !node.parentNode.isConnected)) || + (mode === "prev-sibling" && + node.previousSibling !== prevSibling) + ) { + restore(); + } + }); + const run = () => { + observer.observe(parent, {childList: true}); + }; + const stop = () => { + clearTimeout(timeoutId); + observer.disconnect(); + restore.cancel(); + }; + const skip = () => { + observer.takeRecords(); + }; + const updateParent = (parentNode) => { + parent = parentNode; + stop(); + run(); + }; + run(); + return {run, stop, skip}; + } + function iterateShadowHosts(root, iterator) { + if (root == null) { + return; + } + const walker = document.createTreeWalker( + root, + NodeFilter.SHOW_ELEMENT, + { + acceptNode(node) { + return node.shadowRoot == null + ? NodeFilter.FILTER_SKIP + : NodeFilter.FILTER_ACCEPT; + } + } + ); + for ( + let node = root.shadowRoot ? walker.currentNode : walker.nextNode(); + node != null; + node = walker.nextNode() + ) { + if (node.classList.contains("surfingkeys_hints_host")) { + continue; + } + iterator(node); + iterateShadowHosts(node.shadowRoot, iterator); + } + } + let isDOMReady = () => { + return ( + document.readyState === "complete" || + document.readyState === "interactive" + ); + }; + function setIsDOMReady(newFunc) { + isDOMReady = newFunc; + } + const readyStateListeners = new Set(); + function addDOMReadyListener(listener) { + isDOMReady() ? listener() : readyStateListeners.add(listener); + } + function removeDOMReadyListener(listener) { + readyStateListeners.delete(listener); + } + function isReadyStateComplete() { + return document.readyState === "complete"; + } + const readyStateCompleteListeners = new Set(); + function addReadyStateCompleteListener(listener) { + isReadyStateComplete() + ? listener() + : readyStateCompleteListeners.add(listener); + } + function cleanReadyStateCompleteListeners() { + readyStateCompleteListeners.clear(); + } + if (!isDOMReady()) { + const onReadyStateChange = () => { + if (isDOMReady()) { + readyStateListeners.forEach((listener) => listener()); + readyStateListeners.clear(); + if (isReadyStateComplete()) { + document.removeEventListener( + "readystatechange", + onReadyStateChange + ); + readyStateCompleteListeners.forEach((listener) => + listener() + ); + readyStateCompleteListeners.clear(); + } + } + }; + document.addEventListener("readystatechange", onReadyStateChange); + } + const HUGE_MUTATIONS_COUNT = 1000; + function isHugeMutation(mutations) { + if (mutations.length > HUGE_MUTATIONS_COUNT) { + return true; + } + let addedNodesCount = 0; + for (let i = 0; i < mutations.length; i++) { + addedNodesCount += mutations[i].addedNodes.length; + if (addedNodesCount > HUGE_MUTATIONS_COUNT) { + return true; + } + } + return false; + } + function getElementsTreeOperations(mutations) { + const additions = new Set(); + const deletions = new Set(); + const moves = new Set(); + mutations.forEach((m) => { + forEach(m.addedNodes, (n) => { + if (n instanceof Element && n.isConnected) { + additions.add(n); + } + }); + forEach(m.removedNodes, (n) => { + if (n instanceof Element) { + if (n.isConnected) { + moves.add(n); + additions.delete(n); + } else { + deletions.add(n); + } + } + }); + }); + const duplicateAdditions = []; + const duplicateDeletions = []; + additions.forEach((node) => { + if (additions.has(node.parentElement)) { + duplicateAdditions.push(node); + } + }); + deletions.forEach((node) => { + if (deletions.has(node.parentElement)) { + duplicateDeletions.push(node); + } + }); + duplicateAdditions.forEach((node) => additions.delete(node)); + duplicateDeletions.forEach((node) => deletions.delete(node)); + return {additions, moves, deletions}; + } + const optimizedTreeObservers = new Map(); + const optimizedTreeCallbacks = new WeakMap(); + function createOptimizedTreeObserver(root, callbacks) { + let observer; + let observerCallbacks; + let domReadyListener; + if (optimizedTreeObservers.has(root)) { + observer = optimizedTreeObservers.get(root); + observerCallbacks = optimizedTreeCallbacks.get(observer); + } else { + let hadHugeMutationsBefore = false; + let subscribedForReadyState = false; + observer = new MutationObserver((mutations) => { + if (isHugeMutation(mutations)) { + if (!hadHugeMutationsBefore || isDOMReady()) { + observerCallbacks.forEach(({onHugeMutations}) => + onHugeMutations(root) + ); + } else if (!subscribedForReadyState) { + domReadyListener = () => + observerCallbacks.forEach(({onHugeMutations}) => + onHugeMutations(root) + ); + addDOMReadyListener(domReadyListener); + subscribedForReadyState = true; + } + hadHugeMutationsBefore = true; + } else { + const elementsOperations = + getElementsTreeOperations(mutations); + observerCallbacks.forEach(({onMinorMutations}) => + onMinorMutations(root, elementsOperations) + ); + } + }); + observer.observe(root, {childList: true, subtree: true}); + optimizedTreeObservers.set(root, observer); + observerCallbacks = new Set(); + optimizedTreeCallbacks.set(observer, observerCallbacks); + } + observerCallbacks.add(callbacks); + return { + disconnect() { + observerCallbacks.delete(callbacks); + if (domReadyListener) { + removeDOMReadyListener(domReadyListener); + } + if (observerCallbacks.size === 0) { + observer.disconnect(); + optimizedTreeCallbacks.delete(observer); + optimizedTreeObservers.delete(root); + } + } + }; + } + + function iterateCSSRules( + rules, + iterate, + onImportError, + importedSheets = new Set() + ) { + forEach(rules, (rule) => { + if (isStyleRule(rule)) { + iterate(rule); + if (rule.cssRules?.length > 0) { + iterateCSSRules( + rule.cssRules, + iterate, + onImportError, + importedSheets + ); + } + } else if (isImportRule(rule)) { + try { + const importedSheet = rule.styleSheet; + if (!importedSheets.has(importedSheet)) { + importedSheets.add(importedSheet); + iterateCSSRules( + importedSheet.cssRules, + iterate, + onImportError, + importedSheets + ); + } + } catch (err) { + onImportError?.(); + } + } else if (isMediaRule(rule)) { + const media = Array.from(rule.media); + const isScreenOrAllOrQuery = media.some( + (m) => + m.startsWith("screen") || + m.startsWith("all") || + m.startsWith("(") + ); + const isNotScreen = + !isScreenOrAllOrQuery && + media.some((m) => + ignoredMedia.some((i) => m.startsWith(i)) + ); + if (isScreenOrAllOrQuery || !isNotScreen) { + iterateCSSRules( + rule.cssRules, + iterate, + onImportError, + importedSheets + ); + } + } else if (isSupportsRule(rule)) { + if (CSS.supports(rule.conditionText)) { + iterateCSSRules( + rule.cssRules, + iterate, + onImportError, + importedSheets + ); + } + } else if (isLayerRule(rule)) { + iterateCSSRules( + rule.cssRules, + iterate, + onImportError, + importedSheets + ); + } else if (isContainerRule(rule)) { + iterateCSSRules( + rule.cssRules, + iterate, + onImportError, + importedSheets + ); + } else { + logWarn(`CSSRule type not supported`, rule); + } + }); + } + const ignoredMedia = [ + "aural", + "braille", + "embossed", + "handheld", + "print", + "projection", + "speech", + "tty", + "tv" + ]; + const shorthandVarDependantProperties = [ + "background", + "border", + "border-color", + "border-bottom", + "border-left", + "border-right", + "border-top", + "outline", + "outline-color" + ]; + const shorthandVarDepPropRegexps = isSafari + ? shorthandVarDependantProperties.map((prop) => { + const regexp = new RegExp(`${prop}:\\s*(.*?)\\s*;`); + return [prop, regexp]; + }) + : null; + function iterateCSSDeclarations(style, iterate) { + const cssText = style.cssText; + if (cssText.includes("var(")) { + if (isSafari) { + shorthandVarDepPropRegexps.forEach(([prop, regexp]) => { + const match = cssText.match(regexp); + if (match && match[1]) { + const val = match[1].trim(); + iterate(prop, val); + } + }); + } else { + shorthandVarDependantProperties.forEach((prop) => { + const val = style.getPropertyValue(prop); + if (val && val.includes("var(")) { + iterate(prop, val); + } + }); + } + } + if ( + (cssText.includes("background-color: ;") || + cssText.includes("background-image: ;")) && + !style.getPropertyValue("background") + ) { + handleEmptyShorthand("background", style, iterate); + } + if ( + cssText.includes("border-") && + cssText.includes("-color: ;") && + !style.getPropertyValue("border") + ) { + handleEmptyShorthand("border", style, iterate); + } + forEach(style, (property) => { + const value = style.getPropertyValue(property).trim(); + if (!value) { + return; + } + iterate(property, value); + }); + } + function handleEmptyShorthand(shorthand, style, iterate) { + const parentRule = style.parentRule; + if (isStyleRule(parentRule)) { + const sourceCSSText = + parentRule.parentStyleSheet?.ownerNode?.textContent; + if (sourceCSSText) { + let escapedSelector = escapeRegExpSpecialChars( + parentRule.selectorText + ); + escapedSelector = escapedSelector.replaceAll(/\s+/g, "\\s*"); + escapedSelector = escapedSelector.replaceAll(/::/g, "::?"); + const regexp = new RegExp( + `${escapedSelector}\\s*{[^}]*${shorthand}:\\s*([^;}]+)` + ); + const match = sourceCSSText.match(regexp); + if (match) { + iterate(shorthand, match[1]); + } + } else if (shorthand === "background") { + iterate("background-color", "#ffffff"); + iterate("background-image", "none"); + } + } + } + const cssURLRegex = /url\((('.*?')|(".*?")|([^\)]*?))\)/g; + const cssImportRegex = + /@import\s*(url\()?(('.+?')|(".+?")|([^\)]*?))\)? ?(screen)?;?/gi; + function getCSSURLValue(cssURL) { + return cssURL + .trim() + .replace(/[\n\r\\]+/g, "") + .replace(/^url\((.*)\)$/, "$1") + .trim() + .replace(/^"(.*)"$/, "$1") + .replace(/^'(.*)'$/, "$1") + .replace(/(?:\\(.))/g, "$1"); + } + function getCSSBaseBath(url) { + const cssURL = parseURL(url); + return `${cssURL.origin}${cssURL.pathname.replace(/\?.*$/, "").replace(/(\/)([^\/]+)$/i, "$1")}`; + } + function replaceCSSRelativeURLsWithAbsolute($css, cssBasePath) { + return $css.replace(cssURLRegex, (match) => { + try { + const url = getCSSURLValue(match); + const absoluteURL = getAbsoluteURL(cssBasePath, url); + const escapedURL = absoluteURL.replaceAll("'", "\\'"); + return `url('${escapedURL}')`; + } catch (err) { + logWarn( + "Not able to replace relative URL with Absolute URL, skipping" + ); + return match; + } + }); + } + const fontFaceRegex = /@font-face\s*{[^}]*}/g; + function replaceCSSFontFace($css) { + return $css.replace(fontFaceRegex, ""); + } + const styleRules = new WeakSet(); + const importRules = new WeakSet(); + const mediaRules = new WeakSet(); + const supportsRules = new WeakSet(); + const layerRules = new WeakSet(); + const containerRules = new WeakSet(); + function isStyleRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return true; + } + if (rule.selectorText) { + styleRules.add(rule); + return true; + } + return false; + } + function isImportRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return false; + } + if (importRules.has(rule)) { + return true; + } + if (rule.href) { + importRules.add(rule); + return true; + } + return false; + } + function isMediaRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return false; + } + if (mediaRules.has(rule)) { + return true; + } + if (rule.media) { + mediaRules.add(rule); + return true; + } + return false; + } + function isSupportsRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return false; + } + if (supportsRules.has(rule)) { + return true; + } + if (rule instanceof CSSSupportsRule) { + supportsRules.add(rule); + return true; + } + return false; + } + function isLayerRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return false; + } + if (layerRules.has(rule)) { + return true; + } + if (isLayerRuleSupported && rule instanceof CSSLayerBlockRule) { + layerRules.add(rule); + return true; + } + return false; + } + function isContainerRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return false; + } + if (containerRules.has(rule)) { + return true; + } + if (isContainerRuleSupported && rule instanceof CSSContainerRule) { + containerRules.add(rule); + return true; + } + return false; + } + + const sheetsScopes = new WeakMap(); + function defineSheetScope(sheet, node) { + sheetsScopes.set(sheet, node); + } + function getSheetScope(sheet) { + if (!sheet.ownerNode) { + return null; + } + if (sheetsScopes.has(sheet)) { + return sheetsScopes.get(sheet); + } + let node = sheet.ownerNode; + while (node) { + if (node instanceof ShadowRoot || node instanceof Document) { + defineSheetScope(sheet, node); + return node; + } + node = node.parentNode; + } + return null; + } + + let variablesSheet; + const registeredColors = new Map(); + function registerVariablesSheet(sheet) { + variablesSheet = sheet; + const types = ["background", "text", "border"]; + registeredColors.forEach((registered) => { + types.forEach((type) => { + if (registered[type]) { + const {variable, value} = registered[type]; + variablesSheet?.cssRules[0]?.style.setProperty( + variable, + value + ); + } + }); + }); + } + function releaseVariablesSheet() { + variablesSheet = null; + clearColorPalette(); + } + function getRegisteredVariableValue(type, registered) { + return `var(${registered[type].variable}, ${registered[type].value})`; + } + function getRegisteredColor(type, parsed) { + const hex = rgbToHexString(parsed); + const registered = registeredColors.get(hex); + if (registered?.[type]) { + return getRegisteredVariableValue(type, registered); + } + return null; + } + function registerColor(type, parsed, value) { + const hex = rgbToHexString(parsed); + let registered; + if (registeredColors.has(hex)) { + registered = registeredColors.get(hex); + } else { + const parsed = parseColorWithCache(hex); + registered = {parsed}; + registeredColors.set(hex, registered); + } + const variable = `--darkreader-${type}-${hex.replace("#", "")}`; + registered[type] = {variable, value}; + if (variablesSheet?.cssRules[0]?.style) { + (variablesSheet?.cssRules[0]).style.setProperty(variable, value); + } + return getRegisteredVariableValue(type, registered); + } + function getColorPalette() { + const background = []; + const border = []; + const text = []; + registeredColors.forEach((registered) => { + if (registered.background) { + background.push(registered.parsed); + } + if (registered.border) { + border.push(registered.parsed); + } + if (registered.text) { + text.push(registered.parsed); + } + }); + return {background, border, text}; + } + function clearColorPalette() { + registeredColors.clear(); + } + + function getBgPole(theme) { + const isDarkScheme = theme.mode === 1; + const prop = isDarkScheme + ? "darkSchemeBackgroundColor" + : "lightSchemeBackgroundColor"; + return theme[prop]; + } + function getFgPole(theme) { + const isDarkScheme = theme.mode === 1; + const prop = isDarkScheme + ? "darkSchemeTextColor" + : "lightSchemeTextColor"; + return theme[prop]; + } + const colorModificationCache = new Map(); + function clearColorModificationCache() { + colorModificationCache.clear(); + } + const rgbCacheKeys = ["r", "g", "b", "a"]; + const themeCacheKeys = [ + "mode", + "brightness", + "contrast", + "grayscale", + "sepia", + "darkSchemeBackgroundColor", + "darkSchemeTextColor", + "lightSchemeBackgroundColor", + "lightSchemeTextColor" + ]; + function getCacheId(rgb, theme, poleA, poleB) { + let resultId = ""; + rgbCacheKeys.forEach((key) => { + resultId += `${rgb[key]};`; + }); + themeCacheKeys.forEach((key) => { + resultId += `${theme[key]};`; + }); + resultId += `${poleA};${poleB}`; + return resultId; + } + function modifyColorWithCache( + rgb, + theme, + modifyHSL, + poleColor, + anotherPoleColor + ) { + let fnCache; + if (colorModificationCache.has(modifyHSL)) { + fnCache = colorModificationCache.get(modifyHSL); + } else { + fnCache = new Map(); + colorModificationCache.set(modifyHSL, fnCache); + } + const id = getCacheId(rgb, theme, poleColor, anotherPoleColor); + if (fnCache.has(id)) { + return fnCache.get(id); + } + const hsl = rgbToHSL(rgb); + const pole = poleColor == null ? null : parseToHSLWithCache(poleColor); + const anotherPole = + anotherPoleColor == null + ? null + : parseToHSLWithCache(anotherPoleColor); + const modified = modifyHSL(hsl, pole, anotherPole); + const {r, g, b, a} = hslToRGB(modified); + const matrix = createFilterMatrix({...theme, mode: 0}); + const [rf, gf, bf] = applyColorMatrix([r, g, b], matrix); + const color = + a === 1 + ? rgbToHexString({r: rf, g: gf, b: bf}) + : rgbToString({r: rf, g: gf, b: bf, a}); + fnCache.set(id, color); + return color; + } + function modifyAndRegisterColor(type, rgb, theme, modifier) { + const registered = getRegisteredColor(type, rgb); + if (registered) { + return registered; + } + const value = modifier(rgb, theme); + return registerColor(type, rgb, value); + } + function modifyLightSchemeColor(rgb, theme) { + const poleBg = getBgPole(theme); + const poleFg = getFgPole(theme); + return modifyColorWithCache( + rgb, + theme, + modifyLightModeHSL, + poleFg, + poleBg + ); + } + function modifyLightModeHSL({h, s, l, a}, poleFg, poleBg) { + const isDark = l < 0.5; + let isNeutral; + if (isDark) { + isNeutral = l < 0.2 || s < 0.12; + } else { + const isBlue = h > 200 && h < 280; + isNeutral = s < 0.24 || (l > 0.8 && isBlue); + } + let hx = h; + let sx = s; + if (isNeutral) { + if (isDark) { + hx = poleFg.h; + sx = poleFg.s; + } else { + hx = poleBg.h; + sx = poleBg.s; + } + } + const lx = scale(l, 0, 1, poleFg.l, poleBg.l); + return {h: hx, s: sx, l: lx, a}; + } + const MAX_BG_LIGHTNESS = 0.4; + function modifyBgHSL({h, s, l, a}, pole) { + const isDark = l < 0.5; + const isBlue = h > 200 && h < 280; + const isNeutral = s < 0.12 || (l > 0.8 && isBlue); + if (isDark) { + const lx = scale(l, 0, 0.5, 0, MAX_BG_LIGHTNESS); + if (isNeutral) { + const hx = pole.h; + const sx = pole.s; + return {h: hx, s: sx, l: lx, a}; + } + return {h, s, l: lx, a}; + } + let lx = scale(l, 0.5, 1, MAX_BG_LIGHTNESS, pole.l); + if (isNeutral) { + const hx = pole.h; + const sx = pole.s; + return {h: hx, s: sx, l: lx, a}; + } + let hx = h; + const isYellow = h > 60 && h < 180; + if (isYellow) { + const isCloserToGreen = h > 120; + if (isCloserToGreen) { + hx = scale(h, 120, 180, 135, 180); + } else { + hx = scale(h, 60, 120, 60, 105); + } + } + if (hx > 40 && hx < 80) { + lx *= 0.75; + } + return {h: hx, s, l: lx, a}; + } + function _modifyBackgroundColor(rgb, theme) { + if (theme.mode === 0) { + return modifyLightSchemeColor(rgb, theme); + } + const pole = getBgPole(theme); + return modifyColorWithCache(rgb, theme, modifyBgHSL, pole); + } + function modifyBackgroundColor( + rgb, + theme, + shouldRegisterColorVariable = true + ) { + if (!shouldRegisterColorVariable) { + return _modifyBackgroundColor(rgb, theme); + } + return modifyAndRegisterColor( + "background", + rgb, + theme, + _modifyBackgroundColor + ); + } + const MIN_FG_LIGHTNESS = 0.55; + function modifyBlueFgHue(hue) { + return scale(hue, 205, 245, 205, 220); + } + function modifyFgHSL({h, s, l, a}, pole) { + const isLight = l > 0.5; + const isNeutral = l < 0.2 || s < 0.24; + const isBlue = !isNeutral && h > 205 && h < 245; + if (isLight) { + const lx = scale(l, 0.5, 1, MIN_FG_LIGHTNESS, pole.l); + if (isNeutral) { + const hx = pole.h; + const sx = pole.s; + return {h: hx, s: sx, l: lx, a}; + } + let hx = h; + if (isBlue) { + hx = modifyBlueFgHue(h); + } + return {h: hx, s, l: lx, a}; + } + if (isNeutral) { + const hx = pole.h; + const sx = pole.s; + const lx = scale(l, 0, 0.5, pole.l, MIN_FG_LIGHTNESS); + return {h: hx, s: sx, l: lx, a}; + } + let hx = h; + let lx; + if (isBlue) { + hx = modifyBlueFgHue(h); + lx = scale(l, 0, 0.5, pole.l, Math.min(1, MIN_FG_LIGHTNESS + 0.05)); + } else { + lx = scale(l, 0, 0.5, pole.l, MIN_FG_LIGHTNESS); + } + return {h: hx, s, l: lx, a}; + } + function _modifyForegroundColor(rgb, theme) { + if (theme.mode === 0) { + return modifyLightSchemeColor(rgb, theme); + } + const pole = getFgPole(theme); + return modifyColorWithCache(rgb, theme, modifyFgHSL, pole); + } + function modifyForegroundColor( + rgb, + theme, + shouldRegisterColorVariable = true + ) { + if (!shouldRegisterColorVariable) { + return _modifyForegroundColor(rgb, theme); + } + return modifyAndRegisterColor( + "text", + rgb, + theme, + _modifyForegroundColor + ); + } + function modifyBorderHSL({h, s, l, a}, poleFg, poleBg) { + const isDark = l < 0.5; + const isNeutral = l < 0.2 || s < 0.24; + let hx = h; + let sx = s; + if (isNeutral) { + if (isDark) { + hx = poleFg.h; + sx = poleFg.s; + } else { + hx = poleBg.h; + sx = poleBg.s; + } + } + const lx = scale(l, 0, 1, 0.5, 0.2); + return {h: hx, s: sx, l: lx, a}; + } + function _modifyBorderColor(rgb, theme) { + if (theme.mode === 0) { + return modifyLightSchemeColor(rgb, theme); + } + const poleFg = getFgPole(theme); + const poleBg = getBgPole(theme); + return modifyColorWithCache( + rgb, + theme, + modifyBorderHSL, + poleFg, + poleBg + ); + } + function modifyBorderColor(rgb, theme, shouldRegisterColorVariable = true) { + if (!shouldRegisterColorVariable) { + return _modifyBorderColor(rgb, theme); + } + return modifyAndRegisterColor("border", rgb, theme, _modifyBorderColor); + } + function modifyShadowColor(rgb, theme) { + return modifyBackgroundColor(rgb, theme); + } + function modifyGradientColor(rgb, theme) { + return modifyBackgroundColor(rgb, theme); + } + + const gradientLength = "gradient".length; + const conicGradient = "conic-"; + const conicGradientLength = conicGradient.length; + const radialGradient = "radial-"; + const linearGradient = "linear-"; + function parseGradient(value) { + const result = []; + let index = 0; + let startIndex = conicGradient.length; + while ((index = value.indexOf("gradient", startIndex)) !== -1) { + let typeGradient; + [linearGradient, radialGradient, conicGradient].find( + (possibleType) => { + if (index - possibleType.length >= 0) { + const possibleGradient = value.substring( + index - possibleType.length, + index + ); + if (possibleGradient === possibleType) { + if ( + value.slice( + index - possibleType.length - 10, + index - possibleType.length - 1 + ) === "repeating" + ) { + typeGradient = `repeating-${possibleType}gradient`; + return true; + } + if ( + value.slice( + index - possibleType.length - 8, + index - possibleType.length - 1 + ) === "-webkit" + ) { + typeGradient = `-webkit-${possibleType}gradient`; + return true; + } + typeGradient = `${possibleType}gradient`; + return true; + } + } + } + ); + if (!typeGradient) { + break; + } + const {start, end} = getParenthesesRange( + value, + index + gradientLength + ); + const match = value.substring(start + 1, end - 1); + startIndex = end + 1 + conicGradientLength; + result.push({ + typeGradient, + match, + offset: typeGradient.length + 2, + index: index - typeGradient.length + gradientLength, + hasComma: true + }); + } + if (result.length) { + result[result.length - 1].hasComma = false; + } + return result; + } + + const STORAGE_KEY_IMAGE_DETAILS_LIST = "__darkreader__imageDetails_v2_list"; + const STORAGE_KEY_IMAGE_DETAILS_PREFIX = "__darkreader__imageDetails_v2_"; + const STORAGE_KEY_CSS_FETCH_PREFIX = "__darkreader__cssFetch_"; + let imageCacheTimeout = 0; + const imageDetailsCacheQueue = new Map(); + const cachedImageUrls = []; + function writeImageDetailsQueue() { + imageDetailsCacheQueue.forEach((details, url) => { + if (url && url.startsWith("https://")) { + try { + const json = JSON.stringify(details); + sessionStorage.setItem( + `${STORAGE_KEY_IMAGE_DETAILS_PREFIX}${url}`, + json + ); + cachedImageUrls.push(url); + } catch (err) {} + } + }); + imageDetailsCacheQueue.clear(); + sessionStorage.setItem( + STORAGE_KEY_IMAGE_DETAILS_LIST, + JSON.stringify(cachedImageUrls) + ); + } + function writeImageDetailsCache(url, imageDetails) { + if (!url || !url.startsWith("https://")) { + return; + } + imageDetailsCacheQueue.set(url, imageDetails); + clearTimeout(imageCacheTimeout); + imageCacheTimeout = setTimeout(writeImageDetailsQueue, 1000); + } + function readImageDetailsCache(targetMap) { + try { + const jsonList = sessionStorage.getItem( + STORAGE_KEY_IMAGE_DETAILS_LIST + ); + if (!jsonList) { + return; + } + const list = JSON.parse(jsonList); + list.forEach((url) => { + const json = sessionStorage.getItem( + `${STORAGE_KEY_IMAGE_DETAILS_PREFIX}${url}` + ); + if (json) { + const details = JSON.parse(json); + targetMap.set(url, details); + } + }); + } catch (err) {} + } + function writeCSSFetchCache(url, cssText) { + const key = `${STORAGE_KEY_CSS_FETCH_PREFIX}${url}`; + try { + sessionStorage.setItem(key, cssText); + } catch (err) {} + } + function readCSSFetchCache(url) { + const key = `${STORAGE_KEY_CSS_FETCH_PREFIX}${url}`; + try { + return sessionStorage.getItem(key) ?? null; + } catch (err) {} + return null; + } + + function toSVGMatrix(matrix) { + return matrix + .slice(0, 4) + .map((m) => m.map((m) => m.toFixed(3)).join(" ")) + .join(" "); + } + function getSVGFilterMatrixValue(config) { + return toSVGMatrix(createFilterMatrix(config)); + } + + const MAX_FRAME_DURATION = 1000 / 60; + class AsyncQueue { + constructor() { + this.queue = []; + this.timerId = null; + } + addTask(task) { + this.queue.push(task); + this.scheduleFrame(); + } + stop() { + if (this.timerId !== null) { + cancelAnimationFrame(this.timerId); + this.timerId = null; + } + this.queue = []; + } + scheduleFrame() { + if (this.timerId) { + return; + } + this.timerId = requestAnimationFrame(() => { + this.timerId = null; + const start = Date.now(); + let cb; + while ((cb = this.queue.shift())) { + cb(); + if (Date.now() - start >= MAX_FRAME_DURATION) { + this.scheduleFrame(); + break; + } + } + }); + } + } + + const resolvers$1 = new Map(); + const rejectors = new Map(); + async function bgFetch(request) { + if (window.DarkReader?.Plugins?.fetch) { + return window.DarkReader.Plugins.fetch(request); + } + const parsedURL = new URL(request.url); + if ( + parsedURL.origin !== request.origin && + shouldIgnoreCors(parsedURL) + ) { + throw new Error("Cross-origin limit reached"); + } + return new Promise((resolve, reject) => { + const id = generateUID(); + resolvers$1.set(id, resolve); + rejectors.set(id, reject); + chrome.runtime.sendMessage({ + type: MessageTypeCStoBG.FETCH, + data: request, + id + }); + }); + } + chrome.runtime.onMessage.addListener(({type, data, error, id}) => { + if (type === MessageTypeBGtoCS.FETCH_RESPONSE) { + const resolve = resolvers$1.get(id); + const reject = rejectors.get(id); + resolvers$1.delete(id); + rejectors.delete(id); + if (error) { + reject && + reject( + typeof error === "string" ? new Error(error) : error + ); + } else { + resolve && resolve(data); + } + } + }); + + const imageManager = new AsyncQueue(); + async function getImageDetails(url) { + return new Promise(async (resolve, reject) => { + try { + let dataURL = url.startsWith("data:") + ? url + : await getDataURL(url); + const blob = + tryConvertDataURLToBlobSync(dataURL) ?? + (await loadAsBlob(url)); + let image; + let useViewBox = false; + if (dataURL.startsWith("data:image/svg+xml")) { + const commaIndex = dataURL.indexOf(","); + if (commaIndex >= 0) { + let svgText = dataURL.slice(commaIndex + 1); + const encoding = dataURL + .slice(0, commaIndex) + .split(";")[1]; + if (encoding === "base64") { + if (svgText.includes("%")) { + svgText = decodeURIComponent(svgText); + } + svgText = atob(svgText); + } else if (svgText.startsWith("%3c")) { + svgText = decodeURIComponent(svgText); + } + if (svgText.startsWith(""); + const svgOpening = svgText + .slice(0, closingIndex + 1) + .toLocaleLowerCase(); + if ( + svgOpening.includes("viewbox=") && + !svgOpening.includes("width=") && + !svgOpening.includes("height=") + ) { + useViewBox = true; + const viewboxIndex = + svgOpening.indexOf("viewbox="); + const quote = svgOpening[viewboxIndex + 8]; + const viewboxCloseIndex = svgOpening.indexOf( + quote, + viewboxIndex + 9 + ); + const viewBox = svgOpening + .slice(viewboxIndex + 9, viewboxCloseIndex) + .split(" ") + .map((x) => parseFloat(x)); + if ( + viewBox.length === 4 && + !viewBox.some((x) => isNaN(x)) + ) { + const width = viewBox[2] - viewBox[0]; + const height = viewBox[3] - viewBox[1]; + dataURL = `data:image/svg+xml;base64,${btoa(` { + const analysis = analyzeImage(image); + resolve({ + src: url, + dataURL: analysis.isLarge ? "" : dataURL, + width: image.width, + height: image.height, + useViewBox, + ...analysis + }); + }); + } catch (error) { + reject(error); + } + }); + } + async function getDataURL(url) { + const parsedURL = new URL(url); + if (parsedURL.origin === location.origin) { + return await loadAsDataURL(url); + } + return await bgFetch({ + url, + responseType: "data-url", + origin: location.origin + }); + } + async function tryCreateImageBitmap(blob) { + try { + return await createImageBitmap(blob); + } catch (err) { + logWarn( + `Unable to create image bitmap for type ${blob.type}: ${String(err)}` + ); + return null; + } + } + const INCOMPLETE_DOC_LOADING_IMAGE_LIMIT = 256; + let loadingImagesCount = 0; + async function loadImage(url) { + return new Promise((resolve, reject) => { + const image = new Image(); + image.onload = () => resolve(image); + image.onerror = () => reject(`Unable to load image ${url}`); + if ( + ++loadingImagesCount <= INCOMPLETE_DOC_LOADING_IMAGE_LIMIT || + isReadyStateComplete() + ) { + image.src = url; + } else { + addReadyStateCompleteListener(() => (image.src = url)); + } + }); + } + const MAX_ANALYSIS_PIXELS_COUNT = 32 * 32; + let canvas; + let context; + function createCanvas() { + const maxWidth = MAX_ANALYSIS_PIXELS_COUNT; + const maxHeight = MAX_ANALYSIS_PIXELS_COUNT; + canvas = document.createElement("canvas"); + canvas.width = maxWidth; + canvas.height = maxHeight; + context = canvas.getContext("2d", {willReadFrequently: true}); + context.imageSmoothingEnabled = false; + } + function removeCanvas() { + canvas = null; + context = null; + } + const LARGE_IMAGE_PIXELS_COUNT = 512 * 512; + function analyzeImage(image) { + if (!canvas) { + createCanvas(); + } + let sw; + let sh; + if (image instanceof HTMLImageElement) { + sw = image.naturalWidth; + sh = image.naturalHeight; + } else { + sw = image.width; + sh = image.height; + } + if (sw === 0 || sh === 0) { + logWarn("Image is empty"); + return { + isDark: false, + isLight: false, + isTransparent: false, + isLarge: false, + averageColor: null + }; + } + const isLarge = sw * sh > LARGE_IMAGE_PIXELS_COUNT; + const sourcePixelsCount = sw * sh; + const k = Math.min( + 1, + Math.sqrt(MAX_ANALYSIS_PIXELS_COUNT / sourcePixelsCount) + ); + const width = Math.ceil(sw * k); + const height = Math.ceil(sh * k); + context.clearRect(0, 0, width, height); + context.drawImage(image, 0, 0, sw, sh, 0, 0, width, height); + const imageData = context.getImageData(0, 0, width, height); + const d = imageData.data; + const TRANSPARENT_ALPHA_THRESHOLD = 0.05; + const DARK_LIGHTNESS_THRESHOLD = 0.4; + const LIGHT_LIGHTNESS_THRESHOLD = 0.7; + let transparentPixelsCount = 0; + let darkPixelsCount = 0; + let lightPixelsCount = 0; + let minLightness = 1; + let maxLightness = 0; + let sumR = 0; + let sumG = 0; + let sumB = 0; + let sumA = 0; + let i, x, y; + let r, g, b, a; + let l; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + i = 4 * (y * width + x); + r = d[i + 0]; + g = d[i + 1]; + b = d[i + 2]; + a = d[i + 3]; + sumR += r; + sumG += g; + sumB += b; + sumA += a; + if (a / 255 < TRANSPARENT_ALPHA_THRESHOLD) { + transparentPixelsCount++; + } else { + l = getSRGBLightness(r, g, b); + if (l < DARK_LIGHTNESS_THRESHOLD) { + darkPixelsCount++; + } + if (l > LIGHT_LIGHTNESS_THRESHOLD) { + lightPixelsCount++; + } + if (l < minLightness) { + minLightness = l; + } + if (l > maxLightness) { + maxLightness = l; + } + } + } + } + const totalPixelsCount = width * height; + const opaquePixelsCount = totalPixelsCount - transparentPixelsCount; + const DARK_IMAGE_THRESHOLD = 0.7; + const LIGHT_IMAGE_THRESHOLD = 0.7; + const TRANSPARENT_IMAGE_THRESHOLD = 0.1; + const SOLID_LIGHTNESS_DIFF_THRESHOLD = 0.1; + const isSolid = + sumA === totalPixelsCount * 255 && + maxLightness - minLightness < SOLID_LIGHTNESS_DIFF_THRESHOLD; + const solidColor = isSolid + ? { + r: Math.round(sumR / opaquePixelsCount), + g: Math.round(sumG / opaquePixelsCount), + b: Math.round(sumB / opaquePixelsCount), + a: transparentPixelsCount / totalPixelsCount + } + : null; + return { + isDark: darkPixelsCount / opaquePixelsCount >= DARK_IMAGE_THRESHOLD, + isLight: + lightPixelsCount / opaquePixelsCount >= LIGHT_IMAGE_THRESHOLD, + isTransparent: + transparentPixelsCount / totalPixelsCount >= + TRANSPARENT_IMAGE_THRESHOLD, + isLarge, + solidColor + }; + } + let isBlobURLSupported = null; + let canUseProxy = false; + let blobURLCheckRequested = false; + const blobURLCheckAwaiters = []; + document.addEventListener( + "__darkreader__inlineScriptsAllowed", + () => (canUseProxy = true), + {once: true} + ); + async function requestBlobURLCheck() { + if (!canUseProxy) { + return; + } + if (blobURLCheckRequested) { + return await new Promise((resolve) => + blobURLCheckAwaiters.push(resolve) + ); + } + blobURLCheckRequested = true; + await new Promise((resolve) => { + document.addEventListener( + "__darkreader__blobURLCheckResponse", + (e) => { + isBlobURLSupported = e.detail.blobURLAllowed; + resolve(); + blobURLCheckAwaiters.forEach((r) => r()); + blobURLCheckAwaiters.splice(0); + }, + {once: true} + ); + document.dispatchEvent( + new CustomEvent("__darkreader__blobURLCheckRequest") + ); + }); + } + function isBlobURLCheckResultReady() { + return isBlobURLSupported != null || !canUseProxy; + } + function onCSPError(err) { + if (err.blockedURI === "blob") { + isBlobURLSupported = false; + document.removeEventListener("securitypolicyviolation", onCSPError); + } + } + document.addEventListener("securitypolicyviolation", onCSPError); + const filteredImageURLs = new Map(); + function getFilteredImageURL( + {dataURL, width, height, useViewBox, src}, + theme + ) { + if (dataURL.startsWith("data:image/svg+xml")) { + dataURL = escapeXML(dataURL); + } + const matrix = getSVGFilterMatrixValue(theme); + const size = useViewBox + ? `viewBox="0 0 ${width} ${height}"` + : `width="${width}" height="${height}"`; + const svg = [ + ``, + "", + '', + ``, + "", + "", + ``, + "" + ].join(""); + if (!isBlobURLSupported) { + return `data:image/svg+xml;base64,${btoa(svg)}`; + } + const cached = filteredImageURLs.get(src); + if (cached && cached.matrix === matrix) { + return cached.url; + } + const bytes = new Uint8Array(svg.length); + for (let i = 0; i < svg.length; i++) { + bytes[i] = svg.charCodeAt(i); + } + const blob = new Blob([bytes], {type: "image/svg+xml"}); + const objectURL = URL.createObjectURL(blob); + if (cached) { + URL.revokeObjectURL(cached.url); + } + filteredImageURLs.set(src, {matrix, url: objectURL}); + return objectURL; + } + function getSolidColorImageURL({width, height, useViewBox}, color) { + const size = useViewBox + ? `viewBox="0 0 ${width} ${height}"` + : `width="${width}" height="${height}"`; + const svg = [ + ``, + ``, + "" + ].join(""); + return `data:image/svg+xml;base64,${btoa(svg)}`; + } + const xmlEscapeChars = { + "<": "<", + ">": ">", + "&": "&", + "'": "'", + '"': """ + }; + function escapeXML(str) { + return str.replace(/[<>&'"]/g, (c) => xmlEscapeChars[c] ?? c); + } + const dataURLBlobURLs = new Map(); + function tryConvertDataURLToBlobSync(dataURL) { + const colonIndex = dataURL.indexOf(":"); + const semicolonIndex = dataURL.indexOf(";", colonIndex + 1); + const commaIndex = dataURL.indexOf(",", semicolonIndex + 1); + const encoding = dataURL + .substring(semicolonIndex + 1, commaIndex) + .toLocaleLowerCase(); + const mediaType = dataURL.substring(colonIndex + 1, semicolonIndex); + if (encoding !== "base64" || !mediaType) { + return null; + } + let base64Content = dataURL.substring(commaIndex + 1); + if (base64Content.includes("%")) { + base64Content = decodeURIComponent(base64Content); + } + const characters = atob(base64Content); + const bytes = new Uint8Array(characters.length); + for (let i = 0; i < characters.length; i++) { + bytes[i] = characters.charCodeAt(i); + } + return new Blob([bytes], {type: mediaType}); + } + async function tryConvertDataURLToBlobURL(dataURL) { + if (!isBlobURLSupported) { + return null; + } + const hash = getHashCode(dataURL); + let blobURL = dataURLBlobURLs.get(hash); + if (blobURL) { + return blobURL; + } + let blob = tryConvertDataURLToBlobSync(dataURL); + if (!blob) { + const response = await fetch(dataURL); + blob = await response.blob(); + } + blobURL = URL.createObjectURL(blob); + dataURLBlobURLs.set(hash, blobURL); + return blobURL; + } + function cleanImageProcessingCache() { + imageManager && imageManager.stop(); + removeCanvas(); + filteredImageURLs.forEach(({url}) => URL.revokeObjectURL(url)); + filteredImageURLs.clear(); + dataURLBlobURLs.forEach((u) => URL.revokeObjectURL(u)); + dataURLBlobURLs.clear(); + } + + function getPriority(ruleStyle, property) { + return Boolean(ruleStyle && ruleStyle.getPropertyPriority(property)); + } + function canFilterImage(url) { + if (url.startsWith("data:")) { + return true; + } + try { + return new URL(url).origin === location.origin; + } catch { + return false; + } + } + const bgPropsToCopy = [ + "background-clip", + "background-position", + "background-repeat", + "background-size" + ]; + function getModifiableCSSDeclaration( + property, + value, + rule, + variablesStore, + ignoreImageSelectors, + isCancelled + ) { + let modifier = null; + if (property.startsWith("--")) { + modifier = getVariableModifier( + variablesStore, + property, + value, + rule, + ignoreImageSelectors, + isCancelled + ); + } else if (value.includes("var(")) { + modifier = getVariableDependantModifier( + variablesStore, + property, + value, + rule + ); + } else if (property === "color-scheme") { + modifier = getColorSchemeModifier(); + } else if (property === "scrollbar-color") { + modifier = getScrollbarColorModifier(value); + } else if ( + (property.includes("color") && + property !== "-webkit-print-color-adjust") || + property === "fill" || + property === "stroke" || + property === "stop-color" + ) { + if ( + property.startsWith("border") && + property !== "border-color" && + (value === "initial" || value === "currentcolor") + ) { + const borderSideProp = property.substring( + 0, + property.length - 6 + ); + const borderSideVal = + rule.style.getPropertyValue(borderSideProp); + const borderStyleVal = + rule.style.getPropertyValue("border-style"); + if ( + borderSideVal.startsWith("0px") || + borderSideVal === "none" || + borderStyleVal === "none" + ) { + property = borderSideProp; + modifier = borderSideVal; + } else { + modifier = value; + } + } else { + modifier = getColorModifier(property, value, rule); + } + } else if ( + property === "background-image" || + property === "list-style-image" + ) { + const selectorText = rule.selectorText; + const pushFilter = selectorText + ? (type) => pushFilterSelector(selectorText, type) + : null; + modifier = getBgImageModifier( + value, + rule, + ignoreImageSelectors, + isCancelled, + pushFilter + ); + } else if (property.includes("shadow")) { + modifier = getShadowModifier(value); + } else if (bgPropsToCopy.includes(property) && value !== "initial") { + modifier = value; + } + if (!modifier) { + return null; + } + return { + property, + value: modifier, + important: getPriority(rule.style, property), + sourceValue: value + }; + } + function joinSelectors(...selectors) { + return selectors.filter(Boolean).join(", "); + } + const hostsWithOddScrollbars = ["calendar.google.com"]; + function getModifiedUserAgentStyle(theme, isIFrame, styleSystemControls) { + const lines = []; + if (!isIFrame) { + lines.push("html {"); + lines.push( + ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)} !important;` + ); + lines.push("}"); + } + if (isCSSColorSchemePropSupported && theme.mode === 1) { + lines.push("html {"); + lines.push(` color-scheme: dark !important;`); + lines.push("}"); + lines.push("iframe {"); + lines.push(` color-scheme: dark !important;`); + lines.push("}"); + } + const bgSelectors = joinSelectors( + isIFrame ? "" : "html, body", + styleSystemControls ? "input, textarea, select, button, dialog" : "" + ); + if (bgSelectors) { + lines.push(`${bgSelectors} {`); + lines.push( + ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)};` + ); + lines.push("}"); + } + lines.push( + `${joinSelectors("html, body", styleSystemControls ? "input, textarea, select, button" : "")} {` + ); + lines.push( + ` border-color: ${modifyBorderColor({r: 76, g: 76, b: 76}, theme)};` + ); + lines.push( + ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)};` + ); + lines.push("}"); + lines.push("a {"); + lines.push( + ` color: ${modifyForegroundColor({r: 0, g: 64, b: 255}, theme)};` + ); + lines.push("}"); + lines.push("table {"); + lines.push( + ` border-color: ${modifyBorderColor({r: 128, g: 128, b: 128}, theme)};` + ); + lines.push("}"); + lines.push("mark {"); + lines.push( + ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)};` + ); + lines.push("}"); + lines.push("::placeholder {"); + lines.push( + ` color: ${modifyForegroundColor({r: 169, g: 169, b: 169}, theme)};` + ); + lines.push("}"); + lines.push("input:-webkit-autofill,"); + lines.push("textarea:-webkit-autofill,"); + lines.push("select:-webkit-autofill {"); + lines.push( + ` background-color: ${modifyBackgroundColor({r: 250, g: 255, b: 189}, theme)} !important;` + ); + lines.push( + ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)} !important;` + ); + lines.push("}"); + if ( + theme.scrollbarColor && + !hostsWithOddScrollbars.includes(location.hostname) + ) { + lines.push(getModifiedScrollbarStyle(theme)); + } + if (theme.selectionColor) { + lines.push(getModifiedSelectionStyle(theme)); + } + if (isLayerRuleSupported) { + lines.unshift("@layer {"); + lines.push("}"); + } + return lines.join("\n"); + } + function getSelectionColor(theme) { + let backgroundColorSelection; + let foregroundColorSelection; + if (theme.selectionColor === "auto") { + backgroundColorSelection = modifyBackgroundColor( + {r: 0, g: 96, b: 212}, + {...theme, grayscale: 0} + ); + foregroundColorSelection = modifyForegroundColor( + {r: 255, g: 255, b: 255}, + {...theme, grayscale: 0} + ); + } else { + const rgb = parseColorWithCache(theme.selectionColor); + const hsl = rgbToHSL(rgb); + backgroundColorSelection = theme.selectionColor; + if (hsl.l < 0.5) { + foregroundColorSelection = "#FFF"; + } else { + foregroundColorSelection = "#000"; + } + } + return {backgroundColorSelection, foregroundColorSelection}; + } + function getModifiedSelectionStyle(theme) { + const lines = []; + const modifiedSelectionColor = getSelectionColor(theme); + const backgroundColorSelection = + modifiedSelectionColor.backgroundColorSelection; + const foregroundColorSelection = + modifiedSelectionColor.foregroundColorSelection; + ["::selection", "::-moz-selection"].forEach((selection) => { + lines.push(`${selection} {`); + lines.push( + ` background-color: ${backgroundColorSelection} !important;` + ); + lines.push(` color: ${foregroundColorSelection} !important;`); + lines.push("}"); + }); + return lines.join("\n"); + } + function getModifiedScrollbarStyle(theme) { + let colorTrack; + let colorThumb; + if (theme.scrollbarColor === "auto") { + colorTrack = modifyBackgroundColor({r: 241, g: 241, b: 241}, theme); + colorThumb = modifyBackgroundColor({r: 176, g: 176, b: 176}, theme); + } else { + const rgb = parseColorWithCache(theme.scrollbarColor); + const hsl = rgbToHSL(rgb); + const darken = (darker) => ({ + ...hsl, + l: clamp(hsl.l - darker, 0, 1) + }); + colorTrack = hslToString(darken(0.4)); + colorThumb = hslToString(hsl); + } + return [ + `* {`, + ` scrollbar-color: ${colorThumb} ${colorTrack};`, + `}` + ].join("\n"); + } + function getModifiedFallbackStyle(theme, {strict}) { + const factory = defaultFallbackFactory; + return factory(theme, {strict}); + } + function defaultFallbackFactory(theme, {strict}) { + const lines = []; + lines.push( + `html, body, ${strict ? "body :not(iframe)" : "body > :not(iframe)"} {` + ); + lines.push( + ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)} !important;` + ); + lines.push( + ` border-color: ${modifyBorderColor({r: 64, g: 64, b: 64}, theme)} !important;` + ); + lines.push( + ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)} !important;` + ); + lines.push("}"); + lines.push(`div[style*="background-color: rgb(135, 135, 135)"] {`); + lines.push(` background-color: #878787 !important;`); + lines.push("}"); + return lines.join("\n"); + } + let addFilterSelector$1 = null; + function setFilterSelectorHandler(fn) { + addFilterSelector$1 = fn; + } + function pushFilterSelector(selector, type) { + if (selector && addFilterSelector$1) { + addFilterSelector$1(selector, type); + } + } + const filterCompatibleProps = new Set([ + "background", + "background-image", + "list-style-image" + ]); + function isFilterCompatibleProp(property) { + return filterCompatibleProps.has(property); + } + const unparsableColors = new Set([ + "inherit", + "transparent", + "initial", + "currentcolor", + "none", + "unset", + "auto" + ]); + function getColorModifier(prop, value, rule) { + if ( + unparsableColors.has(value.toLowerCase()) && + !(prop === "color" && value === "initial") + ) { + return value; + } + let rgb = null; + if (prop === "color" && value === "initial") { + rgb = {r: 0, g: 0, b: 0, a: 1}; + } else { + rgb = parseColorWithCache(value); + } + if (!rgb) { + logWarn("Couldn't parse color", value); + return null; + } + if (prop.includes("background")) { + const maskImageValue = rule.style.maskImage ?? rule.style.mask; + if ( + maskImageValue && + !maskImageValue.startsWith("none") && + !maskImageValue.startsWith("linear-gradient") + ) { + return (theme) => modifyForegroundColor(rgb, theme); + } + return (theme) => modifyBackgroundColor(rgb, theme); + } + if (prop.includes("border") || prop.includes("outline")) { + return (theme) => modifyBorderColor(rgb, theme); + } + return (theme) => modifyForegroundColor(rgb, theme); + } + const imageDetailsCache = new Map(); + const awaitingForImageLoading = new Map(); + let didTryLoadCache = false; + function shouldIgnoreImage(selectorText, selectors) { + if (!selectorText || selectors.length === 0) { + return false; + } + if (selectors.some((s) => s === "*")) { + return true; + } + const ruleSelectors = selectorText.split(/,\s*/g); + for (let i = 0; i < selectors.length; i++) { + const ignoredSelector = selectors[i]; + if (ignoredSelector.startsWith("^")) { + const beginning = ignoredSelector.slice(1); + if (ruleSelectors.some((s) => s.startsWith(beginning))) { + return true; + } + } else if (ignoredSelector.endsWith("$")) { + const ending = ignoredSelector.slice( + 0, + ignoredSelector.length - 1 + ); + if (ruleSelectors.some((s) => s.endsWith(ending))) { + return true; + } + } else if (ruleSelectors.some((s) => s === ignoredSelector)) { + return true; + } + } + return false; + } + const imageSelectorQueue = new Map(); + const imageSelectorNodeQueue = new Set(); + let imageSelectorQueueFrameId = null; + let classObserver = null; + function checkImageSelectors(node) { + for (const [selector, callbacks] of imageSelectorQueue) { + if ( + node.querySelector(selector) || + (node instanceof Element && node.matches(selector)) + ) { + imageSelectorQueue.delete(selector); + callbacks.forEach((cb) => cb()); + } + } + if (imageSelectorQueue.size === 0) { + classObserver?.disconnect(); + classObserver = null; + return; + } + if (!classObserver) { + classObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + imageSelectorNodeQueue.add(mutation.target); + if (!imageSelectorQueueFrameId) { + imageSelectorQueueFrameId = requestAnimationFrame( + () => { + imageSelectorNodeQueue.forEach((element) => { + checkImageSelectors(element); + }); + imageSelectorNodeQueue.clear(); + imageSelectorQueueFrameId = null; + } + ); + } + }); + }); + classObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: ["class"], + subtree: true + }); + } + } + function getBgImageModifier( + value, + rule, + ignoreImageSelectors, + isCancelled, + pushFilter = null + ) { + try { + if (shouldIgnoreImage(rule.selectorText, ignoreImageSelectors)) { + return value; + } + const gradients = parseGradient(value); + const urls = getMatches(cssURLRegex, value); + if (urls.length === 0 && gradients.length === 0) { + return value; + } + const getIndices = (matches) => { + let index = 0; + return matches.map((match) => { + const valueIndex = value.indexOf(match, index); + index = valueIndex + match.length; + return {match, index: valueIndex}; + }); + }; + const matches = gradients + .map((i) => ({type: "gradient", ...i})) + .concat( + getIndices(urls).map((i) => ({ + type: "url", + offset: 0, + ...i + })) + ) + .sort((a, b) => (a.index > b.index ? 1 : -1)); + const getGradientModifier = (gradient) => { + const {typeGradient, match, hasComma} = gradient; + const partsRegex = + /([^\(\),]+(\([^\(\)]*(\([^\(\)]*\)*[^\(\)]*)?\))?([^\(\), ]|( (?!calc)))*),?/g; + const colorStopRegex = + /^(from|color-stop|to)\(([^\(\)]*?,\s*)?(.*?)\)$/; + const parts = getMatches(partsRegex, match, 1).map((part) => { + part = part.trim(); + let rgb = parseColorWithCache(part); + if (rgb) { + return (theme) => modifyGradientColor(rgb, theme); + } + const space = part.lastIndexOf(" "); + rgb = parseColorWithCache(part.substring(0, space)); + if (rgb) { + return (theme) => + `${modifyGradientColor(rgb, theme)} ${part.substring(space + 1)}`; + } + const colorStopMatch = part.match(colorStopRegex); + if (colorStopMatch) { + rgb = parseColorWithCache(colorStopMatch[3]); + if (rgb) { + return (theme) => + `${colorStopMatch[1]}(${colorStopMatch[2] ? `${colorStopMatch[2]}, ` : ""}${modifyGradientColor(rgb, theme)})`; + } + } + return () => part; + }); + return (theme) => { + return `${typeGradient}(${parts.map((modify) => modify(theme)).join(", ")})${hasComma ? ", " : ""}`; + }; + }; + const getURLModifier = (urlValue) => { + if (!didTryLoadCache) { + didTryLoadCache = true; + readImageDetailsCache(imageDetailsCache); + } + let url = getCSSURLValue(urlValue); + const isURLEmpty = url.length === 0; + const {parentStyleSheet} = rule; + const ownerNode = parentStyleSheet?.ownerNode; + const scope = + (parentStyleSheet && getSheetScope(parentStyleSheet)) ?? + document; + const baseURL = + parentStyleSheet && parentStyleSheet.href + ? getCSSBaseBath(parentStyleSheet.href) + : ownerNode?.baseURI || location.origin; + url = getAbsoluteURL(baseURL, url); + return async (theme) => { + if (isURLEmpty) { + return "url('')"; + } + let selector = rule.selectorText; + if (selector) { + if (selector.includes("::before")) { + selector = selector.replaceAll("::before", ""); + } + if (selector.includes("::after")) { + selector = selector.replaceAll("::after", ""); + } + if (!scope.querySelector(selector)) { + await new Promise((resolve) => { + if (imageSelectorQueue.has(selector)) { + imageSelectorQueue + .get(selector) + .push(resolve); + } else { + imageSelectorQueue.set(selector, [resolve]); + } + }); + } + } + let imageDetails = null; + if (imageDetailsCache.has(url)) { + imageDetails = imageDetailsCache.get(url); + } else { + try { + if (!isBlobURLCheckResultReady()) { + await requestBlobURLCheck(); + } + if (awaitingForImageLoading.has(url)) { + const awaiters = + awaitingForImageLoading.get(url); + imageDetails = await new Promise((resolve) => + awaiters.push(resolve) + ); + if (!imageDetails) { + return null; + } + } else { + awaitingForImageLoading.set(url, []); + imageDetails = await getImageDetails(url); + imageDetailsCache.set(url, imageDetails); + if (!url.startsWith("data:")) { + const parsedURL = new URL(url); + if (parsedURL.origin === location.origin) { + writeImageDetailsCache( + url, + imageDetails + ); + } + } + awaitingForImageLoading + .get(url) + .forEach((resolve) => + resolve(imageDetails) + ); + awaitingForImageLoading.delete(url); + } + if (isCancelled()) { + return null; + } + } catch (err) { + logWarn(err); + if (awaitingForImageLoading.has(url)) { + awaitingForImageLoading + .get(url) + .forEach((resolve) => resolve(null)); + awaitingForImageLoading.delete(url); + } + } + } + if (imageDetails) { + const bgImageValue = getBgImageValue( + imageDetails, + theme + ); + if (bgImageValue) { + return bgImageValue; + } + } + if (url.startsWith("data:")) { + const blobURL = await tryConvertDataURLToBlobURL(url); + if (blobURL) { + return `url("${blobURL}")`; + } + } + return `url("${url}")`; + }; + }; + const isSafeToInvert = () => { + const repeat = ( + rule.style.backgroundRepeat || "" + ).toLowerCase(); + const size = (rule.style.backgroundSize || "").toLowerCase(); + const isTiled = + repeat.length > 0 && + repeat !== "no-repeat" && + !repeat.includes("no-repeat"); + const isStretched = + size.includes("cover") || + size.includes("contain") || + size.includes("100%"); + return !isTiled && !isStretched; + }; + const getBgImageValue = (imageDetails, theme) => { + const { + isDark, + isLight, + isTransparent, + isLarge, + solidColor, + width + } = imageDetails; + let result = null; + const logSrc = imageDetails.src.startsWith("data:") + ? "data:" + : imageDetails.src; + if (isLarge && isLight && !isTransparent && theme.mode === 1) { + logInfo(`Hiding large light image ${logSrc}`); + result = "none"; + } else if ( + isDark && + isTransparent && + theme.mode === 1 && + width > 2 + ) { + logInfo(`Inverting dark image ${logSrc}`); + if (canFilterImage(imageDetails.src)) { + const inverted = getFilteredImageURL(imageDetails, { + ...theme, + sepia: clamp(theme.sepia + 10, 0, 100) + }); + result = `url("${inverted}")`; + } else if (isSafeToInvert()) { + pushFilter?.("invert"); + } + } else if (isLight && !isTransparent && theme.mode === 1) { + if (solidColor) { + logInfo(`Replacing image with a solid color ${logSrc}`); + const darkColor = modifyBackgroundColor( + solidColor, + theme, + false + ); + const solid = getSolidColorImageURL( + imageDetails, + darkColor + ); + result = `url("${solid}")`; + } else if (canFilterImage(imageDetails.src)) { + logInfo(`Inverting light image ${logSrc}`); + const inverted = getFilteredImageURL( + imageDetails, + theme + ); + result = `url("${inverted}")`; + } else if (isSafeToInvert()) { + pushFilter?.("invert"); + } + } else if ( + theme.mode === 0 && + isLight && + imageDetails.dataURL + ) { + logInfo(`Applying filter to image ${logSrc}`); + if (canFilterImage(imageDetails.src)) { + const filtered = getFilteredImageURL(imageDetails, { + ...theme, + brightness: clamp(theme.brightness - 10, 5, 200), + sepia: clamp(theme.sepia + 10, 0, 100) + }); + result = `url("${filtered}")`; + } else { + pushFilter?.("dim"); + } + } else { + if (theme.mode === 1 && !canFilterImage(imageDetails.src)) { + pushFilter?.("none"); + } + logInfo(`Not modifying the image ${logSrc}`); + } + return result; + }; + const modifiers = []; + let matchIndex = 0; + let prevHasComma = false; + matches.forEach( + ({type, match, index, typeGradient, hasComma, offset}, i) => { + const matchStart = index; + const prefixStart = matchIndex; + const matchEnd = matchStart + match.length + offset; + matchIndex = matchEnd; + if (prefixStart !== matchStart) { + if (prevHasComma) { + modifiers.push(() => { + let betweenValue = value.substring( + prefixStart, + matchStart + ); + if (betweenValue[0] === ",") { + betweenValue = betweenValue.substring(1); + } + return betweenValue; + }); + } else { + modifiers.push(() => + value.substring(prefixStart, matchStart) + ); + } + } + prevHasComma = hasComma || false; + if (type === "url") { + modifiers.push(getURLModifier(match)); + } else if (type === "gradient") { + modifiers.push( + getGradientModifier({ + match, + index, + typeGradient: typeGradient, + hasComma: hasComma || false, + offset + }) + ); + } + if (i === matches.length - 1) { + modifiers.push(() => value.substring(matchEnd)); + } + } + ); + return (theme) => { + const results = modifiers + .filter(Boolean) + .map((modify) => modify(theme)); + if (results.some((r) => r instanceof Promise)) { + return Promise.all(results).then((asyncResults) => { + return asyncResults.filter(Boolean).join(""); + }); + } + const combinedResult = results.join(""); + if (combinedResult.endsWith(", initial")) { + return combinedResult.slice(0, -9); + } + return combinedResult; + }; + } catch (err) { + logWarn(`Unable to parse gradient ${value}`, err); + return null; + } + } + function getShadowModifierWithInfo(value) { + try { + let index = 0; + const colorMatches = getMatches( + /(^|\s)(?!calc)([a-z]+\(.+?\)|#[0-9a-f]+|[a-z]+)(.*?(inset|outset)?($|,))/gi, + value, + 2 + ); + let notParsed = 0; + const modifiers = colorMatches.map((match, i) => { + const prefixIndex = index; + const matchIndex = value.indexOf(match, index); + const matchEnd = matchIndex + match.length; + index = matchEnd; + const rgb = parseColorWithCache(match); + if (!rgb) { + notParsed++; + return () => value.substring(prefixIndex, matchEnd); + } + return (theme) => + `${value.substring(prefixIndex, matchIndex)}${modifyShadowColor(rgb, theme)}${i === colorMatches.length - 1 ? value.substring(matchEnd) : ""}`; + }); + return (theme) => { + const modified = modifiers + .map((modify) => modify(theme)) + .join(""); + return { + matchesLength: colorMatches.length, + unparsableMatchesLength: notParsed, + result: modified + }; + }; + } catch (err) { + logWarn(`Unable to parse shadow ${value}`, err); + return null; + } + } + function getShadowModifier(value) { + const shadowModifier = getShadowModifierWithInfo(value); + if (!shadowModifier) { + return null; + } + return (theme) => shadowModifier(theme).result; + } + function getScrollbarColorModifier(value) { + const colorsMatch = value.match( + /^\s*([a-z]+(\(.*\))?)\s+([a-z]+(\(.*\))?)\s*$/ + ); + if (!colorsMatch) { + return value; + } + const thumb = parseColorWithCache(colorsMatch[1]); + const track = parseColorWithCache(colorsMatch[3]); + if (!thumb || !track) { + logWarn( + "Couldn't parse color", + ...[thumb, track].filter((c) => !c) + ); + return null; + } + return (theme) => + `${modifyForegroundColor(thumb, theme)} ${modifyBackgroundColor(track, theme)}`; + } + function getColorSchemeModifier() { + return (theme) => (theme.mode === 0 ? "dark light" : "dark"); + } + function getVariableModifier( + variablesStore, + prop, + value, + rule, + ignoredImgSelectors, + isCancelled + ) { + return variablesStore.getModifierForVariable({ + varName: prop, + sourceValue: value, + rule, + ignoredImgSelectors, + isCancelled + }); + } + function getVariableDependantModifier(variablesStore, prop, value, rule) { + return variablesStore.getModifierForVarDependant(prop, value, rule); + } + function cleanModificationCache() { + clearColorModificationCache(); + imageDetailsCache.clear(); + cleanImageProcessingCache(); + awaitingForImageLoading.clear(); + imageSelectorQueue.clear(); + classObserver?.disconnect(); + classObserver = null; + } + + const VAR_TYPE_BG_COLOR = 1 << 0; + const VAR_TYPE_TEXT_COLOR = 1 << 1; + const VAR_TYPE_BORDER_COLOR = 1 << 2; + const VAR_TYPE_BG_IMG = 1 << 3; + const shouldSetDefaultColor = + !location.hostname.startsWith("www.ebay.") && + !location.hostname.includes(".ebay."); + class VariablesStore { + constructor() { + this.varTypes = new Map(); + this.rulesQueue = new Set(); + this.inlineStyleQueue = []; + this.definedVars = new Set(); + this.varRefs = new Map(); + this.unknownColorVars = new Set(); + this.unknownBgVars = new Set(); + this.undefinedVars = new Set(); + this.initialVarTypes = new Map(); + this.changedTypeVars = new Set(); + this.typeChangeSubscriptions = new Map(); + this.unstableVarValues = new Map(); + this.varFilterTypes = new Map(); + this.notifyingVarFilterTypes = new Set(); + this.varsChanged = false; + } + clear() { + this.varTypes.clear(); + this.rulesQueue.clear(); + this.inlineStyleQueue.splice(0); + this.definedVars.clear(); + this.varRefs.clear(); + this.unknownColorVars.clear(); + this.unknownBgVars.clear(); + this.undefinedVars.clear(); + this.initialVarTypes.clear(); + this.changedTypeVars.clear(); + this.typeChangeSubscriptions.clear(); + this.unstableVarValues.clear(); + this.varFilterTypes.clear(); + this.notifyingVarFilterTypes.clear(); + } + isVarType(varName, typeNum) { + return ( + this.varTypes.has(varName) && + (this.varTypes.get(varName) & typeNum) > 0 + ); + } + addRulesForMatching(rules) { + this.rulesQueue.add(rules); + } + addInlineStyleForMatching(style) { + this.inlineStyleQueue.push(style); + } + matchVariablesAndDependents() { + if ( + this.rulesQueue.size === 0 && + this.inlineStyleQueue.length === 0 + ) { + return; + } + this.changedTypeVars.clear(); + this.initialVarTypes = new Map(this.varTypes); + this.varsChanged = false; + this.collectRootVariables(); + this.collectVariablesAndVarDep(); + this.collectRootVarDependents(); + if (!this.varsChanged) { + return; + } + this.varRefs.forEach((refs, v) => { + refs.forEach((r) => { + if (this.varTypes.has(v)) { + this.resolveVariableType(r, this.varTypes.get(v)); + } + }); + }); + this.unknownColorVars.forEach((v) => { + if (this.unknownBgVars.has(v)) { + this.unknownColorVars.delete(v); + this.unknownBgVars.delete(v); + this.resolveVariableType(v, VAR_TYPE_BG_COLOR); + } else if ( + this.isVarType( + v, + VAR_TYPE_BG_COLOR | + VAR_TYPE_TEXT_COLOR | + VAR_TYPE_BORDER_COLOR + ) + ) { + this.unknownColorVars.delete(v); + } else { + this.undefinedVars.add(v); + } + }); + this.unknownBgVars.forEach((v) => { + const hasColor = + this.findVarRef(v, (ref) => { + return ( + this.unknownColorVars.has(ref) || + this.isVarType( + ref, + VAR_TYPE_BG_COLOR | + VAR_TYPE_TEXT_COLOR | + VAR_TYPE_BORDER_COLOR + ) + ); + }) != null; + if (hasColor) { + this.iterateVarRefs(v, (ref) => { + this.resolveVariableType(ref, VAR_TYPE_BG_COLOR); + }); + } else if ( + this.isVarType(v, VAR_TYPE_BG_COLOR | VAR_TYPE_BG_IMG) + ) { + this.unknownBgVars.delete(v); + } else { + this.undefinedVars.add(v); + } + }); + this.changedTypeVars.forEach((varName) => { + if (this.typeChangeSubscriptions.has(varName)) { + this.typeChangeSubscriptions + .get(varName) + .forEach((callback) => { + callback(); + }); + } + }); + this.changedTypeVars.clear(); + } + getModifierForVariable(options) { + return (theme) => { + const { + varName, + sourceValue, + rule, + ignoredImgSelectors, + isCancelled + } = options; + const getDeclarations = () => { + const declarations = []; + const addModifiedValue = ( + typeNum, + varNameWrapper, + colorModifier + ) => { + if (!this.isVarType(varName, typeNum)) { + return; + } + const property = varNameWrapper(varName); + let modifiedValue; + if (isVarDependant(sourceValue)) { + if (isConstructedColorVar(sourceValue)) { + let value = insertVarValues( + sourceValue, + this.unstableVarValues + ); + if (!value) { + value = + typeNum === VAR_TYPE_BG_COLOR + ? "#ffffff" + : "#000000"; + } + modifiedValue = colorModifier(value, theme); + } else { + modifiedValue = replaceCSSVariablesNames( + sourceValue, + (v) => varNameWrapper(v), + (fallback) => colorModifier(fallback, theme) + ); + } + } else { + modifiedValue = colorModifier(sourceValue, theme); + } + declarations.push({ + property, + value: modifiedValue + }); + }; + addModifiedValue( + VAR_TYPE_BG_COLOR, + wrapBgColorVariableName, + tryModifyBgColor + ); + addModifiedValue( + VAR_TYPE_TEXT_COLOR, + wrapTextColorVariableName, + tryModifyTextColor + ); + addModifiedValue( + VAR_TYPE_BORDER_COLOR, + wrapBorderColorVariableName, + tryModifyBorderColor + ); + if (this.isVarType(varName, VAR_TYPE_BG_IMG)) { + const property = wrapBgImgVariableName(varName); + let modifiedValue = sourceValue; + if (isVarDependant(sourceValue)) { + modifiedValue = replaceCSSVariablesNames( + sourceValue, + (v) => wrapBgColorVariableName(v), + (fallback) => tryModifyBgColor(fallback, theme) + ); + } + const pushFilter = rule.selectorText + ? (type) => this.setVarFilterType(varName, type) + : null; + const bgModifier = getBgImageModifier( + modifiedValue, + rule, + ignoredImgSelectors, + isCancelled, + pushFilter + ); + modifiedValue = + typeof bgModifier === "function" + ? bgModifier(theme) + : bgModifier; + declarations.push({ + property, + value: modifiedValue + }); + } + return declarations; + }; + const callbacks = new Set(); + const addListener = (onTypeChange) => { + if (!rule.selectorText) { + return; + } + const callback = () => { + const decs = getDeclarations(); + onTypeChange(decs); + }; + callbacks.add(callback); + this.subscribeForVarTypeChange(varName, callback); + }; + const removeListeners = () => { + callbacks.forEach((callback) => { + this.unsubscribeFromVariableTypeChanges( + varName, + callback + ); + }); + }; + return { + declarations: getDeclarations(), + onTypeChange: {addListener, removeListeners} + }; + }; + } + getModifierForVarDependant(property, sourceValue, rule) { + if (rule && rule.selectorText && isFilterCompatibleProp(property)) { + this.watchFilterVars(sourceValue, rule.selectorText); + } + const isConstructedColor = sourceValue.match(/^\s*(rgb|hsl)a?\(/); + const isSimpleConstructedColor = sourceValue.match( + /^rgba?\(var\(--[\-_A-Za-z0-9]+\)(\s*,?\/?\s*0?\.\d+)?\)$/ + ); + if (isConstructedColor && !isSimpleConstructedColor) { + const isBg = property.startsWith("background"); + const isText = isTextColorProperty(property); + return (theme) => { + let value = insertVarValues( + sourceValue, + this.unstableVarValues + ); + if (!value) { + value = isBg ? "#ffffff" : "#000000"; + } + const modifier = isBg + ? tryModifyBgColor + : isText + ? tryModifyTextColor + : tryModifyBorderColor; + return modifier(value, theme); + }; + } + if ( + property === "background-color" || + (isSimpleConstructedColor && property === "background") + ) { + return (theme) => { + const defaultFallback = shouldSetDefaultColor + ? tryModifyBgColor( + isConstructedColor ? "255, 255, 255" : "#ffffff", + theme + ) + : "transparent"; + return replaceCSSVariablesNames( + sourceValue, + (v) => wrapBgColorVariableName(v), + (fallback) => tryModifyBgColor(fallback, theme), + defaultFallback + ); + }; + } + if (isTextColorProperty(property)) { + return (theme) => { + const defaultFallback = tryModifyTextColor( + isConstructedColor ? "0, 0, 0" : "#000000", + theme + ); + return replaceCSSVariablesNames( + sourceValue, + (v) => wrapTextColorVariableName(v), + (fallback) => tryModifyTextColor(fallback, theme), + defaultFallback + ); + }; + } + if ( + property === "background" || + property === "background-image" || + property === "box-shadow" + ) { + return (theme) => { + const unknownVars = new Set(); + const modify = () => { + const variableReplaced = replaceCSSVariablesNames( + sourceValue, + (v) => { + if (this.isVarType(v, VAR_TYPE_BG_COLOR)) { + return wrapBgColorVariableName(v); + } + if (this.isVarType(v, VAR_TYPE_BG_IMG)) { + return wrapBgImgVariableName(v); + } + unknownVars.add(v); + return v; + }, + (fallback) => tryModifyBgColor(fallback, theme) + ); + if (property === "box-shadow") { + const shadowModifier = + getShadowModifierWithInfo(variableReplaced); + const modifiedShadow = shadowModifier(theme); + if ( + modifiedShadow.unparsableMatchesLength !== + modifiedShadow.matchesLength + ) { + return modifiedShadow.result; + } + } + return variableReplaced; + }; + const modified = modify(); + if (unknownVars.size > 0) { + if (isFallbackResolved(modified)) { + return modified; + } + return new Promise((resolve) => { + for (const unknownVar of unknownVars.values()) { + const callback = () => { + this.unsubscribeFromVariableTypeChanges( + unknownVar, + callback + ); + const newValue = modify(); + resolve(newValue); + }; + this.subscribeForVarTypeChange( + unknownVar, + callback + ); + } + }); + } + return modified; + }; + } + if ( + property.startsWith("border") || + property.startsWith("outline") + ) { + return (theme) => { + return replaceCSSVariablesNames( + sourceValue, + (v) => wrapBorderColorVariableName(v), + (fallback) => tryModifyBorderColor(fallback, theme) + ); + }; + } + return null; + } + subscribeForVarTypeChange(varName, callback) { + if (!this.typeChangeSubscriptions.has(varName)) { + this.typeChangeSubscriptions.set(varName, new Set()); + } + const rootStore = this.typeChangeSubscriptions.get(varName); + if (!rootStore.has(callback)) { + rootStore.add(callback); + } + } + unsubscribeFromVariableTypeChanges(varName, callback) { + if (this.typeChangeSubscriptions.has(varName)) { + this.typeChangeSubscriptions.get(varName).delete(callback); + } + } + setVarFilterType(varName, type) { + if (this.varFilterTypes.get(varName) === type) { + return; + } + this.varFilterTypes.set(varName, type); + if (this.notifyingVarFilterTypes.has(varName)) { + return; + } + const subs = this.typeChangeSubscriptions.get(varName); + if (subs && subs.size > 0) { + this.notifyingVarFilterTypes.add(varName); + subs.forEach((callback) => callback()); + this.notifyingVarFilterTypes.delete(varName); + } + } + pushFilterSelectorsForValue(sourceValue, selector) { + const directRefs = new Set(); + iterateVarDependencies(sourceValue, (v) => directRefs.add(v)); + const allRefs = new Set(); + directRefs.forEach((v) => { + allRefs.add(v); + this.iterateVarRefs(v, (ref) => allRefs.add(ref)); + }); + allRefs.forEach((v) => { + const type = this.varFilterTypes.get(v); + if (type) { + pushFilterSelector(selector, type); + } + }); + } + watchFilterVars(sourceValue, selector) { + const directRefs = new Set(); + iterateVarDependencies(sourceValue, (v) => directRefs.add(v)); + const allRefs = new Set(); + directRefs.forEach((v) => { + allRefs.add(v); + this.iterateVarRefs(v, (ref) => allRefs.add(ref)); + }); + this.pushFilterSelectorsForValue(sourceValue, selector); + const callback = () => + this.pushFilterSelectorsForValue(sourceValue, selector); + allRefs.forEach((v) => this.subscribeForVarTypeChange(v, callback)); + } + collectVariablesAndVarDep() { + this.rulesQueue.forEach((rules) => { + iterateCSSRules(rules, (rule) => { + if (rule.style) { + this.collectVarsFromCSSDeclarations(rule.style); + } + }); + }); + this.inlineStyleQueue.forEach((style) => { + this.collectVarsFromCSSDeclarations(style); + }); + this.rulesQueue.clear(); + this.inlineStyleQueue.splice(0); + } + collectVarsFromCSSDeclarations(style) { + iterateCSSDeclarations(style, (property, value) => { + if (isVariable(property)) { + this.inspectVariable(property, value); + } + if (isVarDependant(value)) { + this.inspectVarDependant(property, value); + } + }); + } + shouldProcessRootVariables() { + return ( + this.rulesQueue.size > 0 && + document.documentElement.getAttribute("style")?.includes("--") + ); + } + collectRootVariables() { + if (!this.shouldProcessRootVariables()) { + return; + } + iterateCSSDeclarations( + document.documentElement.style, + (property, value) => { + if (isVariable(property)) { + this.inspectVariable(property, value); + } + } + ); + } + inspectVariable(varName, value) { + this.unstableVarValues.set(varName, value); + if (isVarDependant(value) && isConstructedColorVar(value)) { + if (!this.unknownColorVars.has(varName)) { + this.unknownColorVars.add(varName); + this.varsChanged = true; + } + this.definedVars.add(varName); + } + if (this.definedVars.has(varName)) { + return; + } + this.definedVars.add(varName); + this.varsChanged = true; + const isColor = Boolean( + getRGBValues(value) || parseColorWithCache(value) + ); + if (isColor) { + this.unknownColorVars.add(varName); + } else if ( + value.includes("url(") || + value.includes("linear-gradient(") || + value.includes("radial-gradient(") + ) { + this.resolveVariableType(varName, VAR_TYPE_BG_IMG); + } + } + resolveVariableType(varName, typeNum) { + const initialType = this.initialVarTypes.get(varName) || 0; + const currentType = this.varTypes.get(varName) || 0; + const newType = currentType | typeNum; + this.varTypes.set(varName, newType); + if (newType !== initialType || this.undefinedVars.has(varName)) { + this.changedTypeVars.add(varName); + this.undefinedVars.delete(varName); + } + if ( + newType !== currentType || + this.unknownColorVars.has(varName) || + this.unknownBgVars.has(varName) + ) { + this.varsChanged = true; + } + this.unknownColorVars.delete(varName); + this.unknownBgVars.delete(varName); + } + collectRootVarDependents() { + if (!this.shouldProcessRootVariables()) { + return; + } + iterateCSSDeclarations( + document.documentElement.style, + (property, value) => { + if (isVarDependant(value)) { + this.inspectVarDependant(property, value); + } + } + ); + } + inspectVarDependant(property, value) { + if (isVariable(property)) { + this.iterateVarDeps(value, (ref) => { + if (!this.varRefs.has(property)) { + this.varRefs.set(property, new Set()); + } + const refs = this.varRefs.get(property); + if (!refs.has(ref)) { + refs.add(ref); + this.varsChanged = true; + } + }); + } else if ( + property === "background-color" || + property === "box-shadow" + ) { + this.iterateVarDeps(value, (v) => + this.resolveVariableType(v, VAR_TYPE_BG_COLOR) + ); + } else if (isTextColorProperty(property)) { + this.iterateVarDeps(value, (v) => + this.resolveVariableType(v, VAR_TYPE_TEXT_COLOR) + ); + } else if ( + property.startsWith("border") || + property.startsWith("outline") + ) { + this.iterateVarDeps(value, (v) => + this.resolveVariableType(v, VAR_TYPE_BORDER_COLOR) + ); + } else if ( + property === "background" || + property === "background-image" + ) { + this.iterateVarDeps(value, (v) => { + if ( + this.isVarType(v, VAR_TYPE_BG_COLOR | VAR_TYPE_BG_IMG) + ) { + return; + } + const isBgColor = + this.findVarRef(v, (ref) => { + return ( + this.unknownColorVars.has(ref) || + this.isVarType( + ref, + VAR_TYPE_BG_COLOR | + VAR_TYPE_TEXT_COLOR | + VAR_TYPE_BORDER_COLOR + ) + ); + }) != null; + this.iterateVarRefs(v, (ref) => { + if (isBgColor) { + this.resolveVariableType(ref, VAR_TYPE_BG_COLOR); + } else if (!this.unknownBgVars.has(ref)) { + this.unknownBgVars.add(ref); + this.varsChanged = true; + } + }); + }); + } + } + iterateVarDeps(value, iterator) { + const varDeps = new Set(); + iterateVarDependencies(value, (v) => varDeps.add(v)); + varDeps.forEach((v) => iterator(v)); + } + findVarRef(varName, iterator, visited = new Set()) { + const queue = [varName]; + while (queue.length > 0) { + const v = queue.pop(); + if (visited.has(v)) { + continue; + } + visited.add(v); + if (iterator(v)) { + return v; + } + const refs = this.varRefs.get(v); + if (refs) { + refs.forEach((ref) => queue.push(ref)); + } + } + return null; + } + iterateVarRefs(varName, iterator) { + this.findVarRef(varName, (ref) => { + iterator(ref); + return false; + }); + } + setOnRootVariableChange(callback) { + this.onRootVariableDefined = callback; + } + putRootVars(styleElement, theme) { + const declarations = new Map(); + iterateCSSDeclarations( + document.documentElement.style, + (property, value) => { + if (isVariable(property)) { + if (this.isVarType(property, VAR_TYPE_BG_COLOR)) { + declarations.set( + wrapBgColorVariableName(property), + tryModifyBgColor(value, theme) + ); + } + if (this.isVarType(property, VAR_TYPE_TEXT_COLOR)) { + declarations.set( + wrapTextColorVariableName(property), + tryModifyTextColor(value, theme) + ); + } + if (this.isVarType(property, VAR_TYPE_BORDER_COLOR)) { + declarations.set( + wrapBorderColorVariableName(property), + tryModifyBorderColor(value, theme) + ); + } + this.subscribeForVarTypeChange( + property, + this.onRootVariableDefined + ); + } + } + ); + const cssLines = []; + cssLines.push(":root {"); + for (const [property, value] of declarations) { + cssLines.push(` ${property}: ${value};`); + } + cssLines.push("}"); + const cssText = cssLines.join("\n"); + const sheet = styleElement.sheet; + if (sheet) { + if (sheet.cssRules.length > 0) { + sheet.deleteRule(0); + } + sheet.insertRule(cssText); + } else { + styleElement.textContent = cssText; + } + } + } + const variablesStore = new VariablesStore(); + function getVariableRange(input, searchStart = 0) { + const start = input.indexOf("var(", searchStart); + if (start >= 0) { + const range = getParenthesesRange(input, start + 3); + if (range) { + return {start, end: range.end}; + } + } + return null; + } + function getVariablesMatches(input) { + const ranges = []; + let i = 0; + let range; + while ((range = getVariableRange(input, i))) { + const {start, end} = range; + ranges.push({start, end, value: input.substring(start, end)}); + i = range.end + 1; + } + return ranges; + } + function replaceVariablesMatches(input, replacer) { + const matches = getVariablesMatches(input); + const matchesCount = matches.length; + if (matchesCount === 0) { + return input; + } + const inputLength = input.length; + const replacements = matches.map((m) => + replacer(m.value, matches.length) + ); + const parts = []; + parts.push(input.substring(0, matches[0].start)); + for (let i = 0; i < matchesCount; i++) { + parts.push(replacements[i]); + const start = matches[i].end; + const end = + i < matchesCount - 1 ? matches[i + 1].start : inputLength; + parts.push(input.substring(start, end)); + } + return parts.join(""); + } + function getVariableNameAndFallback(match) { + const commaIndex = match.indexOf(","); + let name; + let fallback; + if (commaIndex >= 0) { + name = match.substring(4, commaIndex).trim(); + fallback = match.substring(commaIndex + 1, match.length - 1).trim(); + } else { + name = match.substring(4, match.length - 1).trim(); + fallback = ""; + } + return {name, fallback}; + } + function replaceCSSVariablesNames( + value, + nameReplacer, + fallbackReplacer, + finalFallback + ) { + const matchReplacer = (match) => { + const {name, fallback} = getVariableNameAndFallback(match); + const newName = nameReplacer(name); + if (!fallback) { + if (finalFallback) { + return `var(${newName}, ${finalFallback})`; + } + return `var(${newName})`; + } + let newFallback; + if (isVarDependant(fallback)) { + newFallback = replaceCSSVariablesNames( + fallback, + nameReplacer, + fallbackReplacer + ); + } else if (fallbackReplacer) { + newFallback = fallbackReplacer(fallback); + } else { + newFallback = fallback; + } + return `var(${newName}, ${newFallback})`; + }; + return replaceVariablesMatches(value, matchReplacer); + } + function iterateVarDependencies(value, iterator) { + replaceCSSVariablesNames(value, (varName) => { + iterator(varName); + return varName; + }); + } + function wrapBgColorVariableName(name) { + return `--darkreader-bg${name}`; + } + function wrapTextColorVariableName(name) { + return `--darkreader-text${name}`; + } + function wrapBorderColorVariableName(name) { + return `--darkreader-border${name}`; + } + function wrapBgImgVariableName(name) { + return `--darkreader-bgimg${name}`; + } + function isVariable(property) { + return property.startsWith("--"); + } + function isVarDependant(value) { + return value.includes("var("); + } + function isConstructedColorVar(value) { + return ( + value.match(/^\s*(rgb|hsl)a?\(/) || + value.match(/^(((\d{1,3})|(var\([\-_A-Za-z0-9]+\))),?\s*?){3}$/) + ); + } + function isFallbackResolved(modified) { + if (modified.startsWith("var(") && modified.endsWith(")")) { + const hasNestedBrackets = modified.endsWith("))"); + const hasDoubleNestedBrackets = modified.endsWith(")))"); + const lastOpenBracketIndex = hasNestedBrackets + ? modified.lastIndexOf("(") + : -1; + const firstOpenBracketIndex = hasDoubleNestedBrackets + ? modified.lastIndexOf("(", lastOpenBracketIndex - 1) + : lastOpenBracketIndex; + const commaIndex = modified.lastIndexOf( + ",", + hasNestedBrackets ? firstOpenBracketIndex : modified.length + ); + if (commaIndex < 0 || modified[commaIndex + 1] !== " ") { + return false; + } + const fallback = modified.slice( + commaIndex + 2, + modified.length - 1 + ); + if (hasNestedBrackets) { + return ( + fallback.startsWith("rgb(") || + fallback.startsWith("rgba(") || + fallback.startsWith("hsl(") || + fallback.startsWith("hsla(") || + fallback.startsWith("var(--darkreader-bg--") || + fallback.startsWith("var(--darkreader-background-") || + (hasDoubleNestedBrackets && + fallback.includes("var(--darkreader-background-")) + ); + } + return fallback.match(/^(#[0-9a-f]+)|([a-z]+)$/i); + } + return false; + } + const textColorProps = [ + "color", + "caret-color", + "-webkit-text-fill-color", + "fill", + "stroke" + ]; + function isTextColorProperty(property) { + return textColorProps.includes(property); + } + function parseRawColorValue(input) { + const v = getRGBValues(input); + if (v) { + const color = + v[3] < 1 + ? `rgb(${v[0]} ${v[1]} ${v[2]} / ${v[3]})` + : `rgb(${v[0]} ${v[1]} ${v[2]})`; + return {isRaw: true, color}; + } + return {isRaw: false, color: input}; + } + function handleRawColorValue(input, theme, modifyFunction) { + const {isRaw, color} = parseRawColorValue(input); + const rgb = parseColorWithCache(color); + if (rgb) { + const outputColor = modifyFunction(rgb, theme, !isRaw); + if (isRaw) { + const outputInRGB = parseColorWithCache(outputColor); + return outputInRGB + ? Number.isNaN(outputInRGB.a) || outputInRGB.a === 1 + ? `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}` + : `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}, ${outputInRGB.a}` + : outputColor; + } + return outputColor; + } + return color; + } + function tryModifyBgColor(color, theme) { + return handleRawColorValue(color, theme, modifyBackgroundColor); + } + function tryModifyTextColor(color, theme) { + return handleRawColorValue(color, theme, modifyForegroundColor); + } + function tryModifyBorderColor(color, theme) { + return handleRawColorValue(color, theme, modifyBorderColor); + } + const MAX_VARIABLE_SUBSTITUTIONS = 100000; + const MAX_VARIABLE_DEPTH = 1000; + function insertVarValues( + source, + varValues, + stack = new Set(), + cache = new Map(), + depth = 0 + ) { + if (depth > MAX_VARIABLE_DEPTH) { + return null; + } + let containsUnresolvedVar = false; + const matchReplacer = (match) => { + const {name, fallback} = getVariableNameAndFallback(match); + const varValue = varValues.get(name); + let inserted = null; + if (varValue) { + if (cache.has(name)) { + inserted = cache.get(name); + } else if (!stack.has(name)) { + stack.add(name); + if (isVarDependant(varValue)) { + inserted = insertVarValues( + varValue, + varValues, + stack, + cache, + depth + 1 + ); + } else { + inserted = varValue; + } + stack.delete(name); + cache.set(name, inserted); + } + } else if (fallback) { + if (isVarDependant(fallback)) { + inserted = insertVarValues( + fallback, + varValues, + stack, + cache, + depth + 1 + ); + } else { + inserted = fallback; + } + } + if (!inserted) { + containsUnresolvedVar = true; + return null; + } + return inserted; + }; + const replaced = replaceVariablesMatches(source, matchReplacer); + if ( + containsUnresolvedVar || + replaced.length > MAX_VARIABLE_SUBSTITUTIONS + ) { + return null; + } + return replaced; + } + + function getThemeKey$1(theme) { + let resultKey = ""; + themeCacheKeys.forEach((key) => { + resultKey += `${key}:${theme[key]};`; + }); + return resultKey; + } + const asyncQueue = createAsyncTasksQueue(); + let cssTextCounter = 0; + const cssTextIds = new Map(); + function getCSSTextKey(cssText) { + const existing = cssTextIds.get(cssText); + if (existing !== undefined) { + return existing; + } + let n = ++cssTextCounter; + let key = ""; + do { + key = String.fromCharCode(n & 0xffff) + key; + n >>>= 16; + } while (n > 0); + cssTextIds.set(cssText, key); + return key; + } + function createStyleSheetModifier() { + let renderId = 0; + function getStyleRuleKey(rule) { + let key = getCSSTextKey(rule.cssText); + if (isMediaRule(rule.parentRule)) { + key = `${getCSSTextKey(rule.parentRule.media.mediaText)}{${key}}`; + } + if (isLayerRule(rule.parentRule)) { + key = `${getCSSTextKey(rule.parentRule.name)}{${key}}`; + } + if (isContainerRule(rule.parentRule)) { + const {containerName, containerQuery} = rule.parentRule; + key = `${getCSSTextKey(`${containerName} ${containerQuery}`)}{${key}}`; + } + return key; + } + const rulesTextCache = new Set(); + const rulesModCache = new Map(); + const varTypeChangeCleaners = new Set(); + let prevFilterKey = null; + let hasNonLoadedLink = false; + let wasRebuilt = false; + function shouldRebuildStyle() { + return hasNonLoadedLink && !wasRebuilt; + } + function modifySheet(options) { + const rules = options.sourceCSSRules; + const { + theme, + ignoreImageAnalysis, + force, + prepareSheet, + isAsyncCancelled + } = options; + let rulesChanged = rulesModCache.size === 0; + const notFoundCacheKeys = new Set(rulesModCache.keys()); + const themeKey = getThemeKey$1(theme); + const themeChanged = themeKey !== prevFilterKey; + if (hasNonLoadedLink) { + wasRebuilt = true; + } + const modRules = []; + iterateCSSRules( + rules, + (rule) => { + const key = getStyleRuleKey(rule); + let textDiffersFromPrev = false; + notFoundCacheKeys.delete(key); + if (!rulesTextCache.has(key)) { + rulesTextCache.add(key); + textDiffersFromPrev = true; + } + if (textDiffersFromPrev) { + rulesChanged = true; + } else { + modRules.push(rulesModCache.get(key)); + return; + } + if (rule.style.all === "revert") { + return; + } + const modDecs = []; + rule.style && + iterateCSSDeclarations( + rule.style, + (property, value) => { + const mod = getModifiableCSSDeclaration( + property, + value, + rule, + variablesStore, + ignoreImageAnalysis, + isAsyncCancelled + ); + if (mod) { + modDecs.push(mod); + } + } + ); + let modRule = null; + if (modDecs.length > 0) { + const parentRule = rule.parentRule; + modRule = { + selector: rule.selectorText, + declarations: modDecs, + parentRule + }; + modRules.push(modRule); + } + rulesModCache.set(key, modRule); + }, + () => { + hasNonLoadedLink = true; + } + ); + notFoundCacheKeys.forEach((key) => { + rulesTextCache.delete(key); + rulesModCache.delete(key); + }); + prevFilterKey = themeKey; + if (!force && !rulesChanged && !themeChanged) { + return; + } + renderId++; + function setRule(target, index, rule) { + const {selector, declarations} = rule; + let selectorText = selector; + const emptyIsWhereSelector = + isChromium && + selector.startsWith(":is(") && + (selector.includes(":is()") || + selector.includes(":where()") || + (selector.includes(":where(") && + selector.includes(":-moz"))); + const viewTransitionSelector = + selector.includes("::view-transition-"); + if (emptyIsWhereSelector || viewTransitionSelector) { + selectorText = ".darkreader-unsupported-selector"; + } + if (isChromium && selectorText.endsWith("::picker")) { + selectorText = selectorText.replaceAll( + "::picker", + "::picker(select)" + ); + } + let ruleText = `${selectorText} {`; + for (const dec of declarations) { + const {property, value, important} = dec; + if (value) { + ruleText += ` ${property}: ${value}${important ? " !important" : ""};`; + } + } + ruleText += " }"; + target.insertRule(ruleText, index); + } + const asyncDeclarations = new Map(); + const varDeclarations = new Map(); + let asyncDeclarationCounter = 0; + let varDeclarationCounter = 0; + const rootReadyGroup = {rule: null, rules: [], isGroup: true}; + const groupRefs = new WeakMap(); + function getGroup(rule) { + if (rule == null) { + return rootReadyGroup; + } + if (groupRefs.has(rule)) { + return groupRefs.get(rule); + } + const group = {rule, rules: [], isGroup: true}; + groupRefs.set(rule, group); + const parentGroup = getGroup(rule.parentRule); + parentGroup.rules.push(group); + return group; + } + varTypeChangeCleaners.forEach((clear) => clear()); + varTypeChangeCleaners.clear(); + modRules + .filter((r) => r) + .forEach(({selector, declarations, parentRule}) => { + const group = getGroup(parentRule); + const readyStyleRule = { + selector, + declarations: [], + isGroup: false + }; + const readyDeclarations = readyStyleRule.declarations; + group.rules.push(readyStyleRule); + function handleAsyncDeclaration( + property, + modified, + important, + sourceValue + ) { + const asyncKey = ++asyncDeclarationCounter; + const asyncDeclaration = { + property, + value: null, + important, + asyncKey, + sourceValue + }; + readyDeclarations.push(asyncDeclaration); + const currentRenderId = renderId; + modified.then((asyncValue) => { + if ( + !asyncValue || + isAsyncCancelled() || + currentRenderId !== renderId + ) { + return; + } + asyncDeclaration.value = asyncValue; + asyncQueue.add(() => { + if ( + isAsyncCancelled() || + currentRenderId !== renderId + ) { + return; + } + rebuildAsyncRule(asyncKey); + }); + }); + } + function handleVarDeclarations( + property, + modified, + important, + sourceValue + ) { + const {declarations: varDecs, onTypeChange} = modified; + const varKey = ++varDeclarationCounter; + const currentRenderId = renderId; + const initialIndex = readyDeclarations.length; + let oldDecs = []; + if (varDecs.length === 0) { + const tempDec = { + property, + value: sourceValue, + important, + sourceValue, + varKey + }; + readyDeclarations.push(tempDec); + oldDecs = [tempDec]; + } + varDecs.forEach((mod) => { + if (mod.value instanceof Promise) { + handleAsyncDeclaration( + mod.property, + mod.value, + important, + sourceValue + ); + } else { + const readyDec = { + property: mod.property, + value: mod.value, + important, + sourceValue, + varKey + }; + readyDeclarations.push(readyDec); + oldDecs.push(readyDec); + } + }); + onTypeChange.addListener((newDecs) => { + if ( + isAsyncCancelled() || + currentRenderId !== renderId + ) { + return; + } + const readyVarDecs = newDecs.map((mod) => { + return { + property: mod.property, + value: mod.value, + important, + sourceValue, + varKey + }; + }); + const index = readyDeclarations.indexOf( + oldDecs[0], + initialIndex + ); + readyDeclarations.splice( + index, + oldDecs.length, + ...readyVarDecs + ); + oldDecs = readyVarDecs; + rebuildVarRule(varKey); + }); + varTypeChangeCleaners.add(() => + onTypeChange.removeListeners() + ); + } + declarations.forEach( + ({property, value, important, sourceValue}) => { + if (typeof value === "function") { + const modified = value(theme); + if (modified instanceof Promise) { + handleAsyncDeclaration( + property, + modified, + important, + sourceValue + ); + } else if (property.startsWith("--")) { + handleVarDeclarations( + property, + modified, + important, + sourceValue + ); + } else { + readyDeclarations.push({ + property, + value: modified, + important, + sourceValue + }); + } + } else { + readyDeclarations.push({ + property, + value, + important, + sourceValue + }); + } + } + ); + }); + const sheet = prepareSheet(); + function buildStyleSheet() { + function createTarget(group, parent) { + const {rule} = group; + if (isStyleRule(rule)) { + const {selectorText} = rule; + const index = parent.cssRules.length; + parent.insertRule(`${selectorText} {}`, index); + return parent.cssRules[index]; + } + if (isMediaRule(rule)) { + const {media} = rule; + const index = parent.cssRules.length; + parent.insertRule( + `@media ${media.mediaText} {}`, + index + ); + return parent.cssRules[index]; + } + if (isLayerRule(rule)) { + const {name} = rule; + const index = parent.cssRules.length; + parent.insertRule(`@layer ${name} {}`, index); + return parent.cssRules[index]; + } + if (isContainerRule(rule)) { + const {containerName, containerQuery} = rule; + const index = parent.cssRules.length; + const query = containerName + ? `${containerName} ${containerQuery}` + : containerQuery; + parent.insertRule(`@container ${query} {}`, index); + return parent.cssRules[index]; + } + return parent; + } + function iterateReadyRules(group, target, styleIterator) { + group.rules.forEach((r) => { + if (r.isGroup) { + const t = createTarget(r, target); + iterateReadyRules(r, t, styleIterator); + } else { + styleIterator(r, target); + } + }); + } + iterateReadyRules(rootReadyGroup, sheet, (rule, target) => { + const index = target.cssRules.length; + rule.declarations.forEach(({asyncKey, varKey}) => { + if (asyncKey != null) { + asyncDeclarations.set(asyncKey, { + rule, + target, + index + }); + } + if (varKey != null) { + varDeclarations.set(varKey, {rule, target, index}); + } + }); + setRule(target, index, rule); + }); + } + function rebuildAsyncRule(key) { + const {rule, target, index} = asyncDeclarations.get(key); + target.deleteRule(index); + setRule(target, index, rule); + asyncDeclarations.delete(key); + } + function rebuildVarRule(key) { + const {rule, target, index} = varDeclarations.get(key); + target.deleteRule(index); + setRule(target, index, rule); + } + buildStyleSheet(); + } + return {modifySheet, shouldRebuildStyle}; + } + + let canUseSheetProxy$1 = false; + document.addEventListener( + "__darkreader__inlineScriptsAllowed", + () => (canUseSheetProxy$1 = true), + {once: true} + ); + const overrides$1 = new WeakSet(); + const overridesBySource = new WeakMap(); + function canHaveAdoptedStyleSheets(node) { + return Array.isArray(node.adoptedStyleSheets); + } + const getAdoptedSheets = isFirefox + ? (node) => + node.adoptedStyleSheets.wrappedJSObject ?? node.adoptedStyleSheets + : (node) => node.adoptedStyleSheets; + const createOverrideSheet = isFirefox + ? () => { + const pageWindow = window.wrappedJSObject ?? window; + return new pageWindow.CSSStyleSheet(); + } + : () => new CSSStyleSheet(); + function createAdoptedStyleSheetOverride(node) { + let cancelAsyncOperations = false; + function iterateSourceSheets(iterator) { + forEach(getAdoptedSheets(node), (sheet) => { + if (!overrides$1.has(sheet)) { + iterator(sheet); + } + defineSheetScope(sheet, node); + }); + } + function injectSheet(sheet, override) { + const newSheets = isFirefox + ? getAdoptedSheets(node) + : [...node.adoptedStyleSheets]; + const sheetIndex = newSheets.indexOf(sheet); + const overrideIndex = newSheets.indexOf(override); + if (overrideIndex >= 0) { + newSheets.splice(overrideIndex, 1); + } + newSheets.splice(sheetIndex + 1, 0, override); + if (!isFirefox) { + node.adoptedStyleSheets = newSheets; + } + } + function clear() { + const newSheets = isFirefox + ? getAdoptedSheets(node) + : [...node.adoptedStyleSheets]; + for (let i = newSheets.length - 1; i >= 0; i--) { + const sheet = newSheets[i]; + if (overrides$1.has(sheet)) { + newSheets.splice(i, 1); + } + } + if ( + !isFirefox && + node.adoptedStyleSheets.length !== newSheets.length + ) { + node.adoptedStyleSheets = newSheets; + } + sourceSheets = new WeakSet(); + sourceDeclarations = new WeakSet(); + } + const cleaners = []; + function destroy() { + cleaners.forEach((c) => c()); + cleaners.splice(0); + cancelAsyncOperations = true; + clear(); + if (frameId) { + cancelAnimationFrame(frameId); + frameId = null; + } + } + let rulesChangeKey = 0; + function getRulesChangeKey() { + let count = 0; + iterateSourceSheets((sheet) => { + count += sheet.cssRules.length; + }); + if (count === 1) { + const rule = getAdoptedSheets(node)[0].cssRules[0]; + return rule instanceof CSSStyleRule ? rule.style.length : count; + } + return count; + } + let sourceSheets = new WeakSet(); + let sourceDeclarations = new WeakSet(); + function render(theme, ignoreImageAnalysis) { + clear(); + const sheets = getAdoptedSheets(node); + for (let i = sheets.length - 1; i >= 0; i--) { + const sheet = sheets[i]; + if (overrides$1.has(sheet)) { + continue; + } + sourceSheets.add(sheet); + const readyOverride = overridesBySource.get(sheet); + if (readyOverride) { + rulesChangeKey = getRulesChangeKey(); + injectSheet(sheet, readyOverride); + continue; + } + const rules = sheet.cssRules; + const override = createOverrideSheet(); + overridesBySource.set(sheet, override); + iterateCSSRules(rules, (rule) => + sourceDeclarations.add(rule.style) + ); + const prepareSheet = () => { + for (let i = override.cssRules.length - 1; i >= 0; i--) { + override.deleteRule(i); + } + override.insertRule("#__darkreader__adoptedOverride {}"); + injectSheet(sheet, override); + overrides$1.add(override); + return override; + }; + const sheetModifier = createStyleSheetModifier(); + sheetModifier.modifySheet({ + prepareSheet, + sourceCSSRules: rules, + theme, + ignoreImageAnalysis, + force: false, + isAsyncCancelled: () => cancelAsyncOperations + }); + } + rulesChangeKey = getRulesChangeKey(); + } + let callbackRequested = false; + function handleArrayChange(callback) { + if (callbackRequested) { + return; + } + callbackRequested = true; + queueMicrotask(() => { + callbackRequested = false; + const sheets = getAdoptedSheets(node).filter( + (s) => !overrides$1.has(s) + ); + sheets.forEach((sheet) => overridesBySource.delete(sheet)); + callback(sheets); + }); + } + function checkForUpdates() { + return getRulesChangeKey() !== rulesChangeKey; + } + let frameId = null; + function watchUsingRAF(callback) { + frameId = requestAnimationFrame(() => { + if (canUseSheetProxy$1) { + return; + } + if (checkForUpdates()) { + handleArrayChange(callback); + } + watchUsingRAF(callback); + }); + } + function addSheetChangeEventListener(type, listener) { + node.addEventListener(type, listener); + cleaners.push(() => node.removeEventListener(type, listener)); + } + function watch(callback) { + const onAdoptedSheetsChange = () => { + canUseSheetProxy$1 = true; + handleArrayChange(callback); + }; + addSheetChangeEventListener( + "__darkreader__adoptedStyleSheetsChange", + onAdoptedSheetsChange + ); + addSheetChangeEventListener( + "__darkreader__adoptedStyleSheetChange", + onAdoptedSheetsChange + ); + addSheetChangeEventListener( + "__darkreader__adoptedStyleDeclarationChange", + onAdoptedSheetsChange + ); + if (canUseSheetProxy$1) { + return; + } + watchUsingRAF(callback); + } + return { + render, + destroy, + watch + }; + } + + const hostsBreakingOnStylePosition = [ + "chat.google.com", + "gogoprivate.com", + "gprivate.com", + "www.berlingske.dk", + "www.bloomberg.com", + "www.diffusioneshop.com", + "www.weekendavisen.dk", + "zhale.me" + ]; + const mode = hostsBreakingOnStylePosition.includes(location.hostname) + ? "away" + : "next"; + function getStyleInjectionMode() { + return mode; + } + const stylesWaitingForBody = new Set(); + let bodyObserver = null; + function injectStyleAway(styleElement) { + if (!document.body) { + stylesWaitingForBody.add(styleElement); + if (!bodyObserver) { + bodyObserver = new MutationObserver(() => { + if (document.body) { + bodyObserver.disconnect(); + bodyObserver = null; + stylesWaitingForBody.forEach((el) => + injectStyleAway(el) + ); + stylesWaitingForBody.clear(); + } + }); + bodyObserver.observe(document, { + childList: true, + subtree: true + }); + } + return; + } + let container = document.body.querySelector( + ".darkreader-style-container" + ); + if (!container) { + container = document.createElement("div"); + container.classList.add("darkreader"); + container.classList.add("darkreader-style-container"); + container.style.display = "none"; + document.body.append(container); + containerObserver = new MutationObserver(() => { + if (container?.nextElementSibling != null) { + container + .querySelectorAll(".darkreader--sync") + .forEach((el) => { + if (el.sheet.cssRules.length > 0) { + let cssText = ""; + for (const rule of el.sheet.cssRules) { + cssText += rule.cssText; + } + el.textContent = cssText; + } + }); + document.body.append(container); + } + }); + containerObserver.observe(document.body, {childList: true}); + } + container.append(styleElement); + } + let containerObserver; + function removeStyleContainer() { + bodyObserver?.disconnect(); + bodyObserver = null; + stylesWaitingForBody.clear(); + containerObserver?.disconnect(); + document.querySelector(".darkreader-style-container")?.remove(); + } + + const overrides = { + "background-color": { + customProp: "--darkreader-inline-bgcolor", + cssProp: "background-color", + dataAttr: "data-darkreader-inline-bgcolor" + }, + "background-image": { + customProp: "--darkreader-inline-bgimage", + cssProp: "background-image", + dataAttr: "data-darkreader-inline-bgimage" + }, + "border-color": { + customProp: "--darkreader-inline-border", + cssProp: "border-color", + dataAttr: "data-darkreader-inline-border" + }, + "border-bottom-color": { + customProp: "--darkreader-inline-border-bottom", + cssProp: "border-bottom-color", + dataAttr: "data-darkreader-inline-border-bottom" + }, + "border-left-color": { + customProp: "--darkreader-inline-border-left", + cssProp: "border-left-color", + dataAttr: "data-darkreader-inline-border-left" + }, + "border-right-color": { + customProp: "--darkreader-inline-border-right", + cssProp: "border-right-color", + dataAttr: "data-darkreader-inline-border-right" + }, + "border-top-color": { + customProp: "--darkreader-inline-border-top", + cssProp: "border-top-color", + dataAttr: "data-darkreader-inline-border-top" + }, + "box-shadow": { + customProp: "--darkreader-inline-boxshadow", + cssProp: "box-shadow", + dataAttr: "data-darkreader-inline-boxshadow" + }, + "color": { + customProp: "--darkreader-inline-color", + cssProp: "color", + dataAttr: "data-darkreader-inline-color" + }, + "fill": { + customProp: "--darkreader-inline-fill", + cssProp: "fill", + dataAttr: "data-darkreader-inline-fill" + }, + "stroke": { + customProp: "--darkreader-inline-stroke", + cssProp: "stroke", + dataAttr: "data-darkreader-inline-stroke" + }, + "outline-color": { + customProp: "--darkreader-inline-outline", + cssProp: "outline-color", + dataAttr: "data-darkreader-inline-outline" + }, + "stop-color": { + customProp: "--darkreader-inline-stopcolor", + cssProp: "stop-color", + dataAttr: "data-darkreader-inline-stopcolor" + } + }; + const shorthandOverrides = { + "background": { + customProp: "--darkreader-inline-bg", + cssProp: "background", + dataAttr: "data-darkreader-inline-bg" + }, + "border": { + customProp: "--darkreader-inline-border-short", + cssProp: "border", + dataAttr: "data-darkreader-inline-border-short" + }, + "border-bottom": { + customProp: "--darkreader-inline-border-bottom-short", + cssProp: "border-bottom", + dataAttr: "data-darkreader-inline-border-bottom-short" + }, + "border-left": { + customProp: "--darkreader-inline-border-left-short", + cssProp: "border-left", + dataAttr: "data-darkreader-inline-border-left-short" + }, + "border-right": { + customProp: "--darkreader-inline-border-right-short", + cssProp: "border-right", + dataAttr: "data-darkreader-inline-border-right-short" + }, + "border-top": { + customProp: "--darkreader-inline-border-top-short", + cssProp: "border-top", + dataAttr: "data-darkreader-inline-border-top-short" + } + }; + const overridesList = Object.values(overrides); + const normalizedPropList = {}; + overridesList.forEach( + ({cssProp, customProp}) => (normalizedPropList[customProp] = cssProp) + ); + const INLINE_STYLE_ATTRS = [ + "style", + "fill", + "stop-color", + "stroke", + "bgcolor", + "color", + "background" + ]; + const INLINE_STYLE_SELECTOR = INLINE_STYLE_ATTRS.map( + (attr) => `[${attr}]` + ).join(", "); + function getInlineOverrideStyle() { + const allOverrides = overridesList.concat( + Object.values(shorthandOverrides) + ); + return allOverrides + .map(({dataAttr, customProp, cssProp}) => { + return [ + `[${dataAttr}] {`, + ` ${cssProp}: var(${customProp}) !important;`, + "}" + ].join("\n"); + }) + .concat([ + "[data-darkreader-inline-invert] {", + " filter: invert(100%) hue-rotate(180deg);", + "}" + ]) + .join("\n"); + } + function getInlineStyleElements(root) { + const results = []; + if (root instanceof Element && root.matches(INLINE_STYLE_SELECTOR)) { + results.push(root); + } + if ( + root instanceof Element || + (isShadowDomSupported && root instanceof ShadowRoot) || + root instanceof Document + ) { + push(results, root.querySelectorAll(INLINE_STYLE_SELECTOR)); + } + return results; + } + const treeObservers = new Map(); + const attrObservers = new Map(); + let asyncCancelled = true; + function isAsyncCancelled() { + return asyncCancelled; + } + function watchForInlineStyles(elementStyleDidChange, shadowRootDiscovered) { + deepWatchForInlineStyles( + document, + elementStyleDidChange, + shadowRootDiscovered + ); + iterateShadowHosts(document.documentElement, (host) => { + deepWatchForInlineStyles( + host.shadowRoot, + elementStyleDidChange, + shadowRootDiscovered + ); + }); + } + function deepWatchForInlineStyles( + root, + elementStyleDidChange, + shadowRootDiscovered + ) { + if (treeObservers.has(root)) { + treeObservers.get(root).disconnect(); + attrObservers.get(root).disconnect(); + } + const discoveredNodes = new WeakSet(); + function discoverNodes(node) { + getInlineStyleElements(node).forEach((el) => { + if (discoveredNodes.has(el)) { + return; + } + discoveredNodes.add(el); + elementStyleDidChange(el); + }); + iterateShadowHosts(node, (n) => { + if (discoveredNodes.has(n)) { + return; + } + discoveredNodes.add(n); + shadowRootDiscovered(n.shadowRoot); + deepWatchForInlineStyles( + n.shadowRoot, + elementStyleDidChange, + shadowRootDiscovered + ); + }); + } + const treeObserver = createOptimizedTreeObserver(root, { + onMinorMutations: (_root, {additions}) => { + additions.forEach((added) => discoverNodes(added)); + variablesStore.matchVariablesAndDependents(); + }, + onHugeMutations: () => { + discoverNodes(root); + variablesStore.matchVariablesAndDependents(); + } + }); + treeObservers.set(root, treeObserver); + let attemptCount = 0; + let start = null; + const ATTEMPTS_INTERVAL = getDuration({seconds: 10}); + const RETRY_TIMEOUT = getDuration({seconds: 2}); + const MAX_ATTEMPTS_COUNT = 50; + let cache = []; + let timeoutId = null; + const handleAttributeMutations = throttle((mutations) => { + const handledTargets = new Set(); + mutations.forEach((m) => { + const target = m.target; + if (handledTargets.has(target)) { + return; + } + if (INLINE_STYLE_ATTRS.includes(m.attributeName)) { + handledTargets.add(target); + elementStyleDidChange(target); + } + }); + variablesStore.matchVariablesAndDependents(); + }); + const attrObserver = new MutationObserver((mutations) => { + if (timeoutId) { + cache.push(...mutations); + return; + } + attemptCount++; + const now = Date.now(); + if (start == null) { + start = now; + } else if (attemptCount >= MAX_ATTEMPTS_COUNT) { + if (now - start < ATTEMPTS_INTERVAL) { + timeoutId = setTimeout(() => { + start = null; + attemptCount = 0; + timeoutId = null; + const attributeCache = cache; + cache = []; + handleAttributeMutations(attributeCache); + }, RETRY_TIMEOUT); + cache.push(...mutations); + return; + } + start = now; + attemptCount = 1; + } + handleAttributeMutations(mutations); + }); + attrObserver.observe(root, { + attributes: true, + attributeFilter: INLINE_STYLE_ATTRS.concat( + overridesList.map(({dataAttr}) => dataAttr) + ), + subtree: true + }); + attrObservers.set(root, attrObserver); + } + function stopWatchingForInlineStyles() { + asyncCancelled = true; + treeObservers.forEach((o) => o.disconnect()); + attrObservers.forEach((o) => o.disconnect()); + treeObservers.clear(); + attrObservers.clear(); + inlineStringValueCache.clear(); + } + const inlineStyleCache = new WeakMap(); + const svgInversionCache = new WeakSet(); + const svgAnalysisConditionCache = new WeakMap(); + const themeProps = ["brightness", "contrast", "grayscale", "sepia", "mode"]; + function shouldAnalyzeSVGAsImage(svg) { + if (svgAnalysisConditionCache.has(svg)) { + return svgAnalysisConditionCache.get(svg); + } + const shouldAnalyze = Boolean( + svg && + (svg.getAttribute("class")?.includes("logo") || + svg.parentElement?.getAttribute("class")?.includes("logo")) + ); + svgAnalysisConditionCache.set(svg, shouldAnalyze); + return shouldAnalyze; + } + let prevTheme$1 = null; + let themeKey = ""; + function getThemeKey(theme) { + if (theme === prevTheme$1) { + return themeKey; + } + themeKey = ""; + for (let i = 0; i < themeProps.length; i++) { + const prop = themeProps[i]; + themeKey += `${prop}="${theme[prop]}" `; + } + prevTheme$1 = theme; + return themeKey; + } + function getInlineStyleCacheKey(el, theme) { + const attrKey = INLINE_STYLE_ATTRS.map( + (attr) => `${attr}="${el.getAttribute(attr)}"` + ); + return `${attrKey} ${getThemeKey(theme)}`; + } + function shouldIgnoreInlineStyle(element, selectors) { + for (let i = 0, len = selectors.length; i < len; i++) { + const ingnoredSelector = selectors[i]; + if (element.matches(ingnoredSelector)) { + return true; + } + } + return false; + } + const LOOP_DETECTION_THRESHOLD = 1000; + const MAX_LOOP_CYCLES = 10; + const elementsLastChanges = new WeakMap(); + const elementsLoopCycles = new WeakMap(); + const SMALL_SVG_THRESHOLD = 32; + const svgNodesRoots = new WeakMap(); + const svgRootSizeTestResults = new WeakMap(); + function getSVGElementRoot(svgElement) { + if (!svgElement) { + return null; + } + if (svgNodesRoots.has(svgElement)) { + return svgNodesRoots.get(svgElement); + } + if (svgElement instanceof SVGSVGElement) { + return svgElement; + } + const parent = svgElement.parentNode; + const root = getSVGElementRoot(parent); + svgNodesRoots.set(svgElement, root); + return root; + } + const inlineStringValueCache = new Map(); + function overrideInlineStyle( + element, + theme, + ignoreInlineSelectors, + ignoreImageSelectors + ) { + if (elementsLastChanges.has(element)) { + if ( + Date.now() - elementsLastChanges.get(element) < + LOOP_DETECTION_THRESHOLD + ) { + const cycles = elementsLoopCycles.get(element) ?? 0; + elementsLoopCycles.set(element, cycles + 1); + } else { + elementsLoopCycles.delete(element); + } + if ((elementsLoopCycles.get(element) ?? 0) >= MAX_LOOP_CYCLES) { + return; + } + } + if (element.parentElement?.dataset.nodeViewContent) { + return; + } + const cacheKey = getInlineStyleCacheKey(element, theme); + if (cacheKey === inlineStyleCache.get(element)) { + return; + } + const unsetProps = new Set(Object.keys(overrides)); + function setCustomProp(targetCSSProp, modifierCSSProp, cssVal) { + const cachedStringValue = inlineStringValueCache + .get(modifierCSSProp) + ?.get(cssVal); + if (cachedStringValue) { + setStaticValue(cachedStringValue); + return; + } + asyncCancelled = false; + const mod = getModifiableCSSDeclaration( + modifierCSSProp, + cssVal, + {style: element.style}, + variablesStore, + ignoreImageSelectors, + isAsyncCancelled + ); + if (!mod) { + return; + } + function setStaticValue(value) { + const {customProp, dataAttr} = + overrides[targetCSSProp] ?? + shorthandOverrides[targetCSSProp]; + element.style.setProperty(customProp, value); + if (!element.hasAttribute(dataAttr)) { + element.setAttribute(dataAttr, ""); + } + unsetProps.delete(targetCSSProp); + } + function setVarDeclaration(mod) { + let prevDeclarations = []; + function setProps(declarations) { + prevDeclarations.forEach(({property}) => { + element.style.removeProperty(property); + }); + declarations.forEach(({property, value}) => { + if (!(value instanceof Promise)) { + element.style.setProperty(property, value); + } + }); + prevDeclarations = declarations; + } + setProps(mod.declarations); + mod.onTypeChange.addListener(setProps); + } + function setAsyncValue(promise, sourceValue) { + promise.then((value) => { + if ( + value && + targetCSSProp === "background" && + value.startsWith("var(--darkreader-bg--") + ) { + setStaticValue(value); + } + if (value && targetCSSProp === "background-image") { + if ( + (element === document.documentElement || + element === document.body) && + value === sourceValue + ) { + value = "none"; + } + setStaticValue(value); + } + inlineStyleCache.set( + element, + getInlineStyleCacheKey(element, theme) + ); + }); + } + const value = + typeof mod.value === "function" ? mod.value(theme) : mod.value; + if (typeof value === "string") { + setStaticValue(value); + if (!inlineStringValueCache.has(modifierCSSProp)) { + inlineStringValueCache.set(modifierCSSProp, new Map()); + } + inlineStringValueCache.get(modifierCSSProp).set(cssVal, value); + } else if (value instanceof Promise) { + setAsyncValue(value, cssVal); + } else if (typeof value === "object") { + setVarDeclaration(value); + } + } + if (ignoreInlineSelectors.length > 0) { + if (shouldIgnoreInlineStyle(element, ignoreInlineSelectors)) { + unsetProps.forEach((cssProp) => { + element.removeAttribute(overrides[cssProp].dataAttr); + }); + return; + } + } + const isSVGElement = element instanceof SVGElement; + const svg = isSVGElement + ? (element.ownerSVGElement ?? + (element instanceof SVGSVGElement ? element : null)) + : null; + if (isSVGElement && theme.mode === 1 && svg) { + if (svgInversionCache.has(svg)) { + return; + } + if (shouldAnalyzeSVGAsImage(svg)) { + svgInversionCache.add(svg); + const analyzeSVGAsImage = () => { + let svgString = svg.outerHTML; + svgString = svgString.replaceAll( + '', + "" + ); + const dataURL = `data:image/svg+xml;base64,${btoa(svgString)}`; + getImageDetails(dataURL).then((details) => { + if ( + (details.isDark && details.isTransparent) || + (details.isLarge && + details.isLight && + !details.isTransparent) + ) { + svg.setAttribute( + "data-darkreader-inline-invert", + "" + ); + } else { + svg.removeAttribute( + "data-darkreader-inline-invert" + ); + } + }); + }; + analyzeSVGAsImage(); + if (!isDOMReady()) { + addDOMReadyListener(analyzeSVGAsImage); + } + return; + } + } + if (element.hasAttribute("bgcolor")) { + let value = element.getAttribute("bgcolor"); + if ( + value.match(/^[0-9a-f]{3}$/i) || + value.match(/^[0-9a-f]{6}$/i) + ) { + value = `#${value}`; + } + setCustomProp("background-color", "background-color", value); + } + if ( + (element === document.documentElement || + element === document.body) && + element.hasAttribute("background") && + element.getAttribute("background") !== "" + ) { + const url = getAbsoluteURL( + location.href, + element.getAttribute("background") ?? "" + ); + const value = `url("${url}")`; + setCustomProp("background-image", "background-image", value); + } + if (element.hasAttribute("color") && element.rel !== "mask-icon") { + let value = element.getAttribute("color"); + if ( + value.match(/^[0-9a-f]{3}$/i) || + value.match(/^[0-9a-f]{6}$/i) + ) { + value = `#${value}`; + } else if (value.match(/^#?[0-9a-f]{4}$/i)) { + const hex = value.startsWith("#") ? value.substring(1) : value; + value = `#${hex}00`; + } + setCustomProp("color", "color", value); + } + if (isSVGElement) { + if (element.hasAttribute("fill")) { + const value = element.getAttribute("fill"); + if (value !== "none" && value !== "currentColor") { + if (!(element instanceof SVGTextElement)) { + const handleSVGElement = () => { + let isSVGSmall = false; + const root = getSVGElementRoot(element); + if (!root) { + return; + } + if (svgRootSizeTestResults.has(root)) { + isSVGSmall = svgRootSizeTestResults.get(root); + } else { + const svgBounds = root.getBoundingClientRect(); + isSVGSmall = + svgBounds.width * svgBounds.height <= + SMALL_SVG_THRESHOLD ** 2; + svgRootSizeTestResults.set(root, isSVGSmall); + } + let isBg; + if (isSVGSmall) { + isBg = false; + } else { + const {width, height} = + element.getBoundingClientRect(); + isBg = + width > SMALL_SVG_THRESHOLD || + height > SMALL_SVG_THRESHOLD; + } + setCustomProp( + "fill", + isBg ? "background-color" : "color", + value + ); + }; + if (isReadyStateComplete()) { + handleSVGElement(); + } else { + addReadyStateCompleteListener(handleSVGElement); + } + } else { + setCustomProp("fill", "color", value); + } + } + } + if (element.hasAttribute("stop-color")) { + setCustomProp( + "stop-color", + "background-color", + element.getAttribute("stop-color") + ); + } + } + if (element.hasAttribute("stroke")) { + const value = element.getAttribute("stroke"); + setCustomProp( + "stroke", + element instanceof SVGLineElement || + element instanceof SVGTextElement + ? "border-color" + : "color", + value + ); + } + element.style && + iterateCSSDeclarations(element.style, (property, value) => { + if (property === "background-image" && value.includes("url")) { + if ( + element === document.documentElement || + element === document.body + ) { + setCustomProp(property, property, value); + } + return; + } + if ( + overrides.hasOwnProperty(property) || + (property.startsWith("--") && !normalizedPropList[property]) + ) { + setCustomProp(property, property, value); + } else if ( + shorthandOverrides[property] && + value.includes("var(") + ) { + setCustomProp(property, property, value); + } else { + const overriddenProp = normalizedPropList[property]; + if ( + overriddenProp && + !element.style.getPropertyValue(overriddenProp) && + !element.hasAttribute(overriddenProp) + ) { + if ( + overriddenProp === "background-color" && + element.hasAttribute("bgcolor") + ) { + return; + } + element.style.setProperty(property, ""); + } + } + }); + if ( + element.style && + element instanceof SVGTextElement && + element.style.fill + ) { + setCustomProp( + "fill", + "color", + element.style.getPropertyValue("fill") + ); + } + if (element.getAttribute("style")?.includes("--")) { + variablesStore.addInlineStyleForMatching(element.style); + } + forEach(unsetProps, (cssProp) => { + element.removeAttribute(overrides[cssProp].dataAttr); + }); + inlineStyleCache.set(element, getInlineStyleCacheKey(element, theme)); + elementsLastChanges.set(element, Date.now()); + } + + const metaThemeColorName = "theme-color"; + const metaThemeColorSelector = `meta[name="${metaThemeColorName}"]`; + let srcMetaThemeColor = null; + let observer = null; + function changeMetaThemeColor(meta, theme) { + srcMetaThemeColor = srcMetaThemeColor || meta.content; + const color = parseColorWithCache(srcMetaThemeColor); + if (!color) { + logWarn("Invalid meta color", color); + return; + } + meta.content = modifyBackgroundColor(color, theme, false); + } + function changeMetaThemeColorWhenAvailable(theme) { + const meta = document.querySelector(metaThemeColorSelector); + if (meta) { + changeMetaThemeColor(meta, theme); + } else { + if (observer) { + observer.disconnect(); + } + observer = new MutationObserver((mutations) => { + loop: for (let i = 0; i < mutations.length; i++) { + const {addedNodes} = mutations[i]; + for (let j = 0; j < addedNodes.length; j++) { + const node = addedNodes[j]; + if ( + node instanceof HTMLMetaElement && + node.name === metaThemeColorName + ) { + observer.disconnect(); + observer = null; + changeMetaThemeColor(node, theme); + break loop; + } + } + } + }); + observer.observe(document.head, {childList: true}); + } + } + function restoreMetaThemeColor() { + if (observer) { + observer.disconnect(); + observer = null; + } + const meta = document.querySelector(metaThemeColorSelector); + if (meta && srcMetaThemeColor) { + meta.content = srcMetaThemeColor; + } + } + + const filterSelectors = { + invert: new Set(), + dim: new Set(), + none: new Set() + }; + function addFilterSelector(selector, type) { + if (!selector) { + return; + } + const selectors = filterSelectors[type]; + let changed = false; + selector.split(",").forEach((part) => { + const s = part.trim(); + if (!s || selectors.has(s)) { + return; + } + for (const existing of selectors) { + if (isSelectorWithin(s, existing)) { + return; + } + } + for (const existing of [...selectors]) { + if (isSelectorWithin(existing, s)) { + selectors.delete(existing); + } + } + selectors.add(s); + changed = true; + }); + return changed; + } + function isSelectorWithin(sub, parent) { + const parentLength = parent.length; + const subLength = sub.length; + if (subLength < parentLength || !sub.startsWith(parent)) { + return false; + } + if (subLength === parentLength) { + return true; + } + let i = parentLength; + const c = sub[i]; + if (c === "." || c === ":" || c === "#" || c === "[" || c === ">") { + return true; + } + if (c === "+" || c === "~" || c !== " ") { + return false; + } + while (sub[i] === " ") { + i++; + } + return sub[i] !== "+" && sub[i] !== "~"; + } + function cleanFilterSelectors() { + filterSelectors.invert.clear(); + filterSelectors.dim.clear(); + filterSelectors.none.clear(); + } + + const cssCommentsRegex = /\/\*[\s\S]*?\*\//g; + function removeCSSComments(cssText) { + return cssText.replace(cssCommentsRegex, ""); + } + + let canUseSheetProxy = false; + document.addEventListener( + "__darkreader__inlineScriptsAllowed", + () => (canUseSheetProxy = true), + {once: true} + ); + function createSheetWatcher( + element, + safeGetSheetRules, + callback, + isCancelled + ) { + let rafSheetWatcher = null; + function watchForSheetChanges() { + watchForSheetChangesUsingProxy(); + if (!(canUseSheetProxy && element.sheet)) { + rafSheetWatcher = createRAFSheetWatcher( + element, + safeGetSheetRules, + callback, + isCancelled + ); + rafSheetWatcher.start(); + } + } + let areSheetChangesPending = false; + function onSheetChange() { + canUseSheetProxy = true; + rafSheetWatcher?.stop(); + if (areSheetChangesPending) { + return; + } + function handleSheetChanges() { + areSheetChangesPending = false; + if (isCancelled()) { + return; + } + callback(); + } + areSheetChangesPending = true; + queueMicrotask(handleSheetChanges); + } + function watchForSheetChangesUsingProxy() { + element.addEventListener( + "__darkreader__updateSheet", + onSheetChange + ); + } + function stopWatchingForSheetChangesUsingProxy() { + element.removeEventListener( + "__darkreader__updateSheet", + onSheetChange + ); + } + function stopWatchingForSheetChanges() { + stopWatchingForSheetChangesUsingProxy(); + rafSheetWatcher?.stop(); + } + return { + start: watchForSheetChanges, + stop: stopWatchingForSheetChanges + }; + } + function createRAFSheetWatcher( + element, + safeGetSheetRules, + callback, + isCancelled + ) { + let rulesChangeKey = null; + let rulesCheckFrameId = null; + function getRulesChangeKey() { + const rules = safeGetSheetRules(); + return rules ? rules.length : null; + } + function didRulesKeyChange() { + return getRulesChangeKey() !== rulesChangeKey; + } + function watchForSheetChangesUsingRAF() { + rulesChangeKey = getRulesChangeKey(); + stopWatchingForSheetChangesUsingRAF(); + const checkForUpdate = () => { + const cancelled = isCancelled(); + if (!cancelled && didRulesKeyChange()) { + rulesChangeKey = getRulesChangeKey(); + callback(); + } + if (cancelled || (canUseSheetProxy && element.sheet)) { + stopWatchingForSheetChangesUsingRAF(); + return; + } + rulesCheckFrameId = requestAnimationFrame(checkForUpdate); + }; + checkForUpdate(); + } + function stopWatchingForSheetChangesUsingRAF() { + rulesCheckFrameId && cancelAnimationFrame(rulesCheckFrameId); + } + return { + start: watchForSheetChangesUsingRAF, + stop: stopWatchingForSheetChangesUsingRAF + }; + } + + const STYLE_SELECTOR = 'style, link[rel*="stylesheet" i]:not([disabled])'; + let ignoredCSSURLPatterns = []; + function setIgnoredCSSURLs(patterns) { + ignoredCSSURLPatterns = patterns || []; + } + function shouldIgnoreCSSURL(url) { + if (!url || ignoredCSSURLPatterns.length === 0) { + return false; + } + for (const pattern of ignoredCSSURLPatterns) { + if (pattern.startsWith("^")) { + if (url.startsWith(pattern.slice(1))) { + return true; + } + } else if (pattern.endsWith("$")) { + if (url.endsWith(pattern.slice(0, -1))) { + return true; + } + } else if (url.includes(pattern)) { + return true; + } + } + return false; + } + const hostsBreakingOnSVGStyleOverride = [ + "account.containerstore.com", + "containerstore.com", + "www.onet.pl" + ]; + function shouldManageStyle(element) { + return ( + (element instanceof HTMLStyleElement || + (element instanceof SVGStyleElement && + !hostsBreakingOnSVGStyleOverride.includes( + location.hostname + )) || + (element instanceof HTMLLinkElement && + Boolean(element.rel) && + element.rel.toLowerCase().includes("stylesheet") && + Boolean(element.href) && + !element.disabled && + (isFirefox + ? !element.href.startsWith("moz-extension://") + : true) && + !shouldIgnoreCSSURL(element.href))) && + !element.classList.contains("darkreader") && + !ignoredMedia.includes(element.media.toLowerCase()) && + !element.classList.contains("stylus") + ); + } + function getManageableStyles(node, results = [], deep = true) { + if (shouldManageStyle(node)) { + results.push(node); + } else if ( + node instanceof Element || + (isShadowDomSupported && node instanceof ShadowRoot) || + node === document + ) { + forEach(node.querySelectorAll(STYLE_SELECTOR), (style) => + getManageableStyles(style, results, false) + ); + if (deep && (node.children?.length > 0 || node.shadowRoot)) { + iterateShadowHosts(node, (host) => + getManageableStyles(host.shadowRoot, results, false) + ); + } + } + return results; + } + const syncStyleSet = new WeakSet(); + const corsCopies = new WeakMap(); + const corsCopiesTextLengths = new WeakMap(); + let loadingLinkCounter = 0; + const rejectorsForLoadingLinks = new Map(); + function cleanLoadingLinks() { + rejectorsForLoadingLinks.clear(); + } + function manageStyle(element, {update, loadingStart, loadingEnd}) { + const inMode = getStyleInjectionMode(); + let syncStyle = null; + if (inMode === "next") { + const prevStyles = []; + let next = element; + while ( + (next = next.nextElementSibling) && + next.matches(".darkreader") + ) { + prevStyles.push(next); + } + syncStyle = + prevStyles.find( + (el) => + el.matches(".darkreader--sync") && !syncStyleSet.has(el) + ) || null; + } + let syncStylePositionWatcher = null; + let cancelAsyncOperations = false; + let isOverrideEmpty = true; + const isAsyncCancelled = () => cancelAsyncOperations; + const sheetModifier = createStyleSheetModifier(); + const observer = new MutationObserver((mutations) => { + if ( + mutations.some((m) => m.type === "characterData") && + containsCSSImport() + ) { + const cssText = (element.textContent ?? "").trim(); + createOrUpdateCORSCopy(cssText, location.href).then(update); + } else { + update(); + } + }); + const observerOptions = { + attributes: true, + childList: true, + subtree: true, + characterData: true + }; + function containsCSSImport() { + if (!(element instanceof HTMLStyleElement)) { + return false; + } + const cssText = removeCSSComments(element.textContent ?? "").trim(); + return cssText.match(cssImportRegex); + } + function hasImports(cssRules, checkCrossOrigin) { + let result = false; + if (cssRules) { + let rule; + cssRulesLoop: for ( + let i = 0, len = cssRules.length; + i < len; + i++ + ) { + rule = cssRules[i]; + if (rule.href) { + if (checkCrossOrigin) { + if ( + !rule.href.startsWith( + "https://fonts.googleapis.com/" + ) && + rule.href.startsWith("http") && + !rule.href.startsWith(location.origin) + ) { + result = true; + break cssRulesLoop; + } + } else { + result = true; + break cssRulesLoop; + } + } + } + } + return result; + } + function getRulesSync() { + if (corsCopies.has(element)) { + return corsCopies.get(element).cssRules; + } + if (containsCSSImport()) { + return null; + } + const cssRules = safeGetSheetRules(); + if ( + element instanceof HTMLLinkElement && + !isRelativeHrefOnAbsolutePath(element.href) && + hasImports(cssRules, false) + ) { + return null; + } + if (hasImports(cssRules, true)) { + return null; + } + !cssRules && + logWarn("[getRulesSync] cssRules is null, trying again."); + return cssRules; + } + function insertStyle() { + if (inMode === "next") { + if (element.nextSibling !== syncStyle) { + element.parentNode.insertBefore( + syncStyle, + element.nextSibling + ); + } + } else if (inMode === "away") { + injectStyleAway(syncStyle); + } + } + function createSyncStyle() { + syncStyle = + element instanceof SVGStyleElement + ? document.createElementNS( + "http://www.w3.org/2000/svg", + "style" + ) + : document.createElement("style"); + syncStyle.classList.add("darkreader"); + syncStyle.classList.add("darkreader--sync"); + syncStyle.media = "screen"; + if (element.title) { + syncStyle.title = element.title; + } + syncStyleSet.add(syncStyle); + } + let isLoadingRules = false; + let wasLoadingError = false; + const loadingLinkId = ++loadingLinkCounter; + async function getRulesAsync() { + let cssText; + let cssBasePath; + if (element instanceof HTMLLinkElement) { + let [cssRules, accessError] = getRulesOrError(); + if (accessError) { + logWarn(accessError); + } + if ( + (isSafari && !element.sheet) || + (!isSafari && !cssRules && !accessError) || + isStillLoadingError(accessError) + ) { + try { + logInfo( + `Linkelement ${loadingLinkId} is not loaded yet and thus will be await for`, + element + ); + await linkLoading(element, loadingLinkId); + } catch (err) { + logWarn(err); + wasLoadingError = true; + } + if (cancelAsyncOperations) { + return null; + } + [cssRules, accessError] = getRulesOrError(); + if (accessError) { + logWarn(accessError); + } + } + if (cssRules) { + if (!hasImports(cssRules, false)) { + return cssRules; + } + } + try { + cssText = await loadText(element.href); + } catch (err) { + logWarn(err); + cssText = ""; + } + cssBasePath = getCSSBaseBath(element.href); + if (cancelAsyncOperations) { + return null; + } + } else if (containsCSSImport()) { + cssText = element.textContent.trim(); + cssBasePath = getCSSBaseBath(location.href); + } else { + return null; + } + await createOrUpdateCORSCopy(cssText, cssBasePath); + if (corsCopies.has(element)) { + return corsCopies.get(element).cssRules; + } + return null; + } + async function createOrUpdateCORSCopy(cssText, cssBasePath) { + if (cssText) { + try { + const fullCSSText = await replaceCSSImports( + cssText, + cssBasePath + ); + if (corsCopies.has(element)) { + if ( + (corsCopiesTextLengths.get(element) ?? 0) < + fullCSSText.length + ) { + corsCopies.get(element).replaceSync(fullCSSText); + corsCopiesTextLengths.set( + element, + fullCSSText.length + ); + } + } else { + const corsCopy = new CSSStyleSheet(); + corsCopy.replaceSync(fullCSSText); + corsCopies.set(element, corsCopy); + } + } catch (err) { + logWarn(err); + } + } + } + function details(options) { + const rules = getRulesSync(); + if (!rules) { + if (options.secondRound) { + logWarn( + "Detected dead-lock at details(), returning early to prevent it." + ); + return null; + } + if (isLoadingRules || wasLoadingError) { + return null; + } + isLoadingRules = true; + loadingStart(); + getRulesAsync() + .then((results) => { + isLoadingRules = false; + loadingEnd(); + if (results) { + update(); + } + }) + .catch((err) => { + logWarn(err); + isLoadingRules = false; + loadingEnd(); + }); + return null; + } + return {rules}; + } + let forceRenderStyle = false; + function render(theme, ignoreImageAnalysis) { + const rules = getRulesSync(); + if (!rules) { + return; + } + cancelAsyncOperations = false; + function removeCSSRulesFromSheet(sheet) { + if (!sheet) { + return; + } + for (let i = sheet.cssRules.length - 1; i >= 0; i--) { + sheet.deleteRule(i); + } + } + function prepareOverridesSheet() { + if (!syncStyle) { + createSyncStyle(); + } + syncStylePositionWatcher && syncStylePositionWatcher.stop(); + insertStyle(); + if (syncStyle.sheet == null) { + syncStyle.textContent = ""; + } + const sheet = syncStyle.sheet; + removeCSSRulesFromSheet(sheet); + if (syncStylePositionWatcher) { + syncStylePositionWatcher.run(); + } else if (inMode === "next") { + syncStylePositionWatcher = watchForNodePosition( + syncStyle, + "prev-sibling", + () => { + forceRenderStyle = true; + buildOverrides(); + } + ); + } + return syncStyle.sheet; + } + function buildOverrides() { + const force = forceRenderStyle; + forceRenderStyle = false; + sheetModifier.modifySheet({ + prepareSheet: prepareOverridesSheet, + sourceCSSRules: rules, + theme, + ignoreImageAnalysis, + force, + isAsyncCancelled + }); + isOverrideEmpty = + !syncStyle.sheet || syncStyle.sheet.cssRules.length === 0; + if (sheetModifier.shouldRebuildStyle()) { + addReadyStateCompleteListener(() => update()); + } + } + buildOverrides(); + } + function getRulesOrError() { + try { + if (element.sheet == null) { + return [null, null]; + } + return [element.sheet.cssRules, null]; + } catch (err) { + return [null, err]; + } + } + function isStillLoadingError(error) { + return error && error.message && error.message.includes("loading"); + } + function safeGetSheetRules() { + const [cssRules, err] = getRulesOrError(); + if (err) { + logWarn(err); + return null; + } + return cssRules; + } + const sheetChangeWatcher = createSheetWatcher( + element, + safeGetSheetRules, + update, + isAsyncCancelled + ); + function pause() { + observer.disconnect(); + cancelAsyncOperations = true; + syncStylePositionWatcher && syncStylePositionWatcher.stop(); + sheetChangeWatcher.stop(); + } + function destroy() { + pause(); + corsCopies.delete(element); + removeNode(syncStyle); + loadingEnd(); + if (rejectorsForLoadingLinks.has(loadingLinkId)) { + const reject = rejectorsForLoadingLinks.get(loadingLinkId); + rejectorsForLoadingLinks.delete(loadingLinkId); + reject && reject(); + } + } + function watch() { + observer.observe(element, observerOptions); + if (element instanceof HTMLStyleElement) { + sheetChangeWatcher.start(); + } + } + const maxMoveCount = 10; + let moveCount = 0; + function restore() { + if (!syncStyle) { + return; + } + moveCount++; + if (moveCount > maxMoveCount) { + logWarn("Style sheet was moved multiple times", element); + return; + } + logWarn("Restore style", syncStyle, element); + insertStyle(); + syncStylePositionWatcher && syncStylePositionWatcher.skip(); + if (!isOverrideEmpty) { + forceRenderStyle = true; + update(); + } + } + return { + details, + render, + pause, + destroy, + watch, + restore + }; + } + async function linkLoading(link, loadingId) { + return new Promise((resolve, reject) => { + const cleanUp = () => { + link.removeEventListener("load", onLoad); + link.removeEventListener("error", onError); + rejectorsForLoadingLinks.delete(loadingId); + }; + const onLoad = () => { + cleanUp(); + resolve(); + }; + const onError = () => { + cleanUp(); + reject( + `Linkelement ${loadingId} couldn't be loaded. ${link.href}` + ); + }; + rejectorsForLoadingLinks.set(loadingId, () => { + cleanUp(); + reject(); + }); + link.addEventListener("load", onLoad, {passive: true}); + link.addEventListener("error", onError, {passive: true}); + if (!link.href) { + onError(); + } + }); + } + function getCSSImportURL(importDeclaration) { + return getCSSURLValue( + importDeclaration + .substring(7) + .trim() + .replace(/;$/, "") + .replace(/screen$/, "") + ); + } + async function loadText(url) { + if (url.startsWith("data:")) { + return await (await fetch(url)).text(); + } + const cache = readCSSFetchCache(url); + if (cache) { + return cache; + } + const parsedURL = new URL(url); + let text; + if (parsedURL.origin === location.origin) { + text = await loadAsText(url, "text/css", location.origin); + } else { + text = await bgFetch({ + url, + responseType: "text", + mimeType: "text/css", + origin: location.origin + }); + } + if (parsedURL.origin === location.origin) { + writeCSSFetchCache(url, text); + } + return text; + } + const MAX_CSS_IMPORT_DEPTH = 32; + async function replaceCSSImports( + cssText, + basePath, + cache = new Map(), + depth = 0 + ) { + if (depth > MAX_CSS_IMPORT_DEPTH) { + return ""; + } + cssText = removeCSSComments(cssText); + cssText = replaceCSSFontFace(cssText); + cssText = replaceCSSRelativeURLsWithAbsolute(cssText, basePath); + const importMatches = getMatchesWithOffsets(cssImportRegex, cssText); + let prev = null; + let shouldIgnoreImportsInBetween = false; + let diff = 0; + for (const match of importMatches) { + let importedCSS; + const prevImportEnd = prev ? prev.offset + prev.text.length : 0; + const nextImportStart = match.offset; + const openBraceIndex = cssText.indexOf("{", prevImportEnd); + const closeBraceIndex = cssText.indexOf("}", prevImportEnd); + if ( + shouldIgnoreImportsInBetween || + (openBraceIndex >= 0 && + openBraceIndex < nextImportStart && + closeBraceIndex >= 0 && + closeBraceIndex < nextImportStart) + ) { + shouldIgnoreImportsInBetween = true; + importedCSS = ""; + } else { + const importURL = getCSSImportURL(match.text); + const absoluteURL = getAbsoluteURL(basePath, importURL); + if (cache.has(absoluteURL)) { + importedCSS = cache.get(absoluteURL); + } else { + try { + importedCSS = await loadText(absoluteURL); + cache.set(absoluteURL, importedCSS); + importedCSS = await replaceCSSImports( + importedCSS, + getCSSBaseBath(absoluteURL), + cache, + depth + 1 + ); + } catch (err) { + logWarn(err); + importedCSS = ""; + } + } + } + cssText = + cssText.substring(0, match.offset + diff) + + importedCSS + + cssText.substring(match.offset + match.text.length + diff); + diff += importedCSS.length - match.text.length; + prev = match; + } + cssText = cssText.trim(); + return cssText; + } + + function injectProxy( + enableStyleSheetsProxy, + enableCustomElementRegistryProxy + ) { + document.dispatchEvent( + new CustomEvent("__darkreader__inlineScriptsAllowed") + ); + const cleaners = []; + function cleanUp() { + cleaners.forEach((clean) => clean()); + cleaners.splice(0); + } + function documentEventListener(type, listener, options) { + document.addEventListener(type, listener, options); + cleaners.push(() => document.removeEventListener(type, listener)); + } + function disableConflictingPlugins() { + const disableWPDarkMode = () => { + if (window?.WPDarkMode?.deactivate) { + window.WPDarkMode.deactivate(); + } + }; + disableWPDarkMode(); + } + documentEventListener("__darkreader__cleanUp", cleanUp); + documentEventListener( + "__darkreader__disableConflictingPlugins", + disableConflictingPlugins + ); + function overrideProperty(cls, prop, overrides) { + const proto = cls.prototype; + const oldDescriptor = Object.getOwnPropertyDescriptor(proto, prop); + if (!oldDescriptor) { + return; + } + const newDescriptor = {...oldDescriptor}; + Object.keys(overrides).forEach((key) => { + const factory = overrides[key]; + newDescriptor[key] = factory(oldDescriptor[key]); + }); + Object.defineProperty(proto, prop, newDescriptor); + cleaners.push(() => + Object.defineProperty(proto, prop, oldDescriptor) + ); + } + function override(cls, prop, factory) { + overrideProperty(cls, prop, {value: factory}); + } + function isDRElement(element) { + return element?.classList?.contains("darkreader"); + } + function isDRSheet(sheet) { + return isDRElement(sheet.ownerNode); + } + const updateSheetEvent = new CustomEvent("__darkreader__updateSheet"); + const adoptedSheetChangeEvent = new CustomEvent( + "__darkreader__adoptedStyleSheetChange" + ); + const shadowDomAttachingEvent = new CustomEvent( + "__darkreader__shadowDomAttaching", + {bubbles: true} + ); + const adoptedSheetOwners = new WeakMap(); + const adoptedDeclarationSheets = new WeakMap(); + function onAdoptedSheetChange(sheet) { + const owners = adoptedSheetOwners.get(sheet); + owners?.forEach((node) => { + if (node.isConnected) { + node.dispatchEvent(adoptedSheetChangeEvent); + } else { + owners.delete(node); + } + }); + } + function reportSheetChange(sheet) { + if (sheet.ownerNode && !isDRSheet(sheet)) { + sheet.ownerNode.dispatchEvent(updateSheetEvent); + } + if (adoptedSheetOwners.has(sheet)) { + onAdoptedSheetChange(sheet); + } + } + function reportSheetChangeAsync(sheet, promise) { + const {ownerNode} = sheet; + if ( + ownerNode && + !isDRSheet(sheet) && + promise && + promise instanceof Promise + ) { + promise.then(() => ownerNode.dispatchEvent(updateSheetEvent)); + } + if (adoptedSheetOwners.has(sheet)) { + if (promise && promise instanceof Promise) { + promise.then(() => onAdoptedSheetChange(sheet)); + } + } + } + override( + CSSStyleSheet, + "addRule", + (native) => + function (selector, style, index) { + native.call(this, selector, style, index); + reportSheetChange(this); + return -1; + } + ); + override( + CSSStyleSheet, + "insertRule", + (native) => + function (rule, index) { + const returnValue = native.call(this, rule, index); + reportSheetChange(this); + return returnValue; + } + ); + override( + CSSStyleSheet, + "deleteRule", + (native) => + function (index) { + native.call(this, index); + reportSheetChange(this); + } + ); + override( + CSSStyleSheet, + "removeRule", + (native) => + function (index) { + native.call(this, index); + reportSheetChange(this); + } + ); + override( + CSSStyleSheet, + "replace", + (native) => + function (cssText) { + const returnValue = native.call(this, cssText); + reportSheetChangeAsync(this, returnValue); + return returnValue; + } + ); + override( + CSSStyleSheet, + "replaceSync", + (native) => + function (cssText) { + native.call(this, cssText); + reportSheetChange(this); + } + ); + override( + Element, + "attachShadow", + (native) => + function (options) { + this.dispatchEvent(shadowDomAttachingEvent); + return native.call(this, options); + } + ); + const shouldWrapHTMLElement = + location.hostname === "baidu.com" || + location.hostname.endsWith(".baidu.com"); + if (shouldWrapHTMLElement) { + override( + Element, + "getElementsByTagName", + (native) => + function (tagName) { + if (tagName !== "style") { + return native.call(this, tagName); + } + const getCurrentElementValue = () => { + const elements = native.call(this, tagName); + return Object.setPrototypeOf( + [...elements].filter( + (element) => + element && !isDRElement(element) + ), + NodeList.prototype + ); + }; + let elements = getCurrentElementValue(); + const nodeListBehavior = { + get: function (_, property) { + return getCurrentElementValue()[ + Number(property) || property + ]; + } + }; + elements = new Proxy(elements, nodeListBehavior); + return elements; + } + ); + } + const shouldProxyChildNodes = ["brilliant.org", "www.vy.no"].includes( + location.hostname + ); + if (shouldProxyChildNodes) { + overrideProperty(Node, "childNodes", { + get: (native) => + function () { + const childNodes = native.call(this); + return Object.setPrototypeOf( + [...childNodes].filter((element) => { + return !isDRElement(element); + }), + NodeList.prototype + ); + } + }); + } + function resolveCustomElement(tag) { + customElements.whenDefined(tag).then(() => { + document.dispatchEvent( + new CustomEvent("__darkreader__isDefined", {detail: {tag}}) + ); + }); + } + documentEventListener("__darkreader__addUndefinedResolver", (e) => + resolveCustomElement(e.detail.tag) + ); + if (enableCustomElementRegistryProxy) { + override( + CustomElementRegistry, + "define", + (native) => + function (name, constructor, options) { + resolveCustomElement(name); + native.call(this, name, constructor, options); + } + ); + } + let blobURLAllowed = null; + function checkBlobURLSupport() { + if (blobURLAllowed != null) { + document.dispatchEvent( + new CustomEvent("__darkreader__blobURLCheckResponse", { + detail: {blobURLAllowed} + }) + ); + return; + } + const svg = + ''; + const bytes = new Uint8Array(svg.length); + for (let i = 0; i < svg.length; i++) { + bytes[i] = svg.charCodeAt(i); + } + const blob = new Blob([bytes], {type: "image/svg+xml"}); + const objectURL = URL.createObjectURL(blob); + const image = new Image(); + image.onload = () => { + blobURLAllowed = true; + sendBlobURLCheckResponse(); + }; + image.onerror = () => { + blobURLAllowed = false; + sendBlobURLCheckResponse(); + }; + image.src = objectURL; + } + function sendBlobURLCheckResponse() { + document.dispatchEvent( + new CustomEvent("__darkreader__blobURLCheckResponse", { + detail: {blobURLAllowed} + }) + ); + } + documentEventListener( + "__darkreader__blobURLCheckRequest", + checkBlobURLSupport + ); + if (enableStyleSheetsProxy) { + overrideProperty(Document, "styleSheets", { + get: (native) => + function () { + const getCurrentValue = () => { + const docSheets = native.call(this); + const filteredSheets = [...docSheets].filter( + (styleSheet) => + styleSheet.ownerNode && + !isDRSheet(styleSheet) + ); + filteredSheets.item = (item) => + filteredSheets[item]; + return Object.setPrototypeOf( + filteredSheets, + StyleSheetList.prototype + ); + }; + let elements = getCurrentValue(); + const styleSheetListBehavior = { + get: function (_, property) { + return getCurrentValue()[property]; + } + }; + elements = new Proxy(elements, styleSheetListBehavior); + return elements; + } + }); + } + const adoptedSheetsSourceProxies = new WeakMap(); + const adoptedSheetsProxySources = new WeakMap(); + const adoptedSheetsChangeEvent = new CustomEvent( + "__darkreader__adoptedStyleSheetsChange" + ); + const adoptedSheetOverrideCache = new WeakSet(); + const adoptedSheetsSnapshots = new WeakMap(); + const isDRAdoptedSheetOverride = (sheet) => { + if (!sheet || !sheet.cssRules) { + return false; + } + if (adoptedSheetOverrideCache.has(sheet)) { + return true; + } + if ( + sheet.cssRules.length > 0 && + sheet.cssRules[0].cssText.startsWith( + "#__darkreader__adoptedOverride" + ) + ) { + adoptedSheetOverrideCache.add(sheet); + return true; + } + return false; + }; + const areArraysEqual = (a, b) => { + return a.length === b.length && a.every((x, i) => x === b[i]); + }; + const onAdoptedSheetsChange = (node) => { + const prev = adoptedSheetsSnapshots.get(node); + const curr = (node.adoptedStyleSheets || []).filter( + (s) => !isDRAdoptedSheetOverride(s) + ); + adoptedSheetsSnapshots.set(node, curr); + if (!prev || !areArraysEqual(prev, curr)) { + curr.forEach((sheet) => { + if (!adoptedSheetOwners.has(sheet)) { + adoptedSheetOwners.set(sheet, new Set()); + } + adoptedSheetOwners.get(sheet).add(node); + for (const rule of sheet.cssRules) { + const declaration = rule.style; + if (declaration) { + adoptedDeclarationSheets.set(declaration, sheet); + } + } + }); + node.dispatchEvent(adoptedSheetsChangeEvent); + } + }; + const proxyAdoptedSheetsArray = (node, source) => { + if (adoptedSheetsProxySources.has(source)) { + return source; + } + if (adoptedSheetsSourceProxies.has(source)) { + return adoptedSheetsSourceProxies.get(source); + } + const proxy = new Proxy(source, { + deleteProperty(target, property) { + delete target[property]; + return true; + }, + set(target, property, value) { + target[property] = value; + if (property === "length") { + onAdoptedSheetsChange(node); + } + return true; + } + }); + adoptedSheetsSourceProxies.set(source, proxy); + adoptedSheetsProxySources.set(proxy, source); + return proxy; + }; + [Document, ShadowRoot].forEach((ctor) => { + overrideProperty(ctor, "adoptedStyleSheets", { + get: (native) => + function () { + const source = native.call(this); + return proxyAdoptedSheetsArray(this, source); + }, + set: (native) => + function (source) { + if (adoptedSheetsProxySources.has(source)) { + source = adoptedSheetsProxySources.get(source); + } + native.call(this, source); + onAdoptedSheetsChange(this); + } + }); + }); + const adoptedDeclarationChangeEvent = new CustomEvent( + "__darkreader__adoptedStyleDeclarationChange" + ); + ["setProperty", "removeProperty"].forEach((key) => { + override(CSSStyleDeclaration, key, (native) => { + return function (...args) { + const returnValue = native.apply(this, args); + const sheet = adoptedDeclarationSheets.get(this); + if (sheet) { + const owners = adoptedSheetOwners.get(sheet); + if (owners) { + owners.forEach((node) => { + node.dispatchEvent( + adoptedDeclarationChangeEvent + ); + }); + } + } + return returnValue; + }; + }); + }); + } + + const definedCustomElements = new Set(); + const undefinedGroups = new Map(); + let elementsDefinitionCallback; + function isCustomElement(element) { + if (element.tagName.includes("-") || element.getAttribute("is")) { + return true; + } + return false; + } + function recordUndefinedElement(element) { + let tag = element.tagName.toLowerCase(); + if (!tag.includes("-")) { + const extendedTag = element.getAttribute("is"); + if (extendedTag) { + tag = extendedTag; + } else { + return; + } + } + if (!undefinedGroups.has(tag)) { + undefinedGroups.set(tag, new Set()); + customElementsWhenDefined(tag).then(() => { + if (elementsDefinitionCallback) { + const elements = undefinedGroups.get(tag); + undefinedGroups.delete(tag); + elementsDefinitionCallback(Array.from(elements)); + } + }); + } + undefinedGroups.get(tag).add(element); + } + function collectUndefinedElements(root) { + if (!isDefinedSelectorSupported) { + return; + } + forEach( + root.querySelectorAll(":not(:defined)"), + recordUndefinedElement + ); + } + let canOptimizeUsingProxy = false; + document.addEventListener( + "__darkreader__inlineScriptsAllowed", + () => { + canOptimizeUsingProxy = true; + }, + {once: true, passive: true} + ); + const unhandledShadowHosts = new Set(); + document.addEventListener("__darkreader__shadowDomAttaching", (e) => { + const host = e.target; + if (unhandledShadowHosts.size === 0) { + queueMicrotask(() => { + const hosts = [...unhandledShadowHosts].filter( + (el) => el.shadowRoot + ); + elementsDefinitionCallback?.(hosts); + unhandledShadowHosts.clear(); + }); + } + unhandledShadowHosts.add(host); + }); + const resolvers = new Map(); + function handleIsDefined(e) { + canOptimizeUsingProxy = true; + const tag = e.detail.tag; + definedCustomElements.add(tag); + if (resolvers.has(tag)) { + const r = resolvers.get(tag); + resolvers.delete(tag); + r.forEach((r) => r()); + } + } + async function customElementsWhenDefined(tag) { + if (definedCustomElements.has(tag)) { + return; + } + return new Promise((resolve) => { + if ( + window.customElements && + typeof customElements.whenDefined === "function" + ) { + customElements.whenDefined(tag).then(() => resolve()); + } else if (canOptimizeUsingProxy) { + if (resolvers.has(tag)) { + resolvers.get(tag).push(resolve); + } else { + resolvers.set(tag, [resolve]); + } + document.dispatchEvent( + new CustomEvent("__darkreader__addUndefinedResolver", { + detail: {tag} + }) + ); + } else { + const checkIfDefined = () => { + const elements = undefinedGroups.get(tag); + if (elements && elements.size > 0) { + if ( + elements.values().next().value.matches(":defined") + ) { + resolve(); + } else { + requestAnimationFrame(checkIfDefined); + } + } + }; + requestAnimationFrame(checkIfDefined); + } + }); + } + function watchWhenCustomElementsDefined(callback) { + elementsDefinitionCallback = callback; + } + function unsubscribeFromDefineCustomElements() { + elementsDefinitionCallback = null; + undefinedGroups.clear(); + document.removeEventListener( + "__darkreader__isDefined", + handleIsDefined + ); + } + + const observers = []; + let observedRoots; + let handledShadowHosts; + function watchForStylePositions( + currentStyles, + update, + shadowRootDiscovered + ) { + stopWatchingForStylePositions(); + const prevStylesByRoot = new WeakMap(); + const getPrevStyles = (root) => { + if (!prevStylesByRoot.has(root)) { + prevStylesByRoot.set(root, new Set()); + } + return prevStylesByRoot.get(root); + }; + currentStyles.forEach((node) => { + let root = node; + while ((root = root.parentNode)) { + if ( + root === document || + root.nodeType === Node.DOCUMENT_FRAGMENT_NODE + ) { + const prevStyles = getPrevStyles(root); + prevStyles.add(node); + break; + } + } + }); + const prevStyleSiblings = new WeakMap(); + const nextStyleSiblings = new WeakMap(); + function saveStylePosition(style) { + prevStyleSiblings.set(style, style.previousElementSibling); + nextStyleSiblings.set(style, style.nextElementSibling); + } + function forgetStylePosition(style) { + prevStyleSiblings.delete(style); + nextStyleSiblings.delete(style); + } + function didStylePositionChange(style) { + return ( + style.previousElementSibling !== prevStyleSiblings.get(style) || + style.nextElementSibling !== nextStyleSiblings.get(style) + ); + } + currentStyles.forEach(saveStylePosition); + function handleStyleOperations(root, operations) { + const {createdStyles, removedStyles, movedStyles} = operations; + createdStyles.forEach((s) => saveStylePosition(s)); + movedStyles.forEach((s) => saveStylePosition(s)); + removedStyles.forEach((s) => forgetStylePosition(s)); + const prevStyles = getPrevStyles(root); + createdStyles.forEach((s) => prevStyles.add(s)); + removedStyles.forEach((s) => prevStyles.delete(s)); + if ( + createdStyles.size + removedStyles.size + movedStyles.size > + 0 + ) { + update({ + created: Array.from(createdStyles), + removed: Array.from(removedStyles), + moved: Array.from(movedStyles), + updated: [] + }); + } + } + function handleMinorTreeMutations(root, {additions, moves, deletions}) { + const createdStyles = new Set(); + const removedStyles = new Set(); + const movedStyles = new Set(); + additions.forEach((node) => + getManageableStyles(node).forEach((style) => + createdStyles.add(style) + ) + ); + deletions.forEach((node) => + getManageableStyles(node).forEach((style) => + removedStyles.add(style) + ) + ); + moves.forEach((node) => + getManageableStyles(node).forEach((style) => + movedStyles.add(style) + ) + ); + handleStyleOperations(root, { + createdStyles, + removedStyles, + movedStyles + }); + const potentialHosts = new Set(); + additions.forEach((n) => { + if (n.parentElement) { + potentialHosts.add(n.parentElement); + } + if (n.previousElementSibling) { + potentialHosts.add(n.previousElementSibling); + } + deepObserve(n); + collectUndefinedElements(n); + }); + potentialHosts.forEach((el) => { + if (el.shadowRoot && !observedRoots.has(el)) { + subscribeForShadowRootChanges(el); + deepObserve(el.shadowRoot); + } + }); + additions.forEach( + (node) => isCustomElement(node) && recordUndefinedElement(node) + ); + additions.forEach((node) => checkImageSelectors(node)); + } + function handleHugeTreeMutations(root) { + const styles = new Set(getManageableStyles(root)); + const createdStyles = new Set(); + const removedStyles = new Set(); + const movedStyles = new Set(); + const prevStyles = getPrevStyles(root); + styles.forEach((s) => { + if (!prevStyles.has(s)) { + createdStyles.add(s); + } + }); + prevStyles.forEach((s) => { + if (!styles.has(s)) { + removedStyles.add(s); + } + }); + styles.forEach((s) => { + if ( + !createdStyles.has(s) && + !removedStyles.has(s) && + didStylePositionChange(s) + ) { + movedStyles.add(s); + } + }); + handleStyleOperations(root, { + createdStyles, + removedStyles, + movedStyles + }); + deepObserve(root); + collectUndefinedElements(root); + checkImageSelectors(root); + } + function handleAttributeMutations(mutations) { + const updatedStyles = new Set(); + const removedStyles = new Set(); + mutations.forEach((m) => { + const {target} = m; + if (target.isConnected) { + if (shouldManageStyle(target)) { + updatedStyles.add(target); + } else if ( + target instanceof HTMLLinkElement && + target.disabled + ) { + removedStyles.add(target); + } + } + }); + if (updatedStyles.size + removedStyles.size > 0) { + update({ + updated: Array.from(updatedStyles), + created: [], + removed: Array.from(removedStyles), + moved: [] + }); + } + } + function observe(root) { + if (observedRoots.has(root)) { + return; + } + const treeObserver = createOptimizedTreeObserver(root, { + onMinorMutations: handleMinorTreeMutations, + onHugeMutations: handleHugeTreeMutations + }); + const attrObserver = new MutationObserver(handleAttributeMutations); + attrObserver.observe(root, { + attributeFilter: ["rel", "disabled", "media", "href"], + subtree: true + }); + observers.push(treeObserver, attrObserver); + observedRoots.add(root); + } + function subscribeForShadowRootChanges(node) { + const {shadowRoot} = node; + if (shadowRoot == null || observedRoots.has(shadowRoot)) { + return; + } + observe(shadowRoot); + shadowRootDiscovered(shadowRoot); + } + function deepObserve(node) { + iterateShadowHosts(node, subscribeForShadowRootChanges); + } + observe(document); + deepObserve(document.documentElement); + watchWhenCustomElementsDefined((hosts) => { + hosts = hosts.filter((node) => !handledShadowHosts.has(node)); + const newStyles = []; + hosts.forEach((host) => + push(newStyles, getManageableStyles(host.shadowRoot)) + ); + update({created: newStyles, updated: [], removed: [], moved: []}); + hosts.forEach((host) => { + const {shadowRoot} = host; + if (shadowRoot == null) { + return; + } + subscribeForShadowRootChanges(host); + deepObserve(shadowRoot); + collectUndefinedElements(shadowRoot); + }); + hosts.forEach((node) => handledShadowHosts.add(node)); + }); + document.addEventListener("__darkreader__isDefined", handleIsDefined); + collectUndefinedElements(document); + addDOMReadyListener(() => { + forEach(document.body.children, (el) => { + if (el.shadowRoot && !observedRoots.has(el)) { + subscribeForShadowRootChanges(el); + deepObserve(el.shadowRoot); + } + }); + }); + } + function resetObservers() { + observers.forEach((o) => o.disconnect()); + observers.splice(0, observers.length); + observedRoots = new WeakSet(); + handledShadowHosts = new WeakSet(); + } + function stopWatchingForStylePositions() { + resetObservers(); + unsubscribeFromDefineCustomElements(); + } + + function watchForStyleChanges(currentStyles, update, shadowRootDiscovered) { + watchForStylePositions(currentStyles, update, shadowRootDiscovered); + } + function stopWatchingForStyleChanges() { + stopWatchingForStylePositions(); + } + + const INSTANCE_ID = generateUID(); + const styleManagers = new Map(); + const adoptedStyleManagers = []; + let theme = null; + let fixes = null; + let isIFrame$1 = null; + let ignoredImageAnalysisSelectors = []; + let ignoredInlineSelectors = []; + let staticStyleMap = new WeakMap(); + function createOrUpdateStyle(className, root = document.head || document) { + let element = root.querySelector(`.${className}`); + if (!staticStyleMap.has(root)) { + staticStyleMap.set(root, new Map()); + } + const classMap = staticStyleMap.get(root); + if (element) { + classMap.set(className, element); + } else if (classMap.has(className)) { + element = classMap.get(className); + } else { + element = document.createElement("style"); + element.classList.add("darkreader"); + element.classList.add(className); + element.media = "screen"; + element.textContent = ""; + classMap.set(className, element); + } + return element; + } + function createOrUpdateScript(className, root = document.head || document) { + let element = root.querySelector(`.${className}`); + if (!element) { + element = document.createElement("script"); + element.classList.add("darkreader"); + element.classList.add(className); + } + return element; + } + const nodePositionWatchers = new Map(); + function setupNodePositionWatcher(node, alias, callback) { + nodePositionWatchers.has(alias) && + nodePositionWatchers.get(alias).stop(); + nodePositionWatchers.set( + alias, + watchForNodePosition(node, "head", callback) + ); + } + function stopStylePositionWatchers() { + forEach(nodePositionWatchers.values(), (watcher) => watcher.stop()); + nodePositionWatchers.clear(); + } + function injectStaticStyle(style, prevNode, watchAlias, callback) { + const mode = getStyleInjectionMode(); + if (mode === "next") { + document.head.insertBefore( + style, + prevNode ? prevNode.nextSibling : document.head.firstChild + ); + setupNodePositionWatcher(style, watchAlias, callback); + } else if (mode === "away") { + injectStyleAway(style); + } + } + const scheduleInversionStyleUpdate = throttle(() => { + const invertStyle = document.head?.querySelector(".darkreader--invert"); + if (invertStyle) { + setInversionStyleValue(invertStyle); + } + shadowRootsWithOverrides.forEach((root) => { + const shadowInvertStyle = root.querySelector(".darkreader--invert"); + if (shadowInvertStyle) { + setInversionStyleValue(shadowInvertStyle); + } + }); + }); + setFilterSelectorHandler((selector, type) => { + const changed = addFilterSelector(selector, type); + if (changed) { + scheduleInversionStyleUpdate(); + } + }); + function setInversionStyleValue(invertStyle) { + if (!theme) { + return; + } + const rules = []; + const appendRule = (selectors, filter) => { + if (!filter || selectors.length === 0) { + return; + } + rules.push( + [ + `${selectors.join(", ")} {`, + ` filter: ${filter} !important;`, + "}" + ].join("\n") + ); + }; + const appendCounterInversion = (selectors) => { + if (theme.mode === 0 || selectors.length === 0) { + return; + } + rules.push( + [ + `${selectors.join(", ")} {`, + ` color: black !important;`, + "}", + `${selectors.map((s) => `${s} > *`).join(", ")} {`, + ` filter: invert(100%) hue-rotate(180deg) !important;`, + "}" + ].join("\n") + ); + }; + const appendInversionCancellation = (selectors) => { + if (theme.mode === 0 || selectors.length === 0) { + return; + } + rules.push( + [ + `${selectors.join(", ")} {`, + ` filter: none !important;`, + ` color: var(--darkreader-neutral-text) !important;`, + "}", + `${selectors.map((s) => `${s} > *`).join(", ")} {`, + ` filter: none !important;`, + "}" + ].join("\n") + ); + }; + if ( + (fixes && Array.isArray(fixes.invert) && fixes.invert.length > 0) || + filterSelectors.invert.size > 0 + ) { + const extraInversionSelectors = [...filterSelectors.invert]; + const invertSelectors = [ + ...(fixes?.invert ?? []), + ...extraInversionSelectors + ]; + const invertFilter = getCSSFilterValue({ + ...theme, + contrast: + theme.mode === 0 + ? theme.contrast + : clamp(theme.contrast - 10, 0, 100) + }); + appendRule(invertSelectors, invertFilter); + appendCounterInversion(extraInversionSelectors); + if (filterSelectors.none.size > 0) { + const noneSelectors = [...filterSelectors.none]; + appendInversionCancellation(noneSelectors); + if (theme.mode === 1) { + const invertedChildSelectors = []; + noneSelectors.forEach((parent) => { + extraInversionSelectors.forEach((child) => + invertedChildSelectors.push(`${parent} > ${child}`) + ); + }); + appendRule(invertedChildSelectors, invertFilter); + } + } + } + if (filterSelectors.dim.size > 0) { + appendRule( + [...filterSelectors.dim], + getCSSFilterValue({ + ...theme, + brightness: clamp(theme.brightness - 10, 5, 200), + sepia: clamp(theme.sepia + 10, 0, 100) + }) + ); + } + invertStyle.textContent = rules.join("\n"); + } + function createStaticStyleOverrides() { + const fallbackStyle = createOrUpdateStyle( + "darkreader--fallback", + document + ); + fallbackStyle.textContent = getModifiedFallbackStyle(theme, { + strict: true + }); + injectStaticStyle(fallbackStyle, null, "fallback"); + const userAgentStyle = createOrUpdateStyle("darkreader--user-agent"); + userAgentStyle.textContent = getModifiedUserAgentStyle( + theme, + isIFrame$1, + theme.styleSystemControls + ); + injectStaticStyle(userAgentStyle, fallbackStyle, "user-agent"); + const textStyle = createOrUpdateStyle("darkreader--text"); + if (theme.useFont || theme.textStroke > 0) { + textStyle.textContent = createTextStyle(theme); + } else { + textStyle.textContent = ""; + } + injectStaticStyle(textStyle, userAgentStyle, "text"); + const invertStyle = createOrUpdateStyle("darkreader--invert"); + setInversionStyleValue(invertStyle); + injectStaticStyle(invertStyle, textStyle, "invert"); + const inlineStyle = createOrUpdateStyle("darkreader--inline"); + inlineStyle.textContent = getInlineOverrideStyle(); + injectStaticStyle(inlineStyle, invertStyle, "inline"); + const variableStyle = createOrUpdateStyle("darkreader--variables"); + const selectionColors = theme?.selectionColor + ? getSelectionColor(theme) + : null; + const neutralBackgroundColor = modifyBackgroundColor( + parseColorWithCache("#ffffff"), + theme + ); + const neutralTextColor = modifyForegroundColor( + parseColorWithCache("#000000"), + theme + ); + variableStyle.textContent = [ + `:root {`, + ` --darkreader-neutral-background: ${neutralBackgroundColor};`, + ` --darkreader-neutral-text: ${neutralTextColor};`, + ` --darkreader-selection-background: ${selectionColors?.backgroundColorSelection ?? "initial"};`, + ` --darkreader-selection-text: ${selectionColors?.foregroundColorSelection ?? "initial"};`, + `}` + ].join("\n"); + injectStaticStyle(variableStyle, inlineStyle, "variables", () => + registerVariablesSheet(variableStyle.sheet) + ); + registerVariablesSheet(variableStyle.sheet); + const rootVarsStyle = createOrUpdateStyle("darkreader--root-vars"); + injectStaticStyle(rootVarsStyle, variableStyle, "root-vars"); + const enableStyleSheetsProxy = !( + fixes && fixes.disableStyleSheetsProxy + ); + const enableCustomElementRegistryProxy = !( + fixes && fixes.disableCustomElementRegistryProxy + ); + document.dispatchEvent(new CustomEvent("__darkreader__cleanUp")); + { + const proxyScript = createOrUpdateScript("darkreader--proxy"); + proxyScript.append( + `(${injectProxy})(${enableStyleSheetsProxy}, ${enableCustomElementRegistryProxy})` + ); + document.head.insertBefore(proxyScript, rootVarsStyle.nextSibling); + proxyScript.remove(); + } + const overrideStyle = createOrUpdateStyle("darkreader--override"); + overrideStyle.textContent = + fixes && fixes.css ? replaceCSSTemplates(fixes.css) : ""; + injectStaticStyle(overrideStyle, document.head.lastChild, "override"); + } + const shadowRootsWithOverrides = new Set(); + function createShadowStaticStyleOverridesInner(root) { + const inlineStyle = createOrUpdateStyle("darkreader--inline", root); + inlineStyle.textContent = getInlineOverrideStyle(); + root.insertBefore(inlineStyle, root.firstChild); + const overrideStyle = createOrUpdateStyle("darkreader--override", root); + overrideStyle.textContent = + fixes && fixes.css ? replaceCSSTemplates(fixes.css) : ""; + root.insertBefore(overrideStyle, inlineStyle.nextSibling); + const invertStyle = createOrUpdateStyle("darkreader--invert", root); + setInversionStyleValue(invertStyle); + root.insertBefore(invertStyle, overrideStyle.nextSibling); + shadowRootsWithOverrides.add(root); + } + function delayedCreateShadowStaticStyleOverrides(root) { + const observer = new MutationObserver((mutations, observer) => { + observer.disconnect(); + for (const {type, removedNodes} of mutations) { + if (type === "childList") { + for (const {nodeName, className} of removedNodes) { + if ( + nodeName === "STYLE" && + [ + "darkreader darkreader--inline", + "darkreader darkreader--override", + "darkreader darkreader--invert" + ].includes(className) + ) { + createShadowStaticStyleOverridesInner(root); + return; + } + } + } + } + }); + observer.observe(root, {childList: true}); + } + function createShadowStaticStyleOverrides(root) { + const delayed = root.firstChild === null; + createShadowStaticStyleOverridesInner(root); + if (delayed) { + delayedCreateShadowStaticStyleOverrides(root); + } + } + function replaceCSSTemplates($cssText) { + return $cssText.replace(/\${(.+?)}/g, (_, $color) => { + const color = parseColorWithCache($color); + if (color) { + const lightness = getSRGBLightness(color.r, color.g, color.b); + if (lightness > 0.5) { + return modifyBackgroundColor(color, theme); + } + return modifyForegroundColor(color, theme); + } + logWarn("Couldn't parse CSSTemplate's color."); + return $color; + }); + } + function cleanFallbackStyle() { + const fallback = + staticStyleMap.get(document.head)?.get("darkreader--fallback") || + staticStyleMap.get(document)?.get("darkreader--fallback") || + document.querySelector(".darkreader--fallback"); + if (fallback) { + fallback.textContent = ""; + } + } + function createDynamicStyleOverrides() { + cancelRendering(); + const allStyles = getManageableStyles(document); + const newManagers = allStyles + .filter((style) => !styleManagers.has(style)) + .map((style) => createManager(style)); + newManagers + .map((manager) => manager.details({secondRound: false})) + .filter((detail) => detail && detail.rules.length > 0) + .forEach((detail) => { + variablesStore.addRulesForMatching(detail.rules); + }); + variablesStore.matchVariablesAndDependents(); + variablesStore.setOnRootVariableChange(() => { + const rootVarsStyle = createOrUpdateStyle("darkreader--root-vars"); + variablesStore.putRootVars(rootVarsStyle, theme); + }); + const rootVarsStyle = createOrUpdateStyle("darkreader--root-vars"); + variablesStore.putRootVars(rootVarsStyle, theme); + styleManagers.forEach((manager) => + manager.render(theme, ignoredImageAnalysisSelectors) + ); + if (loadingStyles.size === 0) { + cleanFallbackStyle(); + } + newManagers.forEach((manager) => manager.watch()); + const inlineStyleElements = toArray( + document.querySelectorAll(INLINE_STYLE_SELECTOR) + ); + iterateShadowHosts(document.documentElement, (host) => { + createShadowStaticStyleOverrides(host.shadowRoot); + const elements = host.shadowRoot.querySelectorAll( + INLINE_STYLE_SELECTOR + ); + if (elements.length > 0) { + push(inlineStyleElements, elements); + } + }); + inlineStyleElements.forEach((el) => + overrideInlineStyle( + el, + theme, + ignoredInlineSelectors, + ignoredImageAnalysisSelectors + ) + ); + handleAdoptedStyleSheets(document); + variablesStore.matchVariablesAndDependents(); + tryInvertChromePDF(); + } + let loadingStylesCounter = 0; + const loadingStyles = new Set(); + function createManager(element) { + const loadingStyleId = ++loadingStylesCounter; + function loadingStart() { + if (!isDOMReady() || !documentIsVisible()) { + loadingStyles.add(loadingStyleId); + logInfo( + `Current amount of styles loading: ${loadingStyles.size}` + ); + const fallbackStyle = createOrUpdateStyle( + "darkreader--fallback" + ); + if (!fallbackStyle.textContent) { + fallbackStyle.textContent = getModifiedFallbackStyle( + theme, + {strict: false} + ); + } + } + } + function loadingEnd() { + loadingStyles.delete(loadingStyleId); + logInfo( + `Removed loadingStyle ${loadingStyleId}, now awaiting: ${loadingStyles.size}` + ); + if (loadingStyles.size === 0 && isDOMReady()) { + cleanFallbackStyle(); + } + } + function update() { + const details = manager.details({secondRound: true}); + if (!details) { + return; + } + variablesStore.addRulesForMatching(details.rules); + variablesStore.matchVariablesAndDependents(); + manager.render(theme, ignoredImageAnalysisSelectors); + } + const manager = manageStyle(element, { + update, + loadingStart, + loadingEnd + }); + styleManagers.set(element, manager); + return manager; + } + function removeManager(element) { + const manager = styleManagers.get(element); + if (manager) { + manager.destroy(); + styleManagers.delete(element); + } + } + const throttledRenderAllStyles = throttle((callback) => { + styleManagers.forEach((manager) => + manager.render(theme, ignoredImageAnalysisSelectors) + ); + adoptedStyleManagers.forEach((manager) => + manager.render(theme, ignoredImageAnalysisSelectors) + ); + callback && callback(); + }); + const cancelRendering = function () { + throttledRenderAllStyles.cancel(); + }; + function onDOMReady() { + if (loadingStyles.size === 0) { + cleanFallbackStyle(); + return; + } + logWarn( + `DOM is ready, but still have styles being loaded.`, + loadingStyles + ); + } + function runDynamicStyle() { + createDynamicStyleOverrides(); + watchForUpdates(); + } + function createThemeAndWatchForUpdates() { + createStaticStyleOverrides(); + if (!documentIsVisible() && !theme.immediateModify) { + setDocumentVisibilityListener(runDynamicStyle); + } else { + runDynamicStyle(); + } + changeMetaThemeColorWhenAvailable(theme); + } + function unwrap(value) { + return value?.wrappedJSObject ?? value; + } + function handleAdoptedStyleSheets(node) { + if (canHaveAdoptedStyleSheets(node)) { + forEach( + isFirefox + ? unwrap(node.adoptedStyleSheets) + : node.adoptedStyleSheets, + (s) => { + variablesStore.addRulesForMatching(s.cssRules); + } + ); + const newManger = createAdoptedStyleSheetOverride(node); + adoptedStyleManagers.push(newManger); + newManger.render(theme, ignoredImageAnalysisSelectors); + newManger.watch((sheets) => { + sheets.forEach((s) => { + variablesStore.addRulesForMatching(s.cssRules); + }); + variablesStore.matchVariablesAndDependents(); + newManger.render(theme, ignoredImageAnalysisSelectors); + }); + } + } + function watchForUpdates() { + const managedStyles = Array.from(styleManagers.keys()); + watchForStyleChanges( + managedStyles, + ({created, updated, removed, moved}) => { + const stylesToRemove = removed; + const stylesToManage = created + .concat(updated) + .concat(moved) + .filter((style) => !styleManagers.has(style)); + const stylesToRestore = moved.filter((style) => + styleManagers.has(style) + ); + stylesToRemove.forEach((style) => removeManager(style)); + const newManagers = stylesToManage.map((style) => + createManager(style) + ); + newManagers + .map((manager) => manager.details({secondRound: false})) + .filter((detail) => detail && detail.rules.length > 0) + .forEach((detail) => { + variablesStore.addRulesForMatching(detail.rules); + }); + variablesStore.matchVariablesAndDependents(); + newManagers.forEach((manager) => + manager.render(theme, ignoredImageAnalysisSelectors) + ); + newManagers.forEach((manager) => manager.watch()); + stylesToRestore.forEach((style) => + styleManagers.get(style).restore() + ); + }, + (shadowRoot) => { + createShadowStaticStyleOverrides(shadowRoot); + handleAdoptedStyleSheets(shadowRoot); + } + ); + watchForInlineStyles( + (element) => { + overrideInlineStyle( + element, + theme, + ignoredInlineSelectors, + ignoredImageAnalysisSelectors + ); + if (element === document.documentElement) { + const styleAttr = element.getAttribute("style") || ""; + if (styleAttr.includes("--")) { + variablesStore.matchVariablesAndDependents(); + const rootVarsStyle = createOrUpdateStyle( + "darkreader--root-vars" + ); + variablesStore.putRootVars(rootVarsStyle, theme); + } + } + }, + (root) => { + createShadowStaticStyleOverrides(root); + const inlineStyleElements = root.querySelectorAll( + INLINE_STYLE_SELECTOR + ); + if (inlineStyleElements.length > 0) { + forEach(inlineStyleElements, (el) => + overrideInlineStyle( + el, + theme, + ignoredInlineSelectors, + ignoredImageAnalysisSelectors + ) + ); + } + } + ); + addDOMReadyListener(onDOMReady); + setupDocumentPiPFontFix(); + } + function stopWatchingForUpdates() { + styleManagers.forEach((manager) => manager.pause()); + stopStylePositionWatchers(); + stopWatchingForStyleChanges(); + stopWatchingForInlineStyles(); + removeDOMReadyListener(onDOMReady); + cleanReadyStateCompleteListeners(); + } + let metaObserver; + let headObserver = null; + function addMetaListener() { + metaObserver = new MutationObserver(() => { + if (document.querySelector('meta[name="darkreader-lock"]')) { + metaObserver.disconnect(); + removeDynamicTheme(); + } + }); + metaObserver.observe(document.head, {childList: true, subtree: true}); + } + function createDarkReaderInstanceMarker() { + const metaElement = document.createElement("meta"); + metaElement.name = "darkreader"; + metaElement.content = INSTANCE_ID; + document.head.appendChild(metaElement); + } + function isDRLocked() { + return document.querySelector('meta[name="darkreader-lock"]') != null; + } + function isAnotherDarkReaderInstanceActive() { + const meta = document.querySelector('meta[name="darkreader"]'); + if (meta) { + if (meta.content !== INSTANCE_ID) { + return true; + } + return false; + } + createDarkReaderInstanceMarker(); + addMetaListener(); + return false; + } + let interceptorAttempts = 2; + function interceptOldScript({success, failure}) { + if (--interceptorAttempts <= 0) { + failure(); + return; + } + const oldMeta = document.head.querySelector('meta[name="darkreader"]'); + if (!oldMeta || oldMeta.content === INSTANCE_ID) { + return; + } + const lock = document.createElement("meta"); + lock.name = "darkreader-lock"; + document.head.append(lock); + queueMicrotask(() => { + lock.remove(); + success(); + }); + } + function disableConflictingPlugins() { + if (document.documentElement.hasAttribute("data-wp-dark-mode-preset")) { + const disableWPDarkMode = () => { + document.dispatchEvent( + new CustomEvent("__darkreader__disableConflictingPlugins") + ); + document.documentElement.classList.remove( + "wp-dark-mode-active" + ); + document.documentElement.removeAttribute( + "data-wp-dark-mode-active" + ); + }; + disableWPDarkMode(); + const observer = new MutationObserver(() => { + if ( + document.documentElement.classList.contains( + "wp-dark-mode-active" + ) || + document.documentElement.hasAttribute( + "data-wp-dark-mode-active" + ) + ) { + disableWPDarkMode(); + } + }); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ["class", "data-wp-dark-mode-active"] + }); + } + } + function tryInvertChromePDF() { + if (!document.body || !chrome.dom) { + return; + } + const root = chrome.dom.openOrClosedShadowRoot(document.body); + if (!root || !root.querySelector('link[href$="/pdf_embedder.css"]')) { + return; + } + if (isChromium && !isMobile) { + const overlay = document.createElement("div"); + overlay.classList.add("darkreader"); + overlay.classList.add("darkreader--pdf-overlay"); + overlay.style.backdropFilter = "invert(100%) contrast(90%)"; + overlay.style.pointerEvents = "none"; + overlay.style.position = "fixed"; + overlay.style.left = "0px"; + overlay.style.right = "0px"; + overlay.style.bottom = "0px"; + overlay.style.top = "56px"; + root.append(overlay); + cleaners.push(() => { + overlay.remove(); + }); + } else { + const sheet = new CSSStyleSheet(); + sheet.replaceSync( + '[type="application/pdf"] { filter: invert(1) contrast(0.9); }' + ); + root.adoptedStyleSheets.push(sheet); + cleaners.push(() => { + const index = root.adoptedStyleSheets.indexOf(sheet); + if (index >= 0) { + root.adoptedStyleSheets.splice(index, 1); + } + }); + } + } + let prevTheme = null; + let prevFixes = null; + function createOrUpdateDynamicThemeInternal( + themeConfig, + dynamicThemeFixes, + iframe + ) { + theme = themeConfig; + fixes = dynamicThemeFixes; + const colorAffectingKeys = [ + "brightness", + "contrast", + "darkSchemeBackgroundColor", + "darkSchemeTextColor", + "grayscale", + "lightSchemeBackgroundColor", + "lightSchemeTextColor", + "sepia" + ]; + if (prevTheme && prevFixes) { + const themeKeys = new Set([ + ...Object.keys(theme), + ...Object.keys(prevTheme) + ]); + let onlyColorsChanged = true; + for (const key of themeKeys) { + if ( + theme[key] !== prevTheme[key] && + !colorAffectingKeys.includes(key) + ) { + onlyColorsChanged = false; + break; + } + } + if ( + onlyColorsChanged && + JSON.stringify(fixes) !== JSON.stringify(prevFixes) + ) { + onlyColorsChanged = false; + } + if (onlyColorsChanged) { + const palette = getColorPalette(); + clearColorPalette(); + palette.background.forEach((color) => + modifyBackgroundColor(color, theme) + ); + palette.text.forEach((color) => + modifyForegroundColor(color, theme) + ); + palette.border.forEach((color) => + modifyBorderColor(color, theme) + ); + return; + } + clearColorPalette(); + } + if (fixes) { + ignoredImageAnalysisSelectors = Array.isArray( + fixes.ignoreImageAnalysis + ) + ? fixes.ignoreImageAnalysis + : []; + ignoredInlineSelectors = Array.isArray(fixes.ignoreInlineStyle) + ? fixes.ignoreInlineStyle + : []; + setIgnoredCSSURLs( + Array.isArray(fixes.ignoreCSSUrl) ? fixes.ignoreCSSUrl : [] + ); + } else { + ignoredImageAnalysisSelectors = []; + ignoredInlineSelectors = []; + setIgnoredCSSURLs([]); + } + if (theme.immediateModify) { + setIsDOMReady(() => { + return true; + }); + } + isIFrame$1 = iframe; + const ready = () => { + const success = () => { + disableConflictingPlugins(); + document.documentElement.setAttribute( + "data-darkreader-mode", + "dynamic" + ); + document.documentElement.setAttribute( + "data-darkreader-scheme", + theme.mode ? "dark" : "dimmed" + ); + createThemeAndWatchForUpdates(); + }; + const failure = () => { + removeDynamicTheme(); + }; + if (isDRLocked()) { + removeNode(document.querySelector(".darkreader--fallback")); + } else if (isAnotherDarkReaderInstanceActive()) { + interceptOldScript({ + success, + failure + }); + } else { + success(); + } + }; + if (document.head) { + ready(); + } else { + if (!isFirefox) { + const fallbackStyle = createOrUpdateStyle( + "darkreader--fallback" + ); + document.documentElement.appendChild(fallbackStyle); + fallbackStyle.textContent = getModifiedFallbackStyle(theme, { + strict: true + }); + } + headObserver?.disconnect(); + headObserver = new MutationObserver(() => { + if (document.head) { + headObserver?.disconnect(); + ready(); + } + }); + cleaners.push(() => { + headObserver?.disconnect(); + headObserver = null; + }); + headObserver.observe(document, {childList: true, subtree: true}); + } + prevTheme = theme; + prevFixes = fixes; + } + function removeProxy() { + document.dispatchEvent(new CustomEvent("__darkreader__cleanUp")); + removeNode(document.head.querySelector(".darkreader--proxy")); + } + const cleaners = []; + let pipListenerRegistered = false; + function setupDocumentPiPFontFix() { + if (pipListenerRegistered) { + return; + } + const docPiP = window.documentPictureInPicture; + if (!docPiP) { + return; + } + pipListenerRegistered = true; + function collectFontSheetCSS() { + const fontSheetRules = []; + for (const sheet of document.styleSheets) { + try { + const rules = Array.from(sheet.cssRules); + if (rules.some((rule) => rule instanceof CSSFontFaceRule)) { + rules.forEach((rule) => + fontSheetRules.push(rule.cssText) + ); + } + } catch (e) {} + } + return fontSheetRules.join("\n"); + } + function getPipDoc() { + return docPiP.window?.document ?? null; + } + function injectFontCSS(fontCSS) { + const pipDoc = getPipDoc(); + if (!pipDoc || pipDoc.querySelector(".darkreader--font-fix")) { + return; + } + const style = pipDoc.createElement("style"); + style.classList.add("darkreader"); + style.classList.add("darkreader--font-fix"); + style.textContent = fontCSS; + (pipDoc.head || pipDoc.documentElement).appendChild(style); + } + function removeFontCSS() { + getPipDoc()?.querySelector(".darkreader--font-fix")?.remove(); + } + function onPiPEnter() { + const pipDoc = getPipDoc(); + if ( + !pipDoc || + pipDoc.querySelector('meta[name="darkreader-lock"]') + ) { + return; + } + const fontCSS = collectFontSheetCSS(); + if (!fontCSS) { + return; + } + injectFontCSS(fontCSS); + const observer = new MutationObserver(() => { + if (pipDoc.querySelector('meta[name="darkreader-lock"]')) { + observer.disconnect(); + docPiP.removeEventListener("enter", onPiPEnter); + removeFontCSS(); + return; + } + injectFontCSS(fontCSS); + }); + observer.observe(pipDoc, {childList: true, subtree: true}); + cleaners.push(() => observer.disconnect()); + docPiP.window.addEventListener("unload", () => + observer.disconnect() + ); + } + docPiP.addEventListener("enter", onPiPEnter); + cleaners.push(() => { + docPiP.removeEventListener("enter", onPiPEnter); + removeFontCSS(); + pipListenerRegistered = false; + }); + } + function removeDynamicTheme() { + document.documentElement.removeAttribute(`data-darkreader-mode`); + document.documentElement.removeAttribute(`data-darkreader-scheme`); + cleanDynamicThemeCache(); + removeNode(document.querySelector(".darkreader--fallback")); + if (document.head) { + const selectors = [ + ".darkreader--user-agent", + ".darkreader--text", + ".darkreader--invert", + ".darkreader--inline", + ".darkreader--override", + ".darkreader--variables", + ".darkreader--root-vars", + 'meta[name="darkreader"]' + ]; + restoreMetaThemeColor(); + selectors.forEach((selector) => + removeNode(document.head.querySelector(selector)) + ); + staticStyleMap = new WeakMap(); + removeProxy(); + } + shadowRootsWithOverrides.forEach((root) => { + removeNode(root.querySelector(".darkreader--inline")); + removeNode(root.querySelector(".darkreader--override")); + }); + shadowRootsWithOverrides.clear(); + forEach(styleManagers.keys(), (el) => removeManager(el)); + loadingStyles.clear(); + cleanLoadingLinks(); + forEach(document.querySelectorAll(".darkreader"), removeNode); + removeStyleContainer(); + adoptedStyleManagers.forEach((manager) => manager.destroy()); + adoptedStyleManagers.splice(0); + metaObserver && metaObserver.disconnect(); + scheduleInversionStyleUpdate.cancel(); + cleaners.forEach((clean) => clean()); + cleaners.splice(0); + } + function cleanDynamicThemeCache() { + variablesStore.clear(); + parsedURLCache.clear(); + cleanFilterSelectors(); + removeDocumentVisibilityListener(); + cancelRendering(); + stopWatchingForUpdates(); + cleanModificationCache(); + clearColorCache(); + releaseVariablesSheet(); + prevTheme = null; + prevFixes = null; + } + + function parseCSS(cssText) { + cssText = removeCSSComments(cssText); + cssText = cssText.trim(); + if (!cssText) { + return []; + } + const rules = []; + const excludeRanges = getTokenExclusionRanges(cssText); + const bracketRanges = getAllOpenCloseRanges( + cssText, + "{", + "}", + excludeRanges + ); + let ruleStart = 0; + bracketRanges.forEach((brackets) => { + const key = cssText.substring(ruleStart, brackets.start).trim(); + const content = cssText.substring( + brackets.start + 1, + brackets.end - 1 + ); + if (key.startsWith("@")) { + const typeEndIndex = key.search(/[\s\(]/); + const rule = { + type: + typeEndIndex < 0 ? key : key.substring(0, typeEndIndex), + query: + typeEndIndex < 0 + ? "" + : key.substring(typeEndIndex).trim(), + rules: parseCSS(content) + }; + rules.push(rule); + } else { + const rule = { + selectors: parseSelectors(key), + declarations: parseDeclarations(content) + }; + rules.push(rule); + } + ruleStart = brackets.end; + }); + return rules; + } + function getAllOpenCloseRanges( + input, + openToken, + closeToken, + excludeRanges = [] + ) { + const ranges = []; + let i = 0; + let range; + while ( + (range = getOpenCloseRange( + input, + i, + openToken, + closeToken, + excludeRanges + )) + ) { + ranges.push(range); + i = range.end; + } + return ranges; + } + function getTokenExclusionRanges(cssText) { + const singleQuoteGoesFirst = + cssText.indexOf("'") < cssText.indexOf('"'); + const firstQuote = singleQuoteGoesFirst ? "'" : '"'; + const secondQuote = singleQuoteGoesFirst ? '"' : "'"; + const excludeRanges = getAllOpenCloseRanges( + cssText, + firstQuote, + firstQuote + ); + excludeRanges.push( + ...getAllOpenCloseRanges( + cssText, + secondQuote, + secondQuote, + excludeRanges + ) + ); + excludeRanges.push( + ...getAllOpenCloseRanges(cssText, "[", "]", excludeRanges) + ); + excludeRanges.push( + ...getAllOpenCloseRanges(cssText, "(", ")", excludeRanges) + ); + return excludeRanges; + } + function parseSelectors(selectorText) { + const excludeRanges = getTokenExclusionRanges(selectorText); + return splitExcluding(selectorText, ",", excludeRanges); + } + function parseDeclarations(cssDeclarationsText) { + const declarations = []; + const excludeRanges = getTokenExclusionRanges(cssDeclarationsText); + splitExcluding(cssDeclarationsText, ";", excludeRanges).forEach( + (part) => { + const colonIndex = part.indexOf(":"); + if (colonIndex > 0) { + const importantIndex = part.indexOf("!important"); + declarations.push({ + property: part.substring(0, colonIndex).trim(), + value: part + .substring( + colonIndex + 1, + importantIndex > 0 + ? importantIndex + : part.length + ) + .trim(), + important: importantIndex > 0 + }); + } + } + ); + return declarations; + } + function isParsedStyleRule(rule) { + return "selectors" in rule; + } + + function formatCSS(cssText) { + const parsed = parseCSS(cssText); + return formatParsedCSS(parsed); + } + function formatParsedCSS(parsed) { + const lines = []; + const tab = " "; + function formatRule(rule, indent) { + if (isParsedStyleRule(rule)) { + formatStyleRule(rule, indent); + } else { + formatAtRule(rule, indent); + } + } + function formatAtRule({type, query, rules}, indent) { + lines.push(`${indent}${type} ${query} {`); + rules.forEach((child) => formatRule(child, `${indent}${tab}`)); + lines.push(`${indent}}`); + } + function formatStyleRule({selectors, declarations}, indent) { + const lastSelectorIndex = selectors.length - 1; + selectors.forEach((selector, i) => { + lines.push( + `${indent}${selector}${i < lastSelectorIndex ? "," : " {"}` + ); + }); + const sorted = sortDeclarations(declarations); + sorted.forEach(({property, value, important}) => { + lines.push( + `${indent}${tab}${property}: ${value}${important ? " !important" : ""};` + ); + }); + lines.push(`${indent}}`); + } + clearEmptyRules(parsed); + parsed.forEach((rule) => formatRule(rule, "")); + return lines.join("\n"); + } + function sortDeclarations(declarations) { + const prefixRegex = /^-[a-z]-/; + return [...declarations].sort((a, b) => { + const aProp = a.property; + const bProp = b.property; + const aPrefix = aProp.match(prefixRegex)?.[0] ?? ""; + const bPrefix = bProp.match(prefixRegex)?.[0] ?? ""; + const aNorm = aPrefix ? aProp.replace(prefixRegex, "") : aProp; + const bNorm = bPrefix ? bProp.replace(prefixRegex, "") : bProp; + if (aNorm === bNorm) { + return aPrefix.localeCompare(bPrefix); + } + return aNorm.localeCompare(bNorm); + }); + } + function clearEmptyRules(rules) { + for (let i = rules.length - 1; i >= 0; i--) { + const rule = rules[i]; + if (isParsedStyleRule(rule)) { + if (rule.declarations.length === 0) { + rules.splice(i, 1); + } + } else { + clearEmptyRules(rule.rules); + if (rule.rules.length === 0) { + rules.splice(i, 1); + } + } + } + } + + const blobRegex = /url\(\"(blob\:.*?)\"\)/g; + async function replaceBlobs(text) { + const promises = []; + getMatches(blobRegex, text, 1).forEach((url) => { + const promise = loadAsDataURL(url); + promises.push(promise); + }); + const data = await Promise.all(promises); + return text.replace(blobRegex, () => `url("${data.shift()}")`); + } + const banner = `/* + _______ + / \\ + .==. .==. + (( ))==(( )) + / "==" "=="\\ + /____|| || ||___\\ + ________ ____ ________ ___ ___ + | ___ \\ / \\ | ___ \\ | | / / + | | \\ \\ / /\\ \\ | | \\ \\| |_/ / + | | ) / /__\\ \\ | |__/ /| ___ \\ + | |__/ / ______ \\| ____ \\| | \\ \\ +_______|_______/__/ ____ \\__\\__|___\\__\\__|___\\__\\____ +| ___ \\ | ____/ / \\ | ___ \\ | ____| ___ \\ +| | \\ \\| |___ / /\\ \\ | | \\ \\| |___| | \\ \\ +| |__/ /| ____/ /__\\ \\ | | ) | ____| |__/ / +| ____ \\| |__/ ______ \\| |__/ /| |___| ____ \\ +|__| \\__\\____/__/ \\__\\_______/ |______|__| \\__\\ + https://darkreader.org +*/ + +/*! Dark reader generated CSS | Licensed under MIT https://github.com/darkreader/darkreader/blob/main/LICENSE */ +`; + async function collectCSS() { + const css = [banner]; + function addStaticCSS(selector, comment) { + const staticStyle = document.querySelector(selector); + if (staticStyle && staticStyle.textContent) { + css.push(`/* ${comment} */`); + css.push(staticStyle.textContent); + css.push(""); + } + } + addStaticCSS(".darkreader--fallback", "Fallback Style"); + addStaticCSS(".darkreader--user-agent", "User-Agent Style"); + addStaticCSS(".darkreader--text", "Text Style"); + addStaticCSS(".darkreader--invert", "Invert Style"); + addStaticCSS(".darkreader--variables", "Variables Style"); + const modifiedCSS = []; + document.querySelectorAll(".darkreader--sync").forEach((element) => { + forEach(element.sheet.cssRules, (rule) => { + rule && rule.cssText && modifiedCSS.push(rule.cssText); + }); + }); + if (modifiedCSS.length) { + const formattedCSS = formatCSS(modifiedCSS.join("\n")); + css.push("/* Modified CSS */"); + css.push(await replaceBlobs(formattedCSS)); + css.push(""); + } + addStaticCSS(".darkreader--override", "Override Style"); + return css.join("\n"); + } + + let isDarkReaderEnabled = false; + const isIFrame = (() => { + try { + return window.self !== window.top; + } catch (err) { + console.warn(err); + return true; + } + })(); + function enable(themeOptions = {}, fixes = null) { + const theme = {...DEFAULT_THEME, ...themeOptions}; + if (theme.engine !== ThemeEngine.dynamicTheme) { + throw new Error("Theme engine is not supported."); + } + createOrUpdateDynamicThemeInternal(theme, fixes, isIFrame); + isDarkReaderEnabled = true; + } + function isEnabled() { + return isDarkReaderEnabled; + } + function disable() { + removeDynamicTheme(); + isDarkReaderEnabled = false; + } + const darkScheme = + typeof matchMedia === "function" + ? matchMedia("(prefers-color-scheme: dark)") + : undefined; + let store = { + themeOptions: null, + fixes: null + }; + function handleColorScheme() { + if (darkScheme?.matches) { + enable(store.themeOptions, store.fixes); + } else { + disable(); + } + } + function auto(themeOptions = {}, fixes = null) { + if (themeOptions) { + store = {themeOptions, fixes}; + handleColorScheme(); + if (isMatchMediaChangeEventListenerSupported) { + darkScheme?.addEventListener("change", handleColorScheme); + } else { + darkScheme?.addListener(handleColorScheme); + } + } else { + if (isMatchMediaChangeEventListenerSupported) { + darkScheme?.removeEventListener("change", handleColorScheme); + } else { + darkScheme?.removeListener(handleColorScheme); + } + disable(); + } + } + async function exportGeneratedCSS() { + return await collectCSS(); + } + const setFetchMethod = setFetchMethod$1; + + exports.auto = auto; + exports.disable = disable; + exports.enable = enable; + exports.exportGeneratedCSS = exportGeneratedCSS; + exports.isEnabled = isEnabled; + exports.setFetchMethod = setFetchMethod; + + Object.defineProperty(exports, "__esModule", {value: true}); +}); diff --git a/ui/vendor/darkreader/darkreader.mjs b/ui/vendor/darkreader/darkreader.mjs new file mode 100644 index 0000000..816a07e --- /dev/null +++ b/ui/vendor/darkreader/darkreader.mjs @@ -0,0 +1,9048 @@ +/** + * Dark Reader v4.9.129 + * https://darkreader.org/ + */ + +var MessageTypeUItoBG; +(function (MessageTypeUItoBG) { + MessageTypeUItoBG["GET_DATA"] = "ui-bg-get-data"; + MessageTypeUItoBG["GET_DEVTOOLS_DATA"] = "ui-bg-get-devtools-data"; + MessageTypeUItoBG["SUBSCRIBE_TO_CHANGES"] = "ui-bg-subscribe-to-changes"; + MessageTypeUItoBG["UNSUBSCRIBE_FROM_CHANGES"] = + "ui-bg-unsubscribe-from-changes"; + MessageTypeUItoBG["CHANGE_SETTINGS"] = "ui-bg-change-settings"; + MessageTypeUItoBG["SET_THEME"] = "ui-bg-set-theme"; + MessageTypeUItoBG["TOGGLE_ACTIVE_TAB"] = "ui-bg-toggle-active-tab"; + MessageTypeUItoBG["MARK_NEWS_AS_READ"] = "ui-bg-mark-news-as-read"; + MessageTypeUItoBG["MARK_NEWS_AS_DISPLAYED"] = + "ui-bg-mark-news-as-displayed"; + MessageTypeUItoBG["LOAD_CONFIG"] = "ui-bg-load-config"; + MessageTypeUItoBG["APPLY_DEV_FIXES"] = "ui-bg-apply-dev-fixes"; + MessageTypeUItoBG["RESET_DEV_FIXES"] = "ui-bg-reset-dev-fixes"; + MessageTypeUItoBG["START_ACTIVATION"] = "ui-bg-start-activation"; + MessageTypeUItoBG["RESET_ACTIVATION"] = "ui-bg-reset-activation"; + MessageTypeUItoBG["COLOR_SCHEME_CHANGE"] = "ui-bg-color-scheme-change"; + MessageTypeUItoBG["HIDE_HIGHLIGHTS"] = "ui-bg-hide-highlights"; +})(MessageTypeUItoBG || (MessageTypeUItoBG = {})); +var MessageTypeBGtoUI; +(function (MessageTypeBGtoUI) { + MessageTypeBGtoUI["CHANGES"] = "bg-ui-changes"; +})(MessageTypeBGtoUI || (MessageTypeBGtoUI = {})); +var DebugMessageTypeBGtoUI; +(function (DebugMessageTypeBGtoUI) { + DebugMessageTypeBGtoUI["CSS_UPDATE"] = "debug-bg-ui-css-update"; + DebugMessageTypeBGtoUI["UPDATE"] = "debug-bg-ui-update"; +})(DebugMessageTypeBGtoUI || (DebugMessageTypeBGtoUI = {})); +var MessageTypeBGtoCS; +(function (MessageTypeBGtoCS) { + MessageTypeBGtoCS["ADD_CSS_FILTER"] = "bg-cs-add-css-filter"; + MessageTypeBGtoCS["ADD_DYNAMIC_THEME"] = "bg-cs-add-dynamic-theme"; + MessageTypeBGtoCS["ADD_STATIC_THEME"] = "bg-cs-add-static-theme"; + MessageTypeBGtoCS["ADD_SVG_FILTER"] = "bg-cs-add-svg-filter"; + MessageTypeBGtoCS["CLEAN_UP"] = "bg-cs-clean-up"; + MessageTypeBGtoCS["FETCH_RESPONSE"] = "bg-cs-fetch-response"; + MessageTypeBGtoCS["UNSUPPORTED_SENDER"] = "bg-cs-unsupported-sender"; +})(MessageTypeBGtoCS || (MessageTypeBGtoCS = {})); +var DebugMessageTypeBGtoCS; +(function (DebugMessageTypeBGtoCS) { + DebugMessageTypeBGtoCS["RELOAD"] = "debug-bg-cs-reload"; +})(DebugMessageTypeBGtoCS || (DebugMessageTypeBGtoCS = {})); +var MessageTypeCStoBG; +(function (MessageTypeCStoBG) { + MessageTypeCStoBG["COLOR_SCHEME_CHANGE"] = "cs-bg-color-scheme-change"; + MessageTypeCStoBG["DARK_THEME_DETECTED"] = "cs-bg-dark-theme-detected"; + MessageTypeCStoBG["DARK_THEME_NOT_DETECTED"] = + "cs-bg-dark-theme-not-detected"; + MessageTypeCStoBG["FETCH"] = "cs-bg-fetch"; + MessageTypeCStoBG["DOCUMENT_CONNECT"] = "cs-bg-document-connect"; + MessageTypeCStoBG["DOCUMENT_FORGET"] = "cs-bg-document-forget"; + MessageTypeCStoBG["DOCUMENT_FREEZE"] = "cs-bg-document-freeze"; + MessageTypeCStoBG["DOCUMENT_RESUME"] = "cs-bg-document-resume"; +})(MessageTypeCStoBG || (MessageTypeCStoBG = {})); +var DebugMessageTypeCStoBG; +(function (DebugMessageTypeCStoBG) { + DebugMessageTypeCStoBG["LOG"] = "debug-cs-bg-log"; +})(DebugMessageTypeCStoBG || (DebugMessageTypeCStoBG = {})); +var MessageTypeCStoUI; +(function (MessageTypeCStoUI) { + MessageTypeCStoUI["EXPORT_CSS_RESPONSE"] = "cs-ui-export-css-response"; +})(MessageTypeCStoUI || (MessageTypeCStoUI = {})); +var MessageTypeUItoCS; +(function (MessageTypeUItoCS) { + MessageTypeUItoCS["EXPORT_CSS"] = "ui-cs-export-css"; +})(MessageTypeUItoCS || (MessageTypeUItoCS = {})); + +const isNavigatorDefined = typeof navigator !== "undefined"; +const userAgent = isNavigatorDefined + ? navigator.userAgentData && Array.isArray(navigator.userAgentData.brands) + ? navigator.userAgentData.brands + .map((brand) => `${brand.brand.toLowerCase()} ${brand.version}`) + .join(" ") + : navigator.userAgent.toLowerCase() + : "some useragent"; +const platform = isNavigatorDefined + ? navigator.userAgentData && + typeof navigator.userAgentData.platform === "string" + ? navigator.userAgentData.platform.toLowerCase() + : navigator.platform.toLowerCase() + : "some platform"; +const isChromium = + userAgent.includes("chrome") || userAgent.includes("chromium"); +const isFirefox = + userAgent.includes("firefox") || + userAgent.includes("thunderbird") || + userAgent.includes("librewolf"); +const isSafari = userAgent.includes("safari") && !isChromium; +const isWindows = platform.startsWith("win"); +const isMacOS = platform.startsWith("mac"); +const isMobile = + isNavigatorDefined && navigator.userAgentData + ? navigator.userAgentData.mobile + : userAgent.includes("mobile") || false; +const isShadowDomSupported = typeof ShadowRoot === "function"; +const isMatchMediaChangeEventListenerSupported = + typeof MediaQueryList === "function" && + typeof MediaQueryList.prototype.addEventListener === "function"; +const isLayerRuleSupported = typeof CSSLayerBlockRule === "function"; +const isContainerRuleSupported = typeof CSSContainerRule === "function"; +(() => { + const m = userAgent.match(/chrom(?:e|ium)(?:\/| )([^ ]+)/); + if (m && m[1]) { + return m[1]; + } + return ""; +})(); +(() => { + const m = userAgent.match(/(?:firefox|librewolf)(?:\/| )([^ ]+)/); + if (m && m[1]) { + return m[1]; + } + return ""; +})(); +const isDefinedSelectorSupported = (() => { + try { + document.querySelector(":defined"); + return true; + } catch (err) { + return false; + } +})(); +const isCSSColorSchemePropSupported = (() => { + try { + if (typeof document === "undefined") { + return false; + } + const el = document.createElement("div"); + if (!el || typeof el.style !== "object") { + return false; + } + if (typeof el.style.colorScheme === "string") { + return true; + } + el.setAttribute("style", "color-scheme: dark"); + return el.style.colorScheme === "dark"; + } catch (e) { + return false; + } +})(); + +async function getOKResponse(url, mimeType, origin) { + const credentials = + origin && url.startsWith(`${origin}/`) ? undefined : "omit"; + const redirect = mimeType === "text/css" ? undefined : "error"; + const response = await fetch(url, { + cache: "force-cache", + credentials, + referrer: origin, + redirect + }); + if ( + isFirefox && + mimeType === "text/css" && + url.startsWith("moz-extension://") && + url.endsWith(".css") + ) { + return response; + } + const contentType = response.headers.get("Content-Type"); + if ( + mimeType && + !(contentType === mimeType || contentType?.startsWith(`${mimeType};`)) + ) { + throw new Error(`Mime type mismatch when loading ${url}`); + } + if ( + response.redirected && + response.url && + shouldIgnoreCors(new URL(response.url)) + ) { + throw new Error("Cross-origin limit reached"); + } + if (!response.ok) { + throw new Error( + `Unable to load ${url} ${response.status} ${response.statusText}` + ); + } + return response; +} +async function loadAsDataURL(url, mimeType) { + const response = await getOKResponse(url, mimeType); + return await readResponseAsDataURL(response); +} +async function loadAsBlob(url, mimeType) { + const response = await getOKResponse(url, mimeType); + return await response.blob(); +} +async function readResponseAsDataURL(response) { + const blob = await response.blob(); + const dataURL = await new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result); + reader.onerror = () => reject(reader.error); + reader.readAsDataURL(blob); + }); + return dataURL; +} +async function loadAsText(url, mimeType, origin) { + const response = await getOKResponse(url, mimeType, origin); + return await response.text(); +} +const MAX_CORS_HOSTS = 16; +const corsHosts = new Set(); +const checkedHosts = new Set(); +const localAliases = [ + "127-0-0-1.org.uk", + "42foo.com", + "domaincontrol.com", + "fbi.com", + "fuf.me", + "lacolhost.com", + "local.sisteminha.com", + "localfabriek.nl", + "localhost", + "localhst.co.uk", + "localmachine.info", + "localmachine.name", + "localtest.me", + "lvh.me", + "mouse-potato.com", + "nip.io", + "sslip.io", + "vcap.me", + "xip.io", + "yoogle.com" +]; +const localSubDomains = [ + ".corp", + ".direct", + ".home", + ".internal", + ".intranet", + ".lan", + ".local", + ".localdomain", + ".test", + ".zz", + ...localAliases.map((alias) => `.${alias}`) +]; +function isIPHost(hostname) { + if (hostname.startsWith("[")) { + return true; + } + const labels = hostname.split("."); + const last = labels[labels.length - 1]; + return /^(0x[0-9a-f]+|\d+)$/i.test(last); +} +function shouldIgnoreCors(url) { + const {host, port, protocol} = url; + const hostname = url.hostname.endsWith(".") + ? url.hostname.slice(0, -1) + : url.hostname; + if (!corsHosts.has(host)) { + corsHosts.add(host); + } + if (checkedHosts.has(host)) { + return false; + } + if ( + corsHosts.size >= MAX_CORS_HOSTS || + protocol !== "https:" || + port !== "" || + localAliases.includes(hostname) || + localSubDomains.some((sub) => hostname.endsWith(sub)) || + isIPHost(hostname) + ) { + return true; + } + checkedHosts.add(host); + return false; +} + +const throwCORSError = async (url) => { + return Promise.reject( + new Error( + [ + "Embedded Dark Reader cannot access a cross-origin resource", + url, + "Overview your URLs and CORS policies or use", + "`DarkReader.setFetchMethod(fetch: (url) => Promise))`.", + "See if using `DarkReader.setFetchMethod(window.fetch)`", + "before `DarkReader.enable()` works." + ].join(" ") + ) + ); +}; +let fetcher = throwCORSError; +function setFetchMethod$1(fetch) { + if (fetch) { + fetcher = fetch; + } else { + fetcher = throwCORSError; + } +} +async function callFetchMethod(url) { + return await fetcher(url); +} + +if (!window.chrome) { + window.chrome = {}; +} +if (!chrome.runtime) { + chrome.runtime = {}; +} +const messageListeners = new Set(); +async function sendMessage(...args) { + if (args[0] && args[0].type === MessageTypeCStoBG.FETCH) { + const {id} = args[0]; + try { + const {url, responseType} = args[0].data; + const response = await callFetchMethod(url); + let text; + if (responseType === "data-url") { + text = await readResponseAsDataURL(response); + } else { + text = await response.text(); + } + messageListeners.forEach((cb) => + cb({ + type: MessageTypeBGtoCS.FETCH_RESPONSE, + data: text, + error: null, + id + }) + ); + } catch (error) { + console.error(error); + messageListeners.forEach((cb) => + cb({ + type: MessageTypeBGtoCS.FETCH_RESPONSE, + data: null, + error, + id + }) + ); + } + } +} +function addMessageListener(callback) { + messageListeners.add(callback); +} +if (typeof chrome.runtime.sendMessage === "function") { + const nativeSendMessage = chrome.runtime.sendMessage; + chrome.runtime.sendMessage = (...args) => { + sendMessage(...args); + nativeSendMessage.apply(chrome.runtime, args); + }; +} else { + chrome.runtime.sendMessage = sendMessage; +} +if (!chrome.runtime.onMessage) { + chrome.runtime.onMessage = {}; +} +if (typeof chrome.runtime.onMessage.addListener === "function") { + const nativeAddListener = chrome.runtime.onMessage.addListener; + chrome.runtime.onMessage.addListener = (...args) => { + addMessageListener(args[0]); + nativeAddListener.apply(chrome.runtime.onMessage, args); + }; +} else { + chrome.runtime.onMessage.addListener = (...args) => + addMessageListener(args[0]); +} + +var ThemeEngine; +(function (ThemeEngine) { + ThemeEngine["cssFilter"] = "cssFilter"; + ThemeEngine["svgFilter"] = "svgFilter"; + ThemeEngine["staticTheme"] = "staticTheme"; + ThemeEngine["dynamicTheme"] = "dynamicTheme"; +})(ThemeEngine || (ThemeEngine = {})); + +var AutomationMode; +(function (AutomationMode) { + AutomationMode["NONE"] = ""; + AutomationMode["TIME"] = "time"; + AutomationMode["SYSTEM"] = "system"; + AutomationMode["LOCATION"] = "location"; +})(AutomationMode || (AutomationMode = {})); + +const DEFAULT_COLORS = { + darkScheme: { + background: "#181a1b", + text: "#e8e6e3" + }, + lightScheme: { + background: "#dcdad7", + text: "#181a1b" + } +}; +const DEFAULT_THEME = { + mode: 1, + brightness: 100, + contrast: 100, + grayscale: 0, + sepia: 0, + useFont: false, + fontFamily: isMacOS + ? "Helvetica Neue" + : isWindows + ? "Segoe UI" + : "Open Sans", + textStroke: 0, + engine: ThemeEngine.dynamicTheme, + stylesheet: "", + darkSchemeBackgroundColor: DEFAULT_COLORS.darkScheme.background, + darkSchemeTextColor: DEFAULT_COLORS.darkScheme.text, + lightSchemeBackgroundColor: DEFAULT_COLORS.lightScheme.background, + lightSchemeTextColor: DEFAULT_COLORS.lightScheme.text, + scrollbarColor: "", + selectionColor: "auto", + styleSystemControls: !isCSSColorSchemePropSupported, + lightColorScheme: "Default", + darkColorScheme: "Default", + immediateModify: false +}; +const filterModeSites = [ + "*.officeapps.live.com", + "*.sharepoint.com", + "docs.google.com", + "onedrive.live.com" +]; +({ + customThemes: filterModeSites.map((url) => { + const engine = ThemeEngine.cssFilter; + return { + url: [url], + theme: {...DEFAULT_THEME, engine}, + builtIn: true + }; + }), + automation: { + mode: AutomationMode.NONE + } +}); + +function getMatches(regex, input, group = 0) { + const matches = []; + let m; + while ((m = regex.exec(input))) { + matches.push(m[group]); + } + return matches; +} +function getMatchesWithOffsets(regex, input, group = 0) { + const matches = []; + let m; + while ((m = regex.exec(input))) { + matches.push({text: m[group], offset: m.index}); + } + return matches; +} +function getHashCode(text) { + const len = text.length; + let hash = 0; + for (let i = 0; i < len; i++) { + const c = text.charCodeAt(i); + hash = ((hash << 5) - hash + c) & 4294967295; + } + return hash; +} +function escapeRegExpSpecialChars(input) { + return input.replaceAll(/[\^$.*+?\(\)\[\]{}|\-\\]/g, "\\$&"); +} +function getParenthesesRange(input, searchStartIndex = 0) { + return getOpenCloseRange(input, searchStartIndex, "(", ")", []); +} +function getOpenCloseRange( + input, + searchStartIndex, + openToken, + closeToken, + excludeRanges +) { + let indexOf; + if (excludeRanges.length === 0) { + indexOf = (token, pos) => input.indexOf(token, pos); + } else { + indexOf = (token, pos) => + indexOfExcluding(input, token, pos, excludeRanges); + } + const {length} = input; + let depth = 0; + let firstOpenIndex = -1; + for (let i = searchStartIndex; i < length; i++) { + if (depth === 0) { + const openIndex = indexOf(openToken, i); + if (openIndex < 0) { + break; + } + firstOpenIndex = openIndex; + depth++; + i = openIndex; + } else { + const closeIndex = indexOf(closeToken, i); + if (closeIndex < 0) { + break; + } + const openIndex = indexOf(openToken, i); + if (openIndex < 0 || closeIndex <= openIndex) { + depth--; + if (depth === 0) { + return {start: firstOpenIndex, end: closeIndex + 1}; + } + i = closeIndex; + } else { + depth++; + i = openIndex; + } + } + } + return null; +} +function indexOfExcluding(input, search, position, excludeRanges) { + const i = input.indexOf(search, position); + const exclusion = excludeRanges.find((r) => i >= r.start && i < r.end); + if (exclusion) { + return indexOfExcluding(input, search, exclusion.end, excludeRanges); + } + return i; +} +function splitExcluding(input, separator, excludeRanges) { + const parts = []; + let commaIndex = -1; + let currIndex = 0; + while ( + (commaIndex = indexOfExcluding( + input, + separator, + currIndex, + excludeRanges + )) >= 0 + ) { + parts.push(input.substring(currIndex, commaIndex).trim()); + currIndex = commaIndex + 1; + } + parts.push(input.substring(currIndex).trim()); + return parts; +} + +let anchor; +const parsedURLCache = new Map(); +function fixBaseURL($url) { + if (!anchor) { + anchor = document.createElement("a"); + } + anchor.href = $url; + return anchor.href; +} +function parseURL($url, $base = null) { + const key = `${$url}${$base ? `;${$base}` : ""}`; + if (parsedURLCache.has(key)) { + return parsedURLCache.get(key); + } + if ($base) { + const parsedURL = new URL($url, fixBaseURL($base)); + parsedURLCache.set(key, parsedURL); + return parsedURL; + } + const parsedURL = new URL(fixBaseURL($url)); + parsedURLCache.set($url, parsedURL); + return parsedURL; +} +function getAbsoluteURL($base, $relative) { + if ($relative.match(/^data\\?\:/)) { + return $relative; + } + if (/^\/\//.test($relative)) { + return `${location.protocol}${$relative}`; + } + const b = parseURL($base); + const a = parseURL($relative, b.href); + return a.href; +} +function isRelativeHrefOnAbsolutePath(href) { + if (href.startsWith("data:")) { + return true; + } + const url = parseURL(href); + if (url.protocol !== location.protocol) { + return false; + } + if (url.hostname !== location.hostname) { + return false; + } + if (url.port !== location.port) { + return false; + } + return url.pathname === location.pathname; +} + +const excludedSelectors = [ + "pre", + "pre *", + "code", + '[aria-hidden="true"]', + '[class*="fa-"]', + ".fa", + ".fab", + ".fad", + ".fal", + ".far", + ".fas", + ".fass", + ".fasr", + ".fat", + ".icofont", + '[style*="font-"]', + '[class*="icon"]', + '[class*="Icon"]', + '[class*="symbol"]', + '[class*="Symbol"]', + ".glyphicon", + '[class*="material-symbol"]', + '[class*="material-icon"]', + "mu", + '[class*="mu-"]', + ".typcn", + '[class*="vjs-"]' +]; +function createTextStyle(config) { + const lines = []; + lines.push(`*:not(${excludedSelectors.join(", ")}) {`); + if (config.useFont && config.fontFamily) { + lines.push(` font-family: ${config.fontFamily} !important;`); + } + if (config.textStroke > 0) { + lines.push(` -webkit-text-stroke: ${config.textStroke}px !important;`); + lines.push(` text-stroke: ${config.textStroke}px !important;`); + } + lines.push("}"); + return lines.join("\n"); +} + +function isArrayLike(items) { + return items.length != null; +} +function forEach(items, iterator) { + if (isArrayLike(items)) { + for (let i = 0, len = items.length; i < len; i++) { + iterator(items[i]); + } + } else { + for (const item of items) { + iterator(item); + } + } +} +function push(array, addition) { + forEach(addition, (a) => array.push(a)); +} +function toArray(items) { + const results = []; + for (let i = 0, len = items.length; i < len; i++) { + results.push(items[i]); + } + return results; +} + +function scale(x, inLow, inHigh, outLow, outHigh) { + return ((x - inLow) * (outHigh - outLow)) / (inHigh - inLow) + outLow; +} +function clamp(x, min, max) { + return Math.min(max, Math.max(min, x)); +} +function multiplyMatrices(m1, m2) { + const result = []; + for (let i = 0, len = m1.length; i < len; i++) { + result[i] = []; + for (let j = 0, len2 = m2[0].length; j < len2; j++) { + let sum = 0; + for (let k = 0, len3 = m1[0].length; k < len3; k++) { + sum += m1[i][k] * m2[k][j]; + } + result[i][j] = sum; + } + } + return result; +} + +function createFilterMatrix(config) { + let m = Matrix.identity(); + if (config.sepia !== 0) { + m = multiplyMatrices(m, Matrix.sepia(config.sepia / 100)); + } + if (config.grayscale !== 0) { + m = multiplyMatrices(m, Matrix.grayscale(config.grayscale / 100)); + } + if (config.contrast !== 100) { + m = multiplyMatrices(m, Matrix.contrast(config.contrast / 100)); + } + if (config.brightness !== 100) { + m = multiplyMatrices(m, Matrix.brightness(config.brightness / 100)); + } + if (config.mode === 1) { + m = multiplyMatrices(m, Matrix.invertNHue()); + } + return m; +} +function applyColorMatrix([r, g, b], matrix) { + const rgb = [[r / 255], [g / 255], [b / 255], [1], [1]]; + const result = multiplyMatrices(matrix, rgb); + return [0, 1, 2].map((i) => clamp(Math.round(result[i][0] * 255), 0, 255)); +} +const Matrix = { + identity() { + return [ + [1, 0, 0, 0, 0], + [0, 1, 0, 0, 0], + [0, 0, 1, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + invertNHue() { + return [ + [0.333, -0.667, -0.667, 0, 1], + [-0.667, 0.333, -0.667, 0, 1], + [-0.667, -0.667, 0.333, 0, 1], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + brightness(v) { + return [ + [v, 0, 0, 0, 0], + [0, v, 0, 0, 0], + [0, 0, v, 0, 0], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + contrast(v) { + const t = (1 - v) / 2; + return [ + [v, 0, 0, 0, t], + [0, v, 0, 0, t], + [0, 0, v, 0, t], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + sepia(v) { + return [ + [ + 0.393 + 0.607 * (1 - v), + 0.769 - 0.769 * (1 - v), + 0.189 - 0.189 * (1 - v), + 0, + 0 + ], + [ + 0.349 - 0.349 * (1 - v), + 0.686 + 0.314 * (1 - v), + 0.168 - 0.168 * (1 - v), + 0, + 0 + ], + [ + 0.272 - 0.272 * (1 - v), + 0.534 - 0.534 * (1 - v), + 0.131 + 0.869 * (1 - v), + 0, + 0 + ], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + }, + grayscale(v) { + return [ + [ + 0.2126 + 0.7874 * (1 - v), + 0.7152 - 0.7152 * (1 - v), + 0.0722 - 0.0722 * (1 - v), + 0, + 0 + ], + [ + 0.2126 - 0.2126 * (1 - v), + 0.7152 + 0.2848 * (1 - v), + 0.0722 - 0.0722 * (1 - v), + 0, + 0 + ], + [ + 0.2126 - 0.2126 * (1 - v), + 0.7152 - 0.7152 * (1 - v), + 0.0722 + 0.9278 * (1 - v), + 0, + 0 + ], + [0, 0, 0, 1, 0], + [0, 0, 0, 0, 1] + ]; + } +}; + +var FilterMode; +(function (FilterMode) { + FilterMode[(FilterMode["light"] = 0)] = "light"; + FilterMode[(FilterMode["dark"] = 1)] = "dark"; +})(FilterMode || (FilterMode = {})); +function getCSSFilterValue(config) { + const filters = []; + if (config.mode === FilterMode.dark) { + filters.push("invert(100%) hue-rotate(180deg)"); + } + if (config.brightness !== 100) { + filters.push(`brightness(${config.brightness}%)`); + } + if (config.contrast !== 100) { + filters.push(`contrast(${config.contrast}%)`); + } + if (config.grayscale !== 0) { + filters.push(`grayscale(${config.grayscale}%)`); + } + if (config.sepia !== 0) { + filters.push(`sepia(${config.sepia}%)`); + } + if (filters.length === 0) { + return null; + } + return filters.join(" "); +} + +function evalMath(expression) { + const rpnStack = []; + const workingStack = []; + let lastToken; + for (let i = 0, len = expression.length; i < len; i++) { + const token = expression[i]; + if (!token || token === " ") { + continue; + } + if (operators.has(token)) { + const op = operators.get(token); + while (workingStack.length) { + const currentOp = operators.get(workingStack[0]); + if (!currentOp) { + break; + } + if (op.lessOrEqualThan(currentOp)) { + rpnStack.push(workingStack.shift()); + } else { + break; + } + } + workingStack.unshift(token); + } else if (!lastToken || operators.has(lastToken)) { + rpnStack.push(token); + } else { + rpnStack[rpnStack.length - 1] += token; + } + lastToken = token; + } + rpnStack.push(...workingStack); + const stack = []; + for (let i = 0, len = rpnStack.length; i < len; i++) { + const op = operators.get(rpnStack[i]); + if (op) { + const args = stack.splice(0, 2); + stack.push(op.exec(args[1], args[0])); + } else { + stack.unshift(parseFloat(rpnStack[i])); + } + } + return stack[0]; +} +class Operator { + constructor(precedence, method) { + this.precendce = precedence; + this.execMethod = method; + } + exec(left, right) { + return this.execMethod(left, right); + } + lessOrEqualThan(op) { + return this.precendce <= op.precendce; + } +} +const operators = new Map([ + ["+", new Operator(1, (left, right) => left + right)], + ["-", new Operator(1, (left, right) => left - right)], + ["*", new Operator(2, (left, right) => left * right)], + ["/", new Operator(2, (left, right) => left / right)] +]); + +const isSystemDarkModeEnabled = () => + matchMedia("(prefers-color-scheme: dark)").matches; + +const hslaParseCache = new Map(); +const rgbaParseCache = new Map(); +function parseColorWithCache($color) { + $color = $color.trim(); + const key = $color; + if (rgbaParseCache.has(key)) { + return rgbaParseCache.get(key); + } + if ($color.includes("calc(")) { + $color = lowerCalcExpression($color); + } + const color = parse($color); + rgbaParseCache.set(key, color); + return color; +} +function parseToHSLWithCache(color) { + if (hslaParseCache.has(color)) { + return hslaParseCache.get(color); + } + const rgb = parseColorWithCache(color); + if (!rgb) { + return null; + } + const hsl = rgbToHSL(rgb); + hslaParseCache.set(color, hsl); + return hsl; +} +function clearColorCache() { + hslaParseCache.clear(); + rgbaParseCache.clear(); +} +function hslToRGB({h, s, l, a = 1}) { + if (s === 0) { + const [r, b, g] = [l, l, l].map((x) => Math.round(x * 255)); + return {r, g, b, a}; + } + const c = (1 - Math.abs(2 * l - 1)) * s; + const x = c * (1 - Math.abs(((h / 60) % 2) - 1)); + const m = l - c / 2; + const [r, g, b] = ( + h < 60 + ? [c, x, 0] + : h < 120 + ? [x, c, 0] + : h < 180 + ? [0, c, x] + : h < 240 + ? [0, x, c] + : h < 300 + ? [x, 0, c] + : [c, 0, x] + ).map((n) => Math.round((n + m) * 255)); + return {r, g, b, a}; +} +function rgbToHSL({r: r255, g: g255, b: b255, a = 1}) { + const r = r255 / 255; + const g = g255 / 255; + const b = b255 / 255; + const max = Math.max(r, g, b); + const min = Math.min(r, g, b); + const c = max - min; + const l = (max + min) / 2; + if (c === 0) { + return {h: 0, s: 0, l, a}; + } + let h = + (max === r + ? ((g - b) / c) % 6 + : max === g + ? (b - r) / c + 2 + : (r - g) / c + 4) * 60; + if (h < 0) { + h += 360; + } + const s = c / (1 - Math.abs(2 * l - 1)); + return {h, s, l, a}; +} +function toFixed(n, digits = 0) { + const fixed = n.toFixed(digits); + if (digits === 0) { + return fixed; + } + const dot = fixed.indexOf("."); + if (dot >= 0) { + const zerosMatch = fixed.match(/0+$/); + if (zerosMatch) { + if (zerosMatch.index === dot + 1) { + return fixed.substring(0, dot); + } + return fixed.substring(0, zerosMatch.index); + } + } + return fixed; +} +function rgbToString(rgb) { + const {r, g, b, a} = rgb; + if (a != null && a < 1) { + return `rgba(${toFixed(r)}, ${toFixed(g)}, ${toFixed(b)}, ${toFixed(a, 2)})`; + } + return `rgb(${toFixed(r)}, ${toFixed(g)}, ${toFixed(b)})`; +} +function rgbToHexString({r, g, b, a}) { + return `#${(a != null && a < 1 ? [r, g, b, Math.round(a * 255)] : [r, g, b]) + .map((x) => { + return `${x < 16 ? "0" : ""}${x.toString(16)}`; + }) + .join("")}`; +} +function hslToString(hsl) { + const {h, s, l, a} = hsl; + if (a != null && a < 1) { + return `hsla(${toFixed(h)}, ${toFixed(s * 100)}%, ${toFixed(l * 100)}%, ${toFixed(a, 2)})`; + } + return `hsl(${toFixed(h)}, ${toFixed(s * 100)}%, ${toFixed(l * 100)}%)`; +} +const rgbMatch = /^rgba?\([^\(\)]+\)$/; +const hslMatch = /^hsla?\([^\(\)]+\)$/; +const hexMatch = /^#[0-9a-f]+$/i; +const supportedColorFuncs = [ + "color", + "color-mix", + "hwb", + "lab", + "lch", + "oklab", + "oklch" +]; +function parse($color) { + const c = $color.trim().toLowerCase(); + if (c.includes("(from ")) { + if (c.indexOf("(from") !== c.lastIndexOf("(from")) { + return null; + } + return domParseColor(c); + } + if (c.match(rgbMatch)) { + if (c.startsWith("rgb(#") || c.startsWith("rgba(#")) { + if (c.lastIndexOf("rgb") > 0) { + return null; + } + return domParseColor(c); + } + return parseRGB(c); + } + if (c.match(hslMatch)) { + return parseHSL(c); + } + if (c.match(hexMatch)) { + return parseHex(c); + } + if (knownColors.has(c)) { + return getColorByName(c); + } + if (systemColors.has(c)) { + return getSystemColor(c); + } + if (c === "transparent") { + return {r: 0, g: 0, b: 0, a: 0}; + } + if ( + c.endsWith(")") && + supportedColorFuncs.some( + (fn) => + c.startsWith(fn) && + c[fn.length] === "(" && + c.lastIndexOf(fn) === 0 + ) + ) { + return domParseColor(c); + } + if (c.startsWith("light-dark(") && c.endsWith(")")) { + const match = c.match( + /^light-dark\(\s*([a-z]+(\(.*\))?),\s*([a-z]+(\(.*\))?)\s*\)$/ + ); + if (match) { + const schemeColor = isSystemDarkModeEnabled() ? match[3] : match[1]; + return parse(schemeColor); + } + } + return null; +} +const C_0 = "0".charCodeAt(0); +const C_9 = "9".charCodeAt(0); +const C_e = "e".charCodeAt(0); +const C_DOT = ".".charCodeAt(0); +const C_PLUS = "+".charCodeAt(0); +const C_MINUS = "-".charCodeAt(0); +const C_SPACE = " ".charCodeAt(0); +const C_COMMA = ",".charCodeAt(0); +const C_SLASH = "/".charCodeAt(0); +const C_PERCENT = "%".charCodeAt(0); +function getNumbersFromString(input, range, units) { + const numbers = []; + const searchStart = input.indexOf("(") + 1; + const searchEnd = input.length - 1; + let numStart = -1; + let unitStart = -1; + const push = (matchEnd) => { + const numEnd = unitStart > -1 ? unitStart : matchEnd; + const $num = input.slice(numStart, numEnd); + let n = parseFloat($num); + const r = range[numbers.length]; + if (unitStart > -1) { + const unit = input.slice(unitStart, matchEnd); + const u = units[unit]; + if (u != null) { + n *= r / u; + } + } + if (r > 1) { + n = Math.round(n); + } + numbers.push(n); + numStart = -1; + unitStart = -1; + }; + for (let i = searchStart; i < searchEnd; i++) { + const c = input.charCodeAt(i); + const isNumChar = + (c >= C_0 && c <= C_9) || + c === C_DOT || + c === C_PLUS || + c === C_MINUS || + c === C_e; + const isDelimiter = c === C_SPACE || c === C_COMMA || c === C_SLASH; + if (isNumChar) { + if (numStart === -1) { + numStart = i; + } + } else if (numStart > -1) { + if (isDelimiter) { + push(i); + } else if (unitStart === -1) { + unitStart = i; + } + } + } + if (numStart > -1) { + push(searchEnd); + } + return numbers; +} +const rgbRange = [255, 255, 255, 1]; +const rgbUnits = {"%": 100}; +function getRGBValues(input) { + const CHAR_CODE_0 = 48; + const length = input.length; + let i = 0; + let digitsCount = 0; + let digitSequence = false; + let floatDigitsCount = -1; + let delimiter = C_SPACE; + let channel = -1; + let result = null; + while (i < length) { + const c = input.charCodeAt(i); + if ((c >= C_0 && c <= C_9) || c === C_DOT) { + if (!digitSequence) { + digitSequence = true; + digitsCount = 0; + floatDigitsCount = -1; + channel++; + if (channel === 3 && result) { + result[3] = 0; + } + if (channel > 3) { + return null; + } + } + if (c === C_DOT) { + if (floatDigitsCount > 0) { + return null; + } + floatDigitsCount = 0; + } else { + const d = c - CHAR_CODE_0; + if (!result) { + result = [0, 0, 0, 1]; + } + if (floatDigitsCount > -1) { + floatDigitsCount++; + result[channel] += d / 10 ** floatDigitsCount; + } else { + digitsCount++; + if (digitsCount > 3) { + return null; + } + result[channel] = result[channel] * 10 + d; + } + } + } else if (c === C_PERCENT) { + if ( + channel < 0 || + channel > 3 || + delimiter !== C_SPACE || + !result + ) { + return null; + } + result[channel] = + channel < 3 + ? Math.round((result[channel] * 255) / 100) + : result[channel] / 100; + digitSequence = false; + } else { + digitSequence = false; + if (c === C_SPACE) { + if (channel === 0) { + delimiter = c; + } + } else if (c === C_COMMA) { + if (channel === -1) { + return null; + } + delimiter = C_COMMA; + } else if (c === C_SLASH) { + if (channel !== 2 || delimiter !== C_SPACE) { + return null; + } + } else { + return null; + } + } + i++; + } + if (channel < 2 || channel > 3) { + return null; + } + return result; +} +function parseRGB($rgb) { + const [r, g, b, a = 1] = getNumbersFromString($rgb, rgbRange, rgbUnits); + if (r == null || g == null || b == null || a == null) { + return null; + } + return {r, g, b, a}; +} +const hslRange = [360, 1, 1, 1]; +const hslUnits = {"%": 100, "deg": 360, "rad": 2 * Math.PI, "turn": 1}; +function parseHSL($hsl) { + const [h, s, l, a = 1] = getNumbersFromString($hsl, hslRange, hslUnits); + if (h == null || s == null || l == null || a == null) { + return null; + } + return hslToRGB({h, s, l, a}); +} +const C_A = "A".charCodeAt(0); +const C_F = "F".charCodeAt(0); +const C_a = "a".charCodeAt(0); +const C_f = "f".charCodeAt(0); +function parseHex($hex) { + const length = $hex.length; + const digitCount = length - 1; + const isShort = digitCount === 3 || digitCount === 4; + const isLong = digitCount === 6 || digitCount === 8; + if (!isShort && !isLong) { + return null; + } + const hex = (i) => { + const c = $hex.charCodeAt(i); + if (c >= C_A && c <= C_F) { + return c + 10 - C_A; + } + if (c >= C_a && c <= C_f) { + return c + 10 - C_a; + } + return c - C_0; + }; + let r; + let g; + let b; + let a = 1; + if (isShort) { + r = hex(1) * 17; + g = hex(2) * 17; + b = hex(3) * 17; + if (digitCount === 4) { + a = (hex(4) * 17) / 255; + } + } else { + r = hex(1) * 16 + hex(2); + g = hex(3) * 16 + hex(4); + b = hex(5) * 16 + hex(6); + if (digitCount === 8) { + a = (hex(7) * 16 + hex(8)) / 255; + } + } + return {r, g, b, a}; +} +function getColorByName($color) { + const n = knownColors.get($color); + return { + r: (n >> 16) & 255, + g: (n >> 8) & 255, + b: (n >> 0) & 255, + a: 1 + }; +} +function getSystemColor($color) { + const n = systemColors.get($color); + return { + r: (n >> 16) & 255, + g: (n >> 8) & 255, + b: (n >> 0) & 255, + a: 1 + }; +} +function lowerCalcExpression(color) { + let searchIndex = 0; + const replaceBetweenIndices = (start, end, replacement) => { + color = color.substring(0, start) + replacement + color.substring(end); + }; + while ((searchIndex = color.indexOf("calc(")) !== -1) { + const range = getParenthesesRange(color, searchIndex); + if (!range) { + break; + } + let slice = color.slice(range.start + 1, range.end - 1); + const includesPercentage = slice.includes("%"); + slice = slice.split("%").join(""); + const output = Math.round(evalMath(slice)); + replaceBetweenIndices( + range.start - 4, + range.end, + output + (includesPercentage ? "%" : "") + ); + } + return color; +} +const knownColors = new Map( + Object.entries({ + aliceblue: 0xf0f8ff, + antiquewhite: 0xfaebd7, + aqua: 0x00ffff, + aquamarine: 0x7fffd4, + azure: 0xf0ffff, + beige: 0xf5f5dc, + bisque: 0xffe4c4, + black: 0x000000, + blanchedalmond: 0xffebcd, + blue: 0x0000ff, + blueviolet: 0x8a2be2, + brown: 0xa52a2a, + burlywood: 0xdeb887, + cadetblue: 0x5f9ea0, + chartreuse: 0x7fff00, + chocolate: 0xd2691e, + coral: 0xff7f50, + cornflowerblue: 0x6495ed, + cornsilk: 0xfff8dc, + crimson: 0xdc143c, + cyan: 0x00ffff, + darkblue: 0x00008b, + darkcyan: 0x008b8b, + darkgoldenrod: 0xb8860b, + darkgray: 0xa9a9a9, + darkgrey: 0xa9a9a9, + darkgreen: 0x006400, + darkkhaki: 0xbdb76b, + darkmagenta: 0x8b008b, + darkolivegreen: 0x556b2f, + darkorange: 0xff8c00, + darkorchid: 0x9932cc, + darkred: 0x8b0000, + darksalmon: 0xe9967a, + darkseagreen: 0x8fbc8f, + darkslateblue: 0x483d8b, + darkslategray: 0x2f4f4f, + darkslategrey: 0x2f4f4f, + darkturquoise: 0x00ced1, + darkviolet: 0x9400d3, + deeppink: 0xff1493, + deepskyblue: 0x00bfff, + dimgray: 0x696969, + dimgrey: 0x696969, + dodgerblue: 0x1e90ff, + firebrick: 0xb22222, + floralwhite: 0xfffaf0, + forestgreen: 0x228b22, + fuchsia: 0xff00ff, + gainsboro: 0xdcdcdc, + ghostwhite: 0xf8f8ff, + gold: 0xffd700, + goldenrod: 0xdaa520, + gray: 0x808080, + grey: 0x808080, + green: 0x008000, + greenyellow: 0xadff2f, + honeydew: 0xf0fff0, + hotpink: 0xff69b4, + indianred: 0xcd5c5c, + indigo: 0x4b0082, + ivory: 0xfffff0, + khaki: 0xf0e68c, + lavender: 0xe6e6fa, + lavenderblush: 0xfff0f5, + lawngreen: 0x7cfc00, + lemonchiffon: 0xfffacd, + lightblue: 0xadd8e6, + lightcoral: 0xf08080, + lightcyan: 0xe0ffff, + lightgoldenrodyellow: 0xfafad2, + lightgray: 0xd3d3d3, + lightgrey: 0xd3d3d3, + lightgreen: 0x90ee90, + lightpink: 0xffb6c1, + lightsalmon: 0xffa07a, + lightseagreen: 0x20b2aa, + lightskyblue: 0x87cefa, + lightslategray: 0x778899, + lightslategrey: 0x778899, + lightsteelblue: 0xb0c4de, + lightyellow: 0xffffe0, + lime: 0x00ff00, + limegreen: 0x32cd32, + linen: 0xfaf0e6, + magenta: 0xff00ff, + maroon: 0x800000, + mediumaquamarine: 0x66cdaa, + mediumblue: 0x0000cd, + mediumorchid: 0xba55d3, + mediumpurple: 0x9370db, + mediumseagreen: 0x3cb371, + mediumslateblue: 0x7b68ee, + mediumspringgreen: 0x00fa9a, + mediumturquoise: 0x48d1cc, + mediumvioletred: 0xc71585, + midnightblue: 0x191970, + mintcream: 0xf5fffa, + mistyrose: 0xffe4e1, + moccasin: 0xffe4b5, + navajowhite: 0xffdead, + navy: 0x000080, + oldlace: 0xfdf5e6, + olive: 0x808000, + olivedrab: 0x6b8e23, + orange: 0xffa500, + orangered: 0xff4500, + orchid: 0xda70d6, + palegoldenrod: 0xeee8aa, + palegreen: 0x98fb98, + paleturquoise: 0xafeeee, + palevioletred: 0xdb7093, + papayawhip: 0xffefd5, + peachpuff: 0xffdab9, + peru: 0xcd853f, + pink: 0xffc0cb, + plum: 0xdda0dd, + powderblue: 0xb0e0e6, + purple: 0x800080, + rebeccapurple: 0x663399, + red: 0xff0000, + rosybrown: 0xbc8f8f, + royalblue: 0x4169e1, + saddlebrown: 0x8b4513, + salmon: 0xfa8072, + sandybrown: 0xf4a460, + seagreen: 0x2e8b57, + seashell: 0xfff5ee, + sienna: 0xa0522d, + silver: 0xc0c0c0, + skyblue: 0x87ceeb, + slateblue: 0x6a5acd, + slategray: 0x708090, + slategrey: 0x708090, + snow: 0xfffafa, + springgreen: 0x00ff7f, + steelblue: 0x4682b4, + tan: 0xd2b48c, + teal: 0x008080, + thistle: 0xd8bfd8, + tomato: 0xff6347, + turquoise: 0x40e0d0, + violet: 0xee82ee, + wheat: 0xf5deb3, + white: 0xffffff, + whitesmoke: 0xf5f5f5, + yellow: 0xffff00, + yellowgreen: 0x9acd32 + }) +); +const systemColors = new Map( + Object.entries({ + "ActiveBorder": 0x3b99fc, + "ActiveCaption": 0x000000, + "AppWorkspace": 0xaaaaaa, + "Background": 0x6363ce, + "ButtonFace": 0xffffff, + "ButtonHighlight": 0xe9e9e9, + "ButtonShadow": 0x9fa09f, + "ButtonText": 0x000000, + "CaptionText": 0x000000, + "GrayText": 0x7f7f7f, + "Highlight": 0xb2d7ff, + "HighlightText": 0x000000, + "InactiveBorder": 0xffffff, + "InactiveCaption": 0xffffff, + "InactiveCaptionText": 0x000000, + "InfoBackground": 0xfbfcc5, + "InfoText": 0x000000, + "Menu": 0xf6f6f6, + "MenuText": 0xffffff, + "Scrollbar": 0xaaaaaa, + "ThreeDDarkShadow": 0x000000, + "ThreeDFace": 0xc0c0c0, + "ThreeDHighlight": 0xffffff, + "ThreeDLightShadow": 0xffffff, + "ThreeDShadow": 0x000000, + "Window": 0xececec, + "WindowFrame": 0xaaaaaa, + "WindowText": 0x000000, + "-webkit-focus-ring-color": 0xe59700 + }).map(([key, value]) => [key.toLowerCase(), value]) +); +function getSRGBLightness(r, g, b) { + return (0.2126 * r + 0.7152 * g + 0.0722 * b) / 255; +} +let canvas$1; +let context$1; +function domParseColor($color) { + if (!context$1) { + canvas$1 = document.createElement("canvas"); + canvas$1.width = 1; + canvas$1.height = 1; + context$1 = canvas$1.getContext("2d", {willReadFrequently: true}); + } + context$1.fillStyle = $color; + context$1.fillRect(0, 0, 1, 1); + const d = context$1.getImageData(0, 0, 1, 1).data; + const color = `rgba(${d[0]}, ${d[1]}, ${d[2]}, ${(d[3] / 255).toFixed(2)})`; + return parseRGB(color); +} + +function throttle(callback) { + let pending = false; + let frameId = null; + let lastArgs; + const throttled = (...args) => { + lastArgs = args; + if (frameId) { + pending = true; + } else { + callback(...lastArgs); + frameId = requestAnimationFrame(() => { + frameId = null; + if (pending) { + callback(...lastArgs); + pending = false; + } + }); + } + }; + const cancel = () => { + cancelAnimationFrame(frameId); + pending = false; + frameId = null; + }; + return Object.assign(throttled, {cancel}); +} +function createAsyncTasksQueue() { + const tasks = []; + let frameId = null; + function runTasks() { + let task; + while ((task = tasks.shift())) { + task(); + } + frameId = null; + } + function add(task) { + tasks.push(task); + if (!frameId) { + frameId = requestAnimationFrame(runTasks); + } + } + function cancel() { + tasks.splice(0); + cancelAnimationFrame(frameId); + frameId = null; + } + return {add, cancel}; +} + +function hexify(number) { + return (number < 16 ? "0" : "") + number.toString(16); +} +function generateUID() { + if ("randomUUID" in crypto) { + const uuid = crypto.randomUUID(); + return ( + uuid.substring(0, 8) + + uuid.substring(9, 13) + + uuid.substring(14, 18) + + uuid.substring(19, 23) + + uuid.substring(24) + ); + } + if ("getRandomValues" in crypto) { + return Array.from(crypto.getRandomValues(new Uint8Array(16))) + .map((x) => hexify(x)) + .join(""); + } + return Math.floor(Math.random() * 2 ** 55).toString(36); +} + +let documentVisibilityListener = null; +let documentIsVisible_ = !document.hidden; +const listenerOptions = { + capture: true, + passive: true +}; +function watchForDocumentVisibility() { + document.addEventListener( + "visibilitychange", + documentVisibilityListener, + listenerOptions + ); + window.addEventListener( + "pageshow", + documentVisibilityListener, + listenerOptions + ); + window.addEventListener( + "focus", + documentVisibilityListener, + listenerOptions + ); +} +function stopWatchingForDocumentVisibility() { + document.removeEventListener( + "visibilitychange", + documentVisibilityListener, + listenerOptions + ); + window.removeEventListener( + "pageshow", + documentVisibilityListener, + listenerOptions + ); + window.removeEventListener( + "focus", + documentVisibilityListener, + listenerOptions + ); +} +function setDocumentVisibilityListener(callback) { + const alreadyWatching = Boolean(documentVisibilityListener); + documentVisibilityListener = () => { + if (!document.hidden) { + removeDocumentVisibilityListener(); + callback(); + documentIsVisible_ = true; + } + }; + if (!alreadyWatching) { + watchForDocumentVisibility(); + } +} +function removeDocumentVisibilityListener() { + stopWatchingForDocumentVisibility(); + documentVisibilityListener = null; +} +function documentIsVisible() { + return documentIsVisible_; +} + +function getDuration(time) { + let duration = 0; + if (time.seconds) { + duration += time.seconds * 1000; + } + if (time.minutes) { + duration += time.minutes * 60 * 1000; + } + if (time.hours) { + duration += time.hours * 60 * 60 * 1000; + } + if (time.days) { + duration += time.days * 24 * 60 * 60 * 1000; + } + return duration; +} + +function logInfo(...args) {} +function logWarn(...args) {} + +function removeNode(node) { + node && node.parentNode && node.parentNode.removeChild(node); +} +function watchForNodePosition(node, mode, onRestore = Function.prototype) { + const MAX_ATTEMPTS_COUNT = 10; + const RETRY_TIMEOUT = getDuration({seconds: 2}); + const ATTEMPTS_INTERVAL = getDuration({seconds: 10}); + let prevSibling = node.previousSibling; + let parent = node.parentNode; + if (!parent) { + throw new Error( + "Unable to watch for node position: parent element not found" + ); + } + if (mode === "prev-sibling" && !prevSibling) { + throw new Error( + "Unable to watch for node position: there is no previous sibling" + ); + } + let attempts = 0; + let start = null; + let timeoutId = null; + const restore = throttle(() => { + if (timeoutId) { + return; + } + attempts++; + const now = Date.now(); + if (start == null) { + start = now; + } else if (attempts >= MAX_ATTEMPTS_COUNT) { + if (now - start < ATTEMPTS_INTERVAL) { + logWarn( + `Node position watcher paused: retry in ${RETRY_TIMEOUT}ms`, + node, + prevSibling + ); + timeoutId = setTimeout(() => { + start = null; + attempts = 0; + timeoutId = null; + restore(); + }, RETRY_TIMEOUT); + return; + } + start = now; + attempts = 1; + } + if (mode === "head") { + if (prevSibling && prevSibling.parentNode !== parent) { + logWarn( + "Sibling moved, moving node to the head end", + node, + prevSibling, + parent + ); + prevSibling = document.head.lastChild; + } + } + if (mode === "prev-sibling") { + if (prevSibling.parentNode == null) { + logWarn( + "Unable to restore node position: sibling was removed", + node, + prevSibling, + parent + ); + stop(); + return; + } + if (prevSibling.parentNode !== parent) { + logWarn( + "Style was moved to another parent", + node, + prevSibling, + parent + ); + updateParent(prevSibling.parentNode); + } + } + if (mode === "head" && !parent.isConnected) { + parent = document.head; + } + logWarn("Restoring node position", node, prevSibling, parent); + parent.insertBefore( + node, + prevSibling && prevSibling.isConnected + ? prevSibling.nextSibling + : parent.firstChild + ); + observer.takeRecords(); + onRestore && onRestore(); + }); + const observer = new MutationObserver(() => { + if ( + (mode === "head" && + (node.parentNode !== parent || !node.parentNode.isConnected)) || + (mode === "prev-sibling" && node.previousSibling !== prevSibling) + ) { + restore(); + } + }); + const run = () => { + observer.observe(parent, {childList: true}); + }; + const stop = () => { + clearTimeout(timeoutId); + observer.disconnect(); + restore.cancel(); + }; + const skip = () => { + observer.takeRecords(); + }; + const updateParent = (parentNode) => { + parent = parentNode; + stop(); + run(); + }; + run(); + return {run, stop, skip}; +} +function iterateShadowHosts(root, iterator) { + if (root == null) { + return; + } + const walker = document.createTreeWalker(root, NodeFilter.SHOW_ELEMENT, { + acceptNode(node) { + return node.shadowRoot == null + ? NodeFilter.FILTER_SKIP + : NodeFilter.FILTER_ACCEPT; + } + }); + for ( + let node = root.shadowRoot ? walker.currentNode : walker.nextNode(); + node != null; + node = walker.nextNode() + ) { + if (node.classList.contains("surfingkeys_hints_host")) { + continue; + } + iterator(node); + iterateShadowHosts(node.shadowRoot, iterator); + } +} +let isDOMReady = () => { + return ( + document.readyState === "complete" || + document.readyState === "interactive" + ); +}; +function setIsDOMReady(newFunc) { + isDOMReady = newFunc; +} +const readyStateListeners = new Set(); +function addDOMReadyListener(listener) { + isDOMReady() ? listener() : readyStateListeners.add(listener); +} +function removeDOMReadyListener(listener) { + readyStateListeners.delete(listener); +} +function isReadyStateComplete() { + return document.readyState === "complete"; +} +const readyStateCompleteListeners = new Set(); +function addReadyStateCompleteListener(listener) { + isReadyStateComplete() + ? listener() + : readyStateCompleteListeners.add(listener); +} +function cleanReadyStateCompleteListeners() { + readyStateCompleteListeners.clear(); +} +if (!isDOMReady()) { + const onReadyStateChange = () => { + if (isDOMReady()) { + readyStateListeners.forEach((listener) => listener()); + readyStateListeners.clear(); + if (isReadyStateComplete()) { + document.removeEventListener( + "readystatechange", + onReadyStateChange + ); + readyStateCompleteListeners.forEach((listener) => listener()); + readyStateCompleteListeners.clear(); + } + } + }; + document.addEventListener("readystatechange", onReadyStateChange); +} +const HUGE_MUTATIONS_COUNT = 1000; +function isHugeMutation(mutations) { + if (mutations.length > HUGE_MUTATIONS_COUNT) { + return true; + } + let addedNodesCount = 0; + for (let i = 0; i < mutations.length; i++) { + addedNodesCount += mutations[i].addedNodes.length; + if (addedNodesCount > HUGE_MUTATIONS_COUNT) { + return true; + } + } + return false; +} +function getElementsTreeOperations(mutations) { + const additions = new Set(); + const deletions = new Set(); + const moves = new Set(); + mutations.forEach((m) => { + forEach(m.addedNodes, (n) => { + if (n instanceof Element && n.isConnected) { + additions.add(n); + } + }); + forEach(m.removedNodes, (n) => { + if (n instanceof Element) { + if (n.isConnected) { + moves.add(n); + additions.delete(n); + } else { + deletions.add(n); + } + } + }); + }); + const duplicateAdditions = []; + const duplicateDeletions = []; + additions.forEach((node) => { + if (additions.has(node.parentElement)) { + duplicateAdditions.push(node); + } + }); + deletions.forEach((node) => { + if (deletions.has(node.parentElement)) { + duplicateDeletions.push(node); + } + }); + duplicateAdditions.forEach((node) => additions.delete(node)); + duplicateDeletions.forEach((node) => deletions.delete(node)); + return {additions, moves, deletions}; +} +const optimizedTreeObservers = new Map(); +const optimizedTreeCallbacks = new WeakMap(); +function createOptimizedTreeObserver(root, callbacks) { + let observer; + let observerCallbacks; + let domReadyListener; + if (optimizedTreeObservers.has(root)) { + observer = optimizedTreeObservers.get(root); + observerCallbacks = optimizedTreeCallbacks.get(observer); + } else { + let hadHugeMutationsBefore = false; + let subscribedForReadyState = false; + observer = new MutationObserver((mutations) => { + if (isHugeMutation(mutations)) { + if (!hadHugeMutationsBefore || isDOMReady()) { + observerCallbacks.forEach(({onHugeMutations}) => + onHugeMutations(root) + ); + } else if (!subscribedForReadyState) { + domReadyListener = () => + observerCallbacks.forEach(({onHugeMutations}) => + onHugeMutations(root) + ); + addDOMReadyListener(domReadyListener); + subscribedForReadyState = true; + } + hadHugeMutationsBefore = true; + } else { + const elementsOperations = getElementsTreeOperations(mutations); + observerCallbacks.forEach(({onMinorMutations}) => + onMinorMutations(root, elementsOperations) + ); + } + }); + observer.observe(root, {childList: true, subtree: true}); + optimizedTreeObservers.set(root, observer); + observerCallbacks = new Set(); + optimizedTreeCallbacks.set(observer, observerCallbacks); + } + observerCallbacks.add(callbacks); + return { + disconnect() { + observerCallbacks.delete(callbacks); + if (domReadyListener) { + removeDOMReadyListener(domReadyListener); + } + if (observerCallbacks.size === 0) { + observer.disconnect(); + optimizedTreeCallbacks.delete(observer); + optimizedTreeObservers.delete(root); + } + } + }; +} + +function iterateCSSRules( + rules, + iterate, + onImportError, + importedSheets = new Set() +) { + forEach(rules, (rule) => { + if (isStyleRule(rule)) { + iterate(rule); + if (rule.cssRules?.length > 0) { + iterateCSSRules( + rule.cssRules, + iterate, + onImportError, + importedSheets + ); + } + } else if (isImportRule(rule)) { + try { + const importedSheet = rule.styleSheet; + if (!importedSheets.has(importedSheet)) { + importedSheets.add(importedSheet); + iterateCSSRules( + importedSheet.cssRules, + iterate, + onImportError, + importedSheets + ); + } + } catch (err) { + onImportError?.(); + } + } else if (isMediaRule(rule)) { + const media = Array.from(rule.media); + const isScreenOrAllOrQuery = media.some( + (m) => + m.startsWith("screen") || + m.startsWith("all") || + m.startsWith("(") + ); + const isNotScreen = + !isScreenOrAllOrQuery && + media.some((m) => ignoredMedia.some((i) => m.startsWith(i))); + if (isScreenOrAllOrQuery || !isNotScreen) { + iterateCSSRules( + rule.cssRules, + iterate, + onImportError, + importedSheets + ); + } + } else if (isSupportsRule(rule)) { + if (CSS.supports(rule.conditionText)) { + iterateCSSRules( + rule.cssRules, + iterate, + onImportError, + importedSheets + ); + } + } else if (isLayerRule(rule)) { + iterateCSSRules( + rule.cssRules, + iterate, + onImportError, + importedSheets + ); + } else if (isContainerRule(rule)) { + iterateCSSRules( + rule.cssRules, + iterate, + onImportError, + importedSheets + ); + } else { + logWarn(`CSSRule type not supported`, rule); + } + }); +} +const ignoredMedia = [ + "aural", + "braille", + "embossed", + "handheld", + "print", + "projection", + "speech", + "tty", + "tv" +]; +const shorthandVarDependantProperties = [ + "background", + "border", + "border-color", + "border-bottom", + "border-left", + "border-right", + "border-top", + "outline", + "outline-color" +]; +const shorthandVarDepPropRegexps = isSafari + ? shorthandVarDependantProperties.map((prop) => { + const regexp = new RegExp(`${prop}:\\s*(.*?)\\s*;`); + return [prop, regexp]; + }) + : null; +function iterateCSSDeclarations(style, iterate) { + const cssText = style.cssText; + if (cssText.includes("var(")) { + if (isSafari) { + shorthandVarDepPropRegexps.forEach(([prop, regexp]) => { + const match = cssText.match(regexp); + if (match && match[1]) { + const val = match[1].trim(); + iterate(prop, val); + } + }); + } else { + shorthandVarDependantProperties.forEach((prop) => { + const val = style.getPropertyValue(prop); + if (val && val.includes("var(")) { + iterate(prop, val); + } + }); + } + } + if ( + (cssText.includes("background-color: ;") || + cssText.includes("background-image: ;")) && + !style.getPropertyValue("background") + ) { + handleEmptyShorthand("background", style, iterate); + } + if ( + cssText.includes("border-") && + cssText.includes("-color: ;") && + !style.getPropertyValue("border") + ) { + handleEmptyShorthand("border", style, iterate); + } + forEach(style, (property) => { + const value = style.getPropertyValue(property).trim(); + if (!value) { + return; + } + iterate(property, value); + }); +} +function handleEmptyShorthand(shorthand, style, iterate) { + const parentRule = style.parentRule; + if (isStyleRule(parentRule)) { + const sourceCSSText = + parentRule.parentStyleSheet?.ownerNode?.textContent; + if (sourceCSSText) { + let escapedSelector = escapeRegExpSpecialChars( + parentRule.selectorText + ); + escapedSelector = escapedSelector.replaceAll(/\s+/g, "\\s*"); + escapedSelector = escapedSelector.replaceAll(/::/g, "::?"); + const regexp = new RegExp( + `${escapedSelector}\\s*{[^}]*${shorthand}:\\s*([^;}]+)` + ); + const match = sourceCSSText.match(regexp); + if (match) { + iterate(shorthand, match[1]); + } + } else if (shorthand === "background") { + iterate("background-color", "#ffffff"); + iterate("background-image", "none"); + } + } +} +const cssURLRegex = /url\((('.*?')|(".*?")|([^\)]*?))\)/g; +const cssImportRegex = + /@import\s*(url\()?(('.+?')|(".+?")|([^\)]*?))\)? ?(screen)?;?/gi; +function getCSSURLValue(cssURL) { + return cssURL + .trim() + .replace(/[\n\r\\]+/g, "") + .replace(/^url\((.*)\)$/, "$1") + .trim() + .replace(/^"(.*)"$/, "$1") + .replace(/^'(.*)'$/, "$1") + .replace(/(?:\\(.))/g, "$1"); +} +function getCSSBaseBath(url) { + const cssURL = parseURL(url); + return `${cssURL.origin}${cssURL.pathname.replace(/\?.*$/, "").replace(/(\/)([^\/]+)$/i, "$1")}`; +} +function replaceCSSRelativeURLsWithAbsolute($css, cssBasePath) { + return $css.replace(cssURLRegex, (match) => { + try { + const url = getCSSURLValue(match); + const absoluteURL = getAbsoluteURL(cssBasePath, url); + const escapedURL = absoluteURL.replaceAll("'", "\\'"); + return `url('${escapedURL}')`; + } catch (err) { + logWarn( + "Not able to replace relative URL with Absolute URL, skipping" + ); + return match; + } + }); +} +const fontFaceRegex = /@font-face\s*{[^}]*}/g; +function replaceCSSFontFace($css) { + return $css.replace(fontFaceRegex, ""); +} +const styleRules = new WeakSet(); +const importRules = new WeakSet(); +const mediaRules = new WeakSet(); +const supportsRules = new WeakSet(); +const layerRules = new WeakSet(); +const containerRules = new WeakSet(); +function isStyleRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return true; + } + if (rule.selectorText) { + styleRules.add(rule); + return true; + } + return false; +} +function isImportRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return false; + } + if (importRules.has(rule)) { + return true; + } + if (rule.href) { + importRules.add(rule); + return true; + } + return false; +} +function isMediaRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return false; + } + if (mediaRules.has(rule)) { + return true; + } + if (rule.media) { + mediaRules.add(rule); + return true; + } + return false; +} +function isSupportsRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return false; + } + if (supportsRules.has(rule)) { + return true; + } + if (rule instanceof CSSSupportsRule) { + supportsRules.add(rule); + return true; + } + return false; +} +function isLayerRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return false; + } + if (layerRules.has(rule)) { + return true; + } + if (isLayerRuleSupported && rule instanceof CSSLayerBlockRule) { + layerRules.add(rule); + return true; + } + return false; +} +function isContainerRule(rule) { + if (!rule) { + return false; + } + if (styleRules.has(rule)) { + return false; + } + if (containerRules.has(rule)) { + return true; + } + if (isContainerRuleSupported && rule instanceof CSSContainerRule) { + containerRules.add(rule); + return true; + } + return false; +} + +const sheetsScopes = new WeakMap(); +function defineSheetScope(sheet, node) { + sheetsScopes.set(sheet, node); +} +function getSheetScope(sheet) { + if (!sheet.ownerNode) { + return null; + } + if (sheetsScopes.has(sheet)) { + return sheetsScopes.get(sheet); + } + let node = sheet.ownerNode; + while (node) { + if (node instanceof ShadowRoot || node instanceof Document) { + defineSheetScope(sheet, node); + return node; + } + node = node.parentNode; + } + return null; +} + +let variablesSheet; +const registeredColors = new Map(); +function registerVariablesSheet(sheet) { + variablesSheet = sheet; + const types = ["background", "text", "border"]; + registeredColors.forEach((registered) => { + types.forEach((type) => { + if (registered[type]) { + const {variable, value} = registered[type]; + variablesSheet?.cssRules[0]?.style.setProperty(variable, value); + } + }); + }); +} +function releaseVariablesSheet() { + variablesSheet = null; + clearColorPalette(); +} +function getRegisteredVariableValue(type, registered) { + return `var(${registered[type].variable}, ${registered[type].value})`; +} +function getRegisteredColor(type, parsed) { + const hex = rgbToHexString(parsed); + const registered = registeredColors.get(hex); + if (registered?.[type]) { + return getRegisteredVariableValue(type, registered); + } + return null; +} +function registerColor(type, parsed, value) { + const hex = rgbToHexString(parsed); + let registered; + if (registeredColors.has(hex)) { + registered = registeredColors.get(hex); + } else { + const parsed = parseColorWithCache(hex); + registered = {parsed}; + registeredColors.set(hex, registered); + } + const variable = `--darkreader-${type}-${hex.replace("#", "")}`; + registered[type] = {variable, value}; + if (variablesSheet?.cssRules[0]?.style) { + (variablesSheet?.cssRules[0]).style.setProperty(variable, value); + } + return getRegisteredVariableValue(type, registered); +} +function getColorPalette() { + const background = []; + const border = []; + const text = []; + registeredColors.forEach((registered) => { + if (registered.background) { + background.push(registered.parsed); + } + if (registered.border) { + border.push(registered.parsed); + } + if (registered.text) { + text.push(registered.parsed); + } + }); + return {background, border, text}; +} +function clearColorPalette() { + registeredColors.clear(); +} + +function getBgPole(theme) { + const isDarkScheme = theme.mode === 1; + const prop = isDarkScheme + ? "darkSchemeBackgroundColor" + : "lightSchemeBackgroundColor"; + return theme[prop]; +} +function getFgPole(theme) { + const isDarkScheme = theme.mode === 1; + const prop = isDarkScheme ? "darkSchemeTextColor" : "lightSchemeTextColor"; + return theme[prop]; +} +const colorModificationCache = new Map(); +function clearColorModificationCache() { + colorModificationCache.clear(); +} +const rgbCacheKeys = ["r", "g", "b", "a"]; +const themeCacheKeys = [ + "mode", + "brightness", + "contrast", + "grayscale", + "sepia", + "darkSchemeBackgroundColor", + "darkSchemeTextColor", + "lightSchemeBackgroundColor", + "lightSchemeTextColor" +]; +function getCacheId(rgb, theme, poleA, poleB) { + let resultId = ""; + rgbCacheKeys.forEach((key) => { + resultId += `${rgb[key]};`; + }); + themeCacheKeys.forEach((key) => { + resultId += `${theme[key]};`; + }); + resultId += `${poleA};${poleB}`; + return resultId; +} +function modifyColorWithCache( + rgb, + theme, + modifyHSL, + poleColor, + anotherPoleColor +) { + let fnCache; + if (colorModificationCache.has(modifyHSL)) { + fnCache = colorModificationCache.get(modifyHSL); + } else { + fnCache = new Map(); + colorModificationCache.set(modifyHSL, fnCache); + } + const id = getCacheId(rgb, theme, poleColor, anotherPoleColor); + if (fnCache.has(id)) { + return fnCache.get(id); + } + const hsl = rgbToHSL(rgb); + const pole = poleColor == null ? null : parseToHSLWithCache(poleColor); + const anotherPole = + anotherPoleColor == null ? null : parseToHSLWithCache(anotherPoleColor); + const modified = modifyHSL(hsl, pole, anotherPole); + const {r, g, b, a} = hslToRGB(modified); + const matrix = createFilterMatrix({...theme, mode: 0}); + const [rf, gf, bf] = applyColorMatrix([r, g, b], matrix); + const color = + a === 1 + ? rgbToHexString({r: rf, g: gf, b: bf}) + : rgbToString({r: rf, g: gf, b: bf, a}); + fnCache.set(id, color); + return color; +} +function modifyAndRegisterColor(type, rgb, theme, modifier) { + const registered = getRegisteredColor(type, rgb); + if (registered) { + return registered; + } + const value = modifier(rgb, theme); + return registerColor(type, rgb, value); +} +function modifyLightSchemeColor(rgb, theme) { + const poleBg = getBgPole(theme); + const poleFg = getFgPole(theme); + return modifyColorWithCache(rgb, theme, modifyLightModeHSL, poleFg, poleBg); +} +function modifyLightModeHSL({h, s, l, a}, poleFg, poleBg) { + const isDark = l < 0.5; + let isNeutral; + if (isDark) { + isNeutral = l < 0.2 || s < 0.12; + } else { + const isBlue = h > 200 && h < 280; + isNeutral = s < 0.24 || (l > 0.8 && isBlue); + } + let hx = h; + let sx = s; + if (isNeutral) { + if (isDark) { + hx = poleFg.h; + sx = poleFg.s; + } else { + hx = poleBg.h; + sx = poleBg.s; + } + } + const lx = scale(l, 0, 1, poleFg.l, poleBg.l); + return {h: hx, s: sx, l: lx, a}; +} +const MAX_BG_LIGHTNESS = 0.4; +function modifyBgHSL({h, s, l, a}, pole) { + const isDark = l < 0.5; + const isBlue = h > 200 && h < 280; + const isNeutral = s < 0.12 || (l > 0.8 && isBlue); + if (isDark) { + const lx = scale(l, 0, 0.5, 0, MAX_BG_LIGHTNESS); + if (isNeutral) { + const hx = pole.h; + const sx = pole.s; + return {h: hx, s: sx, l: lx, a}; + } + return {h, s, l: lx, a}; + } + let lx = scale(l, 0.5, 1, MAX_BG_LIGHTNESS, pole.l); + if (isNeutral) { + const hx = pole.h; + const sx = pole.s; + return {h: hx, s: sx, l: lx, a}; + } + let hx = h; + const isYellow = h > 60 && h < 180; + if (isYellow) { + const isCloserToGreen = h > 120; + if (isCloserToGreen) { + hx = scale(h, 120, 180, 135, 180); + } else { + hx = scale(h, 60, 120, 60, 105); + } + } + if (hx > 40 && hx < 80) { + lx *= 0.75; + } + return {h: hx, s, l: lx, a}; +} +function _modifyBackgroundColor(rgb, theme) { + if (theme.mode === 0) { + return modifyLightSchemeColor(rgb, theme); + } + const pole = getBgPole(theme); + return modifyColorWithCache(rgb, theme, modifyBgHSL, pole); +} +function modifyBackgroundColor(rgb, theme, shouldRegisterColorVariable = true) { + if (!shouldRegisterColorVariable) { + return _modifyBackgroundColor(rgb, theme); + } + return modifyAndRegisterColor( + "background", + rgb, + theme, + _modifyBackgroundColor + ); +} +const MIN_FG_LIGHTNESS = 0.55; +function modifyBlueFgHue(hue) { + return scale(hue, 205, 245, 205, 220); +} +function modifyFgHSL({h, s, l, a}, pole) { + const isLight = l > 0.5; + const isNeutral = l < 0.2 || s < 0.24; + const isBlue = !isNeutral && h > 205 && h < 245; + if (isLight) { + const lx = scale(l, 0.5, 1, MIN_FG_LIGHTNESS, pole.l); + if (isNeutral) { + const hx = pole.h; + const sx = pole.s; + return {h: hx, s: sx, l: lx, a}; + } + let hx = h; + if (isBlue) { + hx = modifyBlueFgHue(h); + } + return {h: hx, s, l: lx, a}; + } + if (isNeutral) { + const hx = pole.h; + const sx = pole.s; + const lx = scale(l, 0, 0.5, pole.l, MIN_FG_LIGHTNESS); + return {h: hx, s: sx, l: lx, a}; + } + let hx = h; + let lx; + if (isBlue) { + hx = modifyBlueFgHue(h); + lx = scale(l, 0, 0.5, pole.l, Math.min(1, MIN_FG_LIGHTNESS + 0.05)); + } else { + lx = scale(l, 0, 0.5, pole.l, MIN_FG_LIGHTNESS); + } + return {h: hx, s, l: lx, a}; +} +function _modifyForegroundColor(rgb, theme) { + if (theme.mode === 0) { + return modifyLightSchemeColor(rgb, theme); + } + const pole = getFgPole(theme); + return modifyColorWithCache(rgb, theme, modifyFgHSL, pole); +} +function modifyForegroundColor(rgb, theme, shouldRegisterColorVariable = true) { + if (!shouldRegisterColorVariable) { + return _modifyForegroundColor(rgb, theme); + } + return modifyAndRegisterColor("text", rgb, theme, _modifyForegroundColor); +} +function modifyBorderHSL({h, s, l, a}, poleFg, poleBg) { + const isDark = l < 0.5; + const isNeutral = l < 0.2 || s < 0.24; + let hx = h; + let sx = s; + if (isNeutral) { + if (isDark) { + hx = poleFg.h; + sx = poleFg.s; + } else { + hx = poleBg.h; + sx = poleBg.s; + } + } + const lx = scale(l, 0, 1, 0.5, 0.2); + return {h: hx, s: sx, l: lx, a}; +} +function _modifyBorderColor(rgb, theme) { + if (theme.mode === 0) { + return modifyLightSchemeColor(rgb, theme); + } + const poleFg = getFgPole(theme); + const poleBg = getBgPole(theme); + return modifyColorWithCache(rgb, theme, modifyBorderHSL, poleFg, poleBg); +} +function modifyBorderColor(rgb, theme, shouldRegisterColorVariable = true) { + if (!shouldRegisterColorVariable) { + return _modifyBorderColor(rgb, theme); + } + return modifyAndRegisterColor("border", rgb, theme, _modifyBorderColor); +} +function modifyShadowColor(rgb, theme) { + return modifyBackgroundColor(rgb, theme); +} +function modifyGradientColor(rgb, theme) { + return modifyBackgroundColor(rgb, theme); +} + +const gradientLength = "gradient".length; +const conicGradient = "conic-"; +const conicGradientLength = conicGradient.length; +const radialGradient = "radial-"; +const linearGradient = "linear-"; +function parseGradient(value) { + const result = []; + let index = 0; + let startIndex = conicGradient.length; + while ((index = value.indexOf("gradient", startIndex)) !== -1) { + let typeGradient; + [linearGradient, radialGradient, conicGradient].find((possibleType) => { + if (index - possibleType.length >= 0) { + const possibleGradient = value.substring( + index - possibleType.length, + index + ); + if (possibleGradient === possibleType) { + if ( + value.slice( + index - possibleType.length - 10, + index - possibleType.length - 1 + ) === "repeating" + ) { + typeGradient = `repeating-${possibleType}gradient`; + return true; + } + if ( + value.slice( + index - possibleType.length - 8, + index - possibleType.length - 1 + ) === "-webkit" + ) { + typeGradient = `-webkit-${possibleType}gradient`; + return true; + } + typeGradient = `${possibleType}gradient`; + return true; + } + } + }); + if (!typeGradient) { + break; + } + const {start, end} = getParenthesesRange(value, index + gradientLength); + const match = value.substring(start + 1, end - 1); + startIndex = end + 1 + conicGradientLength; + result.push({ + typeGradient, + match, + offset: typeGradient.length + 2, + index: index - typeGradient.length + gradientLength, + hasComma: true + }); + } + if (result.length) { + result[result.length - 1].hasComma = false; + } + return result; +} + +const STORAGE_KEY_IMAGE_DETAILS_LIST = "__darkreader__imageDetails_v2_list"; +const STORAGE_KEY_IMAGE_DETAILS_PREFIX = "__darkreader__imageDetails_v2_"; +const STORAGE_KEY_CSS_FETCH_PREFIX = "__darkreader__cssFetch_"; +let imageCacheTimeout = 0; +const imageDetailsCacheQueue = new Map(); +const cachedImageUrls = []; +function writeImageDetailsQueue() { + imageDetailsCacheQueue.forEach((details, url) => { + if (url && url.startsWith("https://")) { + try { + const json = JSON.stringify(details); + sessionStorage.setItem( + `${STORAGE_KEY_IMAGE_DETAILS_PREFIX}${url}`, + json + ); + cachedImageUrls.push(url); + } catch (err) {} + } + }); + imageDetailsCacheQueue.clear(); + sessionStorage.setItem( + STORAGE_KEY_IMAGE_DETAILS_LIST, + JSON.stringify(cachedImageUrls) + ); +} +function writeImageDetailsCache(url, imageDetails) { + if (!url || !url.startsWith("https://")) { + return; + } + imageDetailsCacheQueue.set(url, imageDetails); + clearTimeout(imageCacheTimeout); + imageCacheTimeout = setTimeout(writeImageDetailsQueue, 1000); +} +function readImageDetailsCache(targetMap) { + try { + const jsonList = sessionStorage.getItem(STORAGE_KEY_IMAGE_DETAILS_LIST); + if (!jsonList) { + return; + } + const list = JSON.parse(jsonList); + list.forEach((url) => { + const json = sessionStorage.getItem( + `${STORAGE_KEY_IMAGE_DETAILS_PREFIX}${url}` + ); + if (json) { + const details = JSON.parse(json); + targetMap.set(url, details); + } + }); + } catch (err) {} +} +function writeCSSFetchCache(url, cssText) { + const key = `${STORAGE_KEY_CSS_FETCH_PREFIX}${url}`; + try { + sessionStorage.setItem(key, cssText); + } catch (err) {} +} +function readCSSFetchCache(url) { + const key = `${STORAGE_KEY_CSS_FETCH_PREFIX}${url}`; + try { + return sessionStorage.getItem(key) ?? null; + } catch (err) {} + return null; +} + +function toSVGMatrix(matrix) { + return matrix + .slice(0, 4) + .map((m) => m.map((m) => m.toFixed(3)).join(" ")) + .join(" "); +} +function getSVGFilterMatrixValue(config) { + return toSVGMatrix(createFilterMatrix(config)); +} + +const MAX_FRAME_DURATION = 1000 / 60; +class AsyncQueue { + constructor() { + this.queue = []; + this.timerId = null; + } + addTask(task) { + this.queue.push(task); + this.scheduleFrame(); + } + stop() { + if (this.timerId !== null) { + cancelAnimationFrame(this.timerId); + this.timerId = null; + } + this.queue = []; + } + scheduleFrame() { + if (this.timerId) { + return; + } + this.timerId = requestAnimationFrame(() => { + this.timerId = null; + const start = Date.now(); + let cb; + while ((cb = this.queue.shift())) { + cb(); + if (Date.now() - start >= MAX_FRAME_DURATION) { + this.scheduleFrame(); + break; + } + } + }); + } +} + +const resolvers$1 = new Map(); +const rejectors = new Map(); +async function bgFetch(request) { + if (window.DarkReader?.Plugins?.fetch) { + return window.DarkReader.Plugins.fetch(request); + } + const parsedURL = new URL(request.url); + if (parsedURL.origin !== request.origin && shouldIgnoreCors(parsedURL)) { + throw new Error("Cross-origin limit reached"); + } + return new Promise((resolve, reject) => { + const id = generateUID(); + resolvers$1.set(id, resolve); + rejectors.set(id, reject); + chrome.runtime.sendMessage({ + type: MessageTypeCStoBG.FETCH, + data: request, + id + }); + }); +} +chrome.runtime.onMessage.addListener(({type, data, error, id}) => { + if (type === MessageTypeBGtoCS.FETCH_RESPONSE) { + const resolve = resolvers$1.get(id); + const reject = rejectors.get(id); + resolvers$1.delete(id); + rejectors.delete(id); + if (error) { + reject && + reject(typeof error === "string" ? new Error(error) : error); + } else { + resolve && resolve(data); + } + } +}); + +const imageManager = new AsyncQueue(); +async function getImageDetails(url) { + return new Promise(async (resolve, reject) => { + try { + let dataURL = url.startsWith("data:") ? url : await getDataURL(url); + const blob = + tryConvertDataURLToBlobSync(dataURL) ?? (await loadAsBlob(url)); + let image; + let useViewBox = false; + if (dataURL.startsWith("data:image/svg+xml")) { + const commaIndex = dataURL.indexOf(","); + if (commaIndex >= 0) { + let svgText = dataURL.slice(commaIndex + 1); + const encoding = dataURL.slice(0, commaIndex).split(";")[1]; + if (encoding === "base64") { + if (svgText.includes("%")) { + svgText = decodeURIComponent(svgText); + } + svgText = atob(svgText); + } else if (svgText.startsWith("%3c")) { + svgText = decodeURIComponent(svgText); + } + if (svgText.startsWith(""); + const svgOpening = svgText + .slice(0, closingIndex + 1) + .toLocaleLowerCase(); + if ( + svgOpening.includes("viewbox=") && + !svgOpening.includes("width=") && + !svgOpening.includes("height=") + ) { + useViewBox = true; + const viewboxIndex = svgOpening.indexOf("viewbox="); + const quote = svgOpening[viewboxIndex + 8]; + const viewboxCloseIndex = svgOpening.indexOf( + quote, + viewboxIndex + 9 + ); + const viewBox = svgOpening + .slice(viewboxIndex + 9, viewboxCloseIndex) + .split(" ") + .map((x) => parseFloat(x)); + if ( + viewBox.length === 4 && + !viewBox.some((x) => isNaN(x)) + ) { + const width = viewBox[2] - viewBox[0]; + const height = viewBox[3] - viewBox[1]; + dataURL = `data:image/svg+xml;base64,${btoa(` { + const analysis = analyzeImage(image); + resolve({ + src: url, + dataURL: analysis.isLarge ? "" : dataURL, + width: image.width, + height: image.height, + useViewBox, + ...analysis + }); + }); + } catch (error) { + reject(error); + } + }); +} +async function getDataURL(url) { + const parsedURL = new URL(url); + if (parsedURL.origin === location.origin) { + return await loadAsDataURL(url); + } + return await bgFetch({ + url, + responseType: "data-url", + origin: location.origin + }); +} +async function tryCreateImageBitmap(blob) { + try { + return await createImageBitmap(blob); + } catch (err) { + logWarn( + `Unable to create image bitmap for type ${blob.type}: ${String(err)}` + ); + return null; + } +} +const INCOMPLETE_DOC_LOADING_IMAGE_LIMIT = 256; +let loadingImagesCount = 0; +async function loadImage(url) { + return new Promise((resolve, reject) => { + const image = new Image(); + image.onload = () => resolve(image); + image.onerror = () => reject(`Unable to load image ${url}`); + if ( + ++loadingImagesCount <= INCOMPLETE_DOC_LOADING_IMAGE_LIMIT || + isReadyStateComplete() + ) { + image.src = url; + } else { + addReadyStateCompleteListener(() => (image.src = url)); + } + }); +} +const MAX_ANALYSIS_PIXELS_COUNT = 32 * 32; +let canvas; +let context; +function createCanvas() { + const maxWidth = MAX_ANALYSIS_PIXELS_COUNT; + const maxHeight = MAX_ANALYSIS_PIXELS_COUNT; + canvas = document.createElement("canvas"); + canvas.width = maxWidth; + canvas.height = maxHeight; + context = canvas.getContext("2d", {willReadFrequently: true}); + context.imageSmoothingEnabled = false; +} +function removeCanvas() { + canvas = null; + context = null; +} +const LARGE_IMAGE_PIXELS_COUNT = 512 * 512; +function analyzeImage(image) { + if (!canvas) { + createCanvas(); + } + let sw; + let sh; + if (image instanceof HTMLImageElement) { + sw = image.naturalWidth; + sh = image.naturalHeight; + } else { + sw = image.width; + sh = image.height; + } + if (sw === 0 || sh === 0) { + logWarn("Image is empty"); + return { + isDark: false, + isLight: false, + isTransparent: false, + isLarge: false, + averageColor: null + }; + } + const isLarge = sw * sh > LARGE_IMAGE_PIXELS_COUNT; + const sourcePixelsCount = sw * sh; + const k = Math.min( + 1, + Math.sqrt(MAX_ANALYSIS_PIXELS_COUNT / sourcePixelsCount) + ); + const width = Math.ceil(sw * k); + const height = Math.ceil(sh * k); + context.clearRect(0, 0, width, height); + context.drawImage(image, 0, 0, sw, sh, 0, 0, width, height); + const imageData = context.getImageData(0, 0, width, height); + const d = imageData.data; + const TRANSPARENT_ALPHA_THRESHOLD = 0.05; + const DARK_LIGHTNESS_THRESHOLD = 0.4; + const LIGHT_LIGHTNESS_THRESHOLD = 0.7; + let transparentPixelsCount = 0; + let darkPixelsCount = 0; + let lightPixelsCount = 0; + let minLightness = 1; + let maxLightness = 0; + let sumR = 0; + let sumG = 0; + let sumB = 0; + let sumA = 0; + let i, x, y; + let r, g, b, a; + let l; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + i = 4 * (y * width + x); + r = d[i + 0]; + g = d[i + 1]; + b = d[i + 2]; + a = d[i + 3]; + sumR += r; + sumG += g; + sumB += b; + sumA += a; + if (a / 255 < TRANSPARENT_ALPHA_THRESHOLD) { + transparentPixelsCount++; + } else { + l = getSRGBLightness(r, g, b); + if (l < DARK_LIGHTNESS_THRESHOLD) { + darkPixelsCount++; + } + if (l > LIGHT_LIGHTNESS_THRESHOLD) { + lightPixelsCount++; + } + if (l < minLightness) { + minLightness = l; + } + if (l > maxLightness) { + maxLightness = l; + } + } + } + } + const totalPixelsCount = width * height; + const opaquePixelsCount = totalPixelsCount - transparentPixelsCount; + const DARK_IMAGE_THRESHOLD = 0.7; + const LIGHT_IMAGE_THRESHOLD = 0.7; + const TRANSPARENT_IMAGE_THRESHOLD = 0.1; + const SOLID_LIGHTNESS_DIFF_THRESHOLD = 0.1; + const isSolid = + sumA === totalPixelsCount * 255 && + maxLightness - minLightness < SOLID_LIGHTNESS_DIFF_THRESHOLD; + const solidColor = isSolid + ? { + r: Math.round(sumR / opaquePixelsCount), + g: Math.round(sumG / opaquePixelsCount), + b: Math.round(sumB / opaquePixelsCount), + a: transparentPixelsCount / totalPixelsCount + } + : null; + return { + isDark: darkPixelsCount / opaquePixelsCount >= DARK_IMAGE_THRESHOLD, + isLight: lightPixelsCount / opaquePixelsCount >= LIGHT_IMAGE_THRESHOLD, + isTransparent: + transparentPixelsCount / totalPixelsCount >= + TRANSPARENT_IMAGE_THRESHOLD, + isLarge, + solidColor + }; +} +let isBlobURLSupported = null; +let canUseProxy = false; +let blobURLCheckRequested = false; +const blobURLCheckAwaiters = []; +document.addEventListener( + "__darkreader__inlineScriptsAllowed", + () => (canUseProxy = true), + {once: true} +); +async function requestBlobURLCheck() { + if (!canUseProxy) { + return; + } + if (blobURLCheckRequested) { + return await new Promise((resolve) => + blobURLCheckAwaiters.push(resolve) + ); + } + blobURLCheckRequested = true; + await new Promise((resolve) => { + document.addEventListener( + "__darkreader__blobURLCheckResponse", + (e) => { + isBlobURLSupported = e.detail.blobURLAllowed; + resolve(); + blobURLCheckAwaiters.forEach((r) => r()); + blobURLCheckAwaiters.splice(0); + }, + {once: true} + ); + document.dispatchEvent( + new CustomEvent("__darkreader__blobURLCheckRequest") + ); + }); +} +function isBlobURLCheckResultReady() { + return isBlobURLSupported != null || !canUseProxy; +} +function onCSPError(err) { + if (err.blockedURI === "blob") { + isBlobURLSupported = false; + document.removeEventListener("securitypolicyviolation", onCSPError); + } +} +document.addEventListener("securitypolicyviolation", onCSPError); +const filteredImageURLs = new Map(); +function getFilteredImageURL({dataURL, width, height, useViewBox, src}, theme) { + if (dataURL.startsWith("data:image/svg+xml")) { + dataURL = escapeXML(dataURL); + } + const matrix = getSVGFilterMatrixValue(theme); + const size = useViewBox + ? `viewBox="0 0 ${width} ${height}"` + : `width="${width}" height="${height}"`; + const svg = [ + ``, + "", + '', + ``, + "", + "", + ``, + "" + ].join(""); + if (!isBlobURLSupported) { + return `data:image/svg+xml;base64,${btoa(svg)}`; + } + const cached = filteredImageURLs.get(src); + if (cached && cached.matrix === matrix) { + return cached.url; + } + const bytes = new Uint8Array(svg.length); + for (let i = 0; i < svg.length; i++) { + bytes[i] = svg.charCodeAt(i); + } + const blob = new Blob([bytes], {type: "image/svg+xml"}); + const objectURL = URL.createObjectURL(blob); + if (cached) { + URL.revokeObjectURL(cached.url); + } + filteredImageURLs.set(src, {matrix, url: objectURL}); + return objectURL; +} +function getSolidColorImageURL({width, height, useViewBox}, color) { + const size = useViewBox + ? `viewBox="0 0 ${width} ${height}"` + : `width="${width}" height="${height}"`; + const svg = [ + ``, + ``, + "" + ].join(""); + return `data:image/svg+xml;base64,${btoa(svg)}`; +} +const xmlEscapeChars = { + "<": "<", + ">": ">", + "&": "&", + "'": "'", + '"': """ +}; +function escapeXML(str) { + return str.replace(/[<>&'"]/g, (c) => xmlEscapeChars[c] ?? c); +} +const dataURLBlobURLs = new Map(); +function tryConvertDataURLToBlobSync(dataURL) { + const colonIndex = dataURL.indexOf(":"); + const semicolonIndex = dataURL.indexOf(";", colonIndex + 1); + const commaIndex = dataURL.indexOf(",", semicolonIndex + 1); + const encoding = dataURL + .substring(semicolonIndex + 1, commaIndex) + .toLocaleLowerCase(); + const mediaType = dataURL.substring(colonIndex + 1, semicolonIndex); + if (encoding !== "base64" || !mediaType) { + return null; + } + let base64Content = dataURL.substring(commaIndex + 1); + if (base64Content.includes("%")) { + base64Content = decodeURIComponent(base64Content); + } + const characters = atob(base64Content); + const bytes = new Uint8Array(characters.length); + for (let i = 0; i < characters.length; i++) { + bytes[i] = characters.charCodeAt(i); + } + return new Blob([bytes], {type: mediaType}); +} +async function tryConvertDataURLToBlobURL(dataURL) { + if (!isBlobURLSupported) { + return null; + } + const hash = getHashCode(dataURL); + let blobURL = dataURLBlobURLs.get(hash); + if (blobURL) { + return blobURL; + } + let blob = tryConvertDataURLToBlobSync(dataURL); + if (!blob) { + const response = await fetch(dataURL); + blob = await response.blob(); + } + blobURL = URL.createObjectURL(blob); + dataURLBlobURLs.set(hash, blobURL); + return blobURL; +} +function cleanImageProcessingCache() { + imageManager && imageManager.stop(); + removeCanvas(); + filteredImageURLs.forEach(({url}) => URL.revokeObjectURL(url)); + filteredImageURLs.clear(); + dataURLBlobURLs.forEach((u) => URL.revokeObjectURL(u)); + dataURLBlobURLs.clear(); +} + +function getPriority(ruleStyle, property) { + return Boolean(ruleStyle && ruleStyle.getPropertyPriority(property)); +} +function canFilterImage(url) { + if (url.startsWith("data:")) { + return true; + } + try { + return new URL(url).origin === location.origin; + } catch { + return false; + } +} +const bgPropsToCopy = [ + "background-clip", + "background-position", + "background-repeat", + "background-size" +]; +function getModifiableCSSDeclaration( + property, + value, + rule, + variablesStore, + ignoreImageSelectors, + isCancelled +) { + let modifier = null; + if (property.startsWith("--")) { + modifier = getVariableModifier( + variablesStore, + property, + value, + rule, + ignoreImageSelectors, + isCancelled + ); + } else if (value.includes("var(")) { + modifier = getVariableDependantModifier( + variablesStore, + property, + value, + rule + ); + } else if (property === "color-scheme") { + modifier = getColorSchemeModifier(); + } else if (property === "scrollbar-color") { + modifier = getScrollbarColorModifier(value); + } else if ( + (property.includes("color") && + property !== "-webkit-print-color-adjust") || + property === "fill" || + property === "stroke" || + property === "stop-color" + ) { + if ( + property.startsWith("border") && + property !== "border-color" && + (value === "initial" || value === "currentcolor") + ) { + const borderSideProp = property.substring(0, property.length - 6); + const borderSideVal = rule.style.getPropertyValue(borderSideProp); + const borderStyleVal = rule.style.getPropertyValue("border-style"); + if ( + borderSideVal.startsWith("0px") || + borderSideVal === "none" || + borderStyleVal === "none" + ) { + property = borderSideProp; + modifier = borderSideVal; + } else { + modifier = value; + } + } else { + modifier = getColorModifier(property, value, rule); + } + } else if ( + property === "background-image" || + property === "list-style-image" + ) { + const selectorText = rule.selectorText; + const pushFilter = selectorText + ? (type) => pushFilterSelector(selectorText, type) + : null; + modifier = getBgImageModifier( + value, + rule, + ignoreImageSelectors, + isCancelled, + pushFilter + ); + } else if (property.includes("shadow")) { + modifier = getShadowModifier(value); + } else if (bgPropsToCopy.includes(property) && value !== "initial") { + modifier = value; + } + if (!modifier) { + return null; + } + return { + property, + value: modifier, + important: getPriority(rule.style, property), + sourceValue: value + }; +} +function joinSelectors(...selectors) { + return selectors.filter(Boolean).join(", "); +} +const hostsWithOddScrollbars = ["calendar.google.com"]; +function getModifiedUserAgentStyle(theme, isIFrame, styleSystemControls) { + const lines = []; + if (!isIFrame) { + lines.push("html {"); + lines.push( + ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)} !important;` + ); + lines.push("}"); + } + if (isCSSColorSchemePropSupported && theme.mode === 1) { + lines.push("html {"); + lines.push(` color-scheme: dark !important;`); + lines.push("}"); + lines.push("iframe {"); + lines.push(` color-scheme: dark !important;`); + lines.push("}"); + } + const bgSelectors = joinSelectors( + isIFrame ? "" : "html, body", + styleSystemControls ? "input, textarea, select, button, dialog" : "" + ); + if (bgSelectors) { + lines.push(`${bgSelectors} {`); + lines.push( + ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)};` + ); + lines.push("}"); + } + lines.push( + `${joinSelectors("html, body", styleSystemControls ? "input, textarea, select, button" : "")} {` + ); + lines.push( + ` border-color: ${modifyBorderColor({r: 76, g: 76, b: 76}, theme)};` + ); + lines.push( + ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)};` + ); + lines.push("}"); + lines.push("a {"); + lines.push( + ` color: ${modifyForegroundColor({r: 0, g: 64, b: 255}, theme)};` + ); + lines.push("}"); + lines.push("table {"); + lines.push( + ` border-color: ${modifyBorderColor({r: 128, g: 128, b: 128}, theme)};` + ); + lines.push("}"); + lines.push("mark {"); + lines.push( + ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)};` + ); + lines.push("}"); + lines.push("::placeholder {"); + lines.push( + ` color: ${modifyForegroundColor({r: 169, g: 169, b: 169}, theme)};` + ); + lines.push("}"); + lines.push("input:-webkit-autofill,"); + lines.push("textarea:-webkit-autofill,"); + lines.push("select:-webkit-autofill {"); + lines.push( + ` background-color: ${modifyBackgroundColor({r: 250, g: 255, b: 189}, theme)} !important;` + ); + lines.push( + ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)} !important;` + ); + lines.push("}"); + if ( + theme.scrollbarColor && + !hostsWithOddScrollbars.includes(location.hostname) + ) { + lines.push(getModifiedScrollbarStyle(theme)); + } + if (theme.selectionColor) { + lines.push(getModifiedSelectionStyle(theme)); + } + if (isLayerRuleSupported) { + lines.unshift("@layer {"); + lines.push("}"); + } + return lines.join("\n"); +} +function getSelectionColor(theme) { + let backgroundColorSelection; + let foregroundColorSelection; + if (theme.selectionColor === "auto") { + backgroundColorSelection = modifyBackgroundColor( + {r: 0, g: 96, b: 212}, + {...theme, grayscale: 0} + ); + foregroundColorSelection = modifyForegroundColor( + {r: 255, g: 255, b: 255}, + {...theme, grayscale: 0} + ); + } else { + const rgb = parseColorWithCache(theme.selectionColor); + const hsl = rgbToHSL(rgb); + backgroundColorSelection = theme.selectionColor; + if (hsl.l < 0.5) { + foregroundColorSelection = "#FFF"; + } else { + foregroundColorSelection = "#000"; + } + } + return {backgroundColorSelection, foregroundColorSelection}; +} +function getModifiedSelectionStyle(theme) { + const lines = []; + const modifiedSelectionColor = getSelectionColor(theme); + const backgroundColorSelection = + modifiedSelectionColor.backgroundColorSelection; + const foregroundColorSelection = + modifiedSelectionColor.foregroundColorSelection; + ["::selection", "::-moz-selection"].forEach((selection) => { + lines.push(`${selection} {`); + lines.push( + ` background-color: ${backgroundColorSelection} !important;` + ); + lines.push(` color: ${foregroundColorSelection} !important;`); + lines.push("}"); + }); + return lines.join("\n"); +} +function getModifiedScrollbarStyle(theme) { + let colorTrack; + let colorThumb; + if (theme.scrollbarColor === "auto") { + colorTrack = modifyBackgroundColor({r: 241, g: 241, b: 241}, theme); + colorThumb = modifyBackgroundColor({r: 176, g: 176, b: 176}, theme); + } else { + const rgb = parseColorWithCache(theme.scrollbarColor); + const hsl = rgbToHSL(rgb); + const darken = (darker) => ({...hsl, l: clamp(hsl.l - darker, 0, 1)}); + colorTrack = hslToString(darken(0.4)); + colorThumb = hslToString(hsl); + } + return [ + `* {`, + ` scrollbar-color: ${colorThumb} ${colorTrack};`, + `}` + ].join("\n"); +} +function getModifiedFallbackStyle(theme, {strict}) { + const factory = defaultFallbackFactory; + return factory(theme, {strict}); +} +function defaultFallbackFactory(theme, {strict}) { + const lines = []; + lines.push( + `html, body, ${strict ? "body :not(iframe)" : "body > :not(iframe)"} {` + ); + lines.push( + ` background-color: ${modifyBackgroundColor({r: 255, g: 255, b: 255}, theme)} !important;` + ); + lines.push( + ` border-color: ${modifyBorderColor({r: 64, g: 64, b: 64}, theme)} !important;` + ); + lines.push( + ` color: ${modifyForegroundColor({r: 0, g: 0, b: 0}, theme)} !important;` + ); + lines.push("}"); + lines.push(`div[style*="background-color: rgb(135, 135, 135)"] {`); + lines.push(` background-color: #878787 !important;`); + lines.push("}"); + return lines.join("\n"); +} +let addFilterSelector$1 = null; +function setFilterSelectorHandler(fn) { + addFilterSelector$1 = fn; +} +function pushFilterSelector(selector, type) { + if (selector && addFilterSelector$1) { + addFilterSelector$1(selector, type); + } +} +const filterCompatibleProps = new Set([ + "background", + "background-image", + "list-style-image" +]); +function isFilterCompatibleProp(property) { + return filterCompatibleProps.has(property); +} +const unparsableColors = new Set([ + "inherit", + "transparent", + "initial", + "currentcolor", + "none", + "unset", + "auto" +]); +function getColorModifier(prop, value, rule) { + if ( + unparsableColors.has(value.toLowerCase()) && + !(prop === "color" && value === "initial") + ) { + return value; + } + let rgb = null; + if (prop === "color" && value === "initial") { + rgb = {r: 0, g: 0, b: 0, a: 1}; + } else { + rgb = parseColorWithCache(value); + } + if (!rgb) { + logWarn("Couldn't parse color", value); + return null; + } + if (prop.includes("background")) { + const maskImageValue = rule.style.maskImage ?? rule.style.mask; + if ( + maskImageValue && + !maskImageValue.startsWith("none") && + !maskImageValue.startsWith("linear-gradient") + ) { + return (theme) => modifyForegroundColor(rgb, theme); + } + return (theme) => modifyBackgroundColor(rgb, theme); + } + if (prop.includes("border") || prop.includes("outline")) { + return (theme) => modifyBorderColor(rgb, theme); + } + return (theme) => modifyForegroundColor(rgb, theme); +} +const imageDetailsCache = new Map(); +const awaitingForImageLoading = new Map(); +let didTryLoadCache = false; +function shouldIgnoreImage(selectorText, selectors) { + if (!selectorText || selectors.length === 0) { + return false; + } + if (selectors.some((s) => s === "*")) { + return true; + } + const ruleSelectors = selectorText.split(/,\s*/g); + for (let i = 0; i < selectors.length; i++) { + const ignoredSelector = selectors[i]; + if (ignoredSelector.startsWith("^")) { + const beginning = ignoredSelector.slice(1); + if (ruleSelectors.some((s) => s.startsWith(beginning))) { + return true; + } + } else if (ignoredSelector.endsWith("$")) { + const ending = ignoredSelector.slice(0, ignoredSelector.length - 1); + if (ruleSelectors.some((s) => s.endsWith(ending))) { + return true; + } + } else if (ruleSelectors.some((s) => s === ignoredSelector)) { + return true; + } + } + return false; +} +const imageSelectorQueue = new Map(); +const imageSelectorNodeQueue = new Set(); +let imageSelectorQueueFrameId = null; +let classObserver = null; +function checkImageSelectors(node) { + for (const [selector, callbacks] of imageSelectorQueue) { + if ( + node.querySelector(selector) || + (node instanceof Element && node.matches(selector)) + ) { + imageSelectorQueue.delete(selector); + callbacks.forEach((cb) => cb()); + } + } + if (imageSelectorQueue.size === 0) { + classObserver?.disconnect(); + classObserver = null; + return; + } + if (!classObserver) { + classObserver = new MutationObserver((mutations) => { + mutations.forEach((mutation) => { + imageSelectorNodeQueue.add(mutation.target); + if (!imageSelectorQueueFrameId) { + imageSelectorQueueFrameId = requestAnimationFrame(() => { + imageSelectorNodeQueue.forEach((element) => { + checkImageSelectors(element); + }); + imageSelectorNodeQueue.clear(); + imageSelectorQueueFrameId = null; + }); + } + }); + }); + classObserver.observe(document.documentElement, { + attributes: true, + attributeFilter: ["class"], + subtree: true + }); + } +} +function getBgImageModifier( + value, + rule, + ignoreImageSelectors, + isCancelled, + pushFilter = null +) { + try { + if (shouldIgnoreImage(rule.selectorText, ignoreImageSelectors)) { + return value; + } + const gradients = parseGradient(value); + const urls = getMatches(cssURLRegex, value); + if (urls.length === 0 && gradients.length === 0) { + return value; + } + const getIndices = (matches) => { + let index = 0; + return matches.map((match) => { + const valueIndex = value.indexOf(match, index); + index = valueIndex + match.length; + return {match, index: valueIndex}; + }); + }; + const matches = gradients + .map((i) => ({type: "gradient", ...i})) + .concat( + getIndices(urls).map((i) => ({type: "url", offset: 0, ...i})) + ) + .sort((a, b) => (a.index > b.index ? 1 : -1)); + const getGradientModifier = (gradient) => { + const {typeGradient, match, hasComma} = gradient; + const partsRegex = + /([^\(\),]+(\([^\(\)]*(\([^\(\)]*\)*[^\(\)]*)?\))?([^\(\), ]|( (?!calc)))*),?/g; + const colorStopRegex = + /^(from|color-stop|to)\(([^\(\)]*?,\s*)?(.*?)\)$/; + const parts = getMatches(partsRegex, match, 1).map((part) => { + part = part.trim(); + let rgb = parseColorWithCache(part); + if (rgb) { + return (theme) => modifyGradientColor(rgb, theme); + } + const space = part.lastIndexOf(" "); + rgb = parseColorWithCache(part.substring(0, space)); + if (rgb) { + return (theme) => + `${modifyGradientColor(rgb, theme)} ${part.substring(space + 1)}`; + } + const colorStopMatch = part.match(colorStopRegex); + if (colorStopMatch) { + rgb = parseColorWithCache(colorStopMatch[3]); + if (rgb) { + return (theme) => + `${colorStopMatch[1]}(${colorStopMatch[2] ? `${colorStopMatch[2]}, ` : ""}${modifyGradientColor(rgb, theme)})`; + } + } + return () => part; + }); + return (theme) => { + return `${typeGradient}(${parts.map((modify) => modify(theme)).join(", ")})${hasComma ? ", " : ""}`; + }; + }; + const getURLModifier = (urlValue) => { + if (!didTryLoadCache) { + didTryLoadCache = true; + readImageDetailsCache(imageDetailsCache); + } + let url = getCSSURLValue(urlValue); + const isURLEmpty = url.length === 0; + const {parentStyleSheet} = rule; + const ownerNode = parentStyleSheet?.ownerNode; + const scope = + (parentStyleSheet && getSheetScope(parentStyleSheet)) ?? + document; + const baseURL = + parentStyleSheet && parentStyleSheet.href + ? getCSSBaseBath(parentStyleSheet.href) + : ownerNode?.baseURI || location.origin; + url = getAbsoluteURL(baseURL, url); + return async (theme) => { + if (isURLEmpty) { + return "url('')"; + } + let selector = rule.selectorText; + if (selector) { + if (selector.includes("::before")) { + selector = selector.replaceAll("::before", ""); + } + if (selector.includes("::after")) { + selector = selector.replaceAll("::after", ""); + } + if (!scope.querySelector(selector)) { + await new Promise((resolve) => { + if (imageSelectorQueue.has(selector)) { + imageSelectorQueue.get(selector).push(resolve); + } else { + imageSelectorQueue.set(selector, [resolve]); + } + }); + } + } + let imageDetails = null; + if (imageDetailsCache.has(url)) { + imageDetails = imageDetailsCache.get(url); + } else { + try { + if (!isBlobURLCheckResultReady()) { + await requestBlobURLCheck(); + } + if (awaitingForImageLoading.has(url)) { + const awaiters = awaitingForImageLoading.get(url); + imageDetails = await new Promise((resolve) => + awaiters.push(resolve) + ); + if (!imageDetails) { + return null; + } + } else { + awaitingForImageLoading.set(url, []); + imageDetails = await getImageDetails(url); + imageDetailsCache.set(url, imageDetails); + if (!url.startsWith("data:")) { + const parsedURL = new URL(url); + if (parsedURL.origin === location.origin) { + writeImageDetailsCache(url, imageDetails); + } + } + awaitingForImageLoading + .get(url) + .forEach((resolve) => resolve(imageDetails)); + awaitingForImageLoading.delete(url); + } + if (isCancelled()) { + return null; + } + } catch (err) { + logWarn(err); + if (awaitingForImageLoading.has(url)) { + awaitingForImageLoading + .get(url) + .forEach((resolve) => resolve(null)); + awaitingForImageLoading.delete(url); + } + } + } + if (imageDetails) { + const bgImageValue = getBgImageValue(imageDetails, theme); + if (bgImageValue) { + return bgImageValue; + } + } + if (url.startsWith("data:")) { + const blobURL = await tryConvertDataURLToBlobURL(url); + if (blobURL) { + return `url("${blobURL}")`; + } + } + return `url("${url}")`; + }; + }; + const isSafeToInvert = () => { + const repeat = (rule.style.backgroundRepeat || "").toLowerCase(); + const size = (rule.style.backgroundSize || "").toLowerCase(); + const isTiled = + repeat.length > 0 && + repeat !== "no-repeat" && + !repeat.includes("no-repeat"); + const isStretched = + size.includes("cover") || + size.includes("contain") || + size.includes("100%"); + return !isTiled && !isStretched; + }; + const getBgImageValue = (imageDetails, theme) => { + const {isDark, isLight, isTransparent, isLarge, solidColor, width} = + imageDetails; + let result = null; + const logSrc = imageDetails.src.startsWith("data:") + ? "data:" + : imageDetails.src; + if (isLarge && isLight && !isTransparent && theme.mode === 1) { + logInfo(`Hiding large light image ${logSrc}`); + result = "none"; + } else if ( + isDark && + isTransparent && + theme.mode === 1 && + width > 2 + ) { + logInfo(`Inverting dark image ${logSrc}`); + if (canFilterImage(imageDetails.src)) { + const inverted = getFilteredImageURL(imageDetails, { + ...theme, + sepia: clamp(theme.sepia + 10, 0, 100) + }); + result = `url("${inverted}")`; + } else if (isSafeToInvert()) { + pushFilter?.("invert"); + } + } else if (isLight && !isTransparent && theme.mode === 1) { + if (solidColor) { + logInfo(`Replacing image with a solid color ${logSrc}`); + const darkColor = modifyBackgroundColor( + solidColor, + theme, + false + ); + const solid = getSolidColorImageURL( + imageDetails, + darkColor + ); + result = `url("${solid}")`; + } else if (canFilterImage(imageDetails.src)) { + logInfo(`Inverting light image ${logSrc}`); + const inverted = getFilteredImageURL(imageDetails, theme); + result = `url("${inverted}")`; + } else if (isSafeToInvert()) { + pushFilter?.("invert"); + } + } else if (theme.mode === 0 && isLight && imageDetails.dataURL) { + logInfo(`Applying filter to image ${logSrc}`); + if (canFilterImage(imageDetails.src)) { + const filtered = getFilteredImageURL(imageDetails, { + ...theme, + brightness: clamp(theme.brightness - 10, 5, 200), + sepia: clamp(theme.sepia + 10, 0, 100) + }); + result = `url("${filtered}")`; + } else { + pushFilter?.("dim"); + } + } else { + if (theme.mode === 1 && !canFilterImage(imageDetails.src)) { + pushFilter?.("none"); + } + logInfo(`Not modifying the image ${logSrc}`); + } + return result; + }; + const modifiers = []; + let matchIndex = 0; + let prevHasComma = false; + matches.forEach( + ({type, match, index, typeGradient, hasComma, offset}, i) => { + const matchStart = index; + const prefixStart = matchIndex; + const matchEnd = matchStart + match.length + offset; + matchIndex = matchEnd; + if (prefixStart !== matchStart) { + if (prevHasComma) { + modifiers.push(() => { + let betweenValue = value.substring( + prefixStart, + matchStart + ); + if (betweenValue[0] === ",") { + betweenValue = betweenValue.substring(1); + } + return betweenValue; + }); + } else { + modifiers.push(() => + value.substring(prefixStart, matchStart) + ); + } + } + prevHasComma = hasComma || false; + if (type === "url") { + modifiers.push(getURLModifier(match)); + } else if (type === "gradient") { + modifiers.push( + getGradientModifier({ + match, + index, + typeGradient: typeGradient, + hasComma: hasComma || false, + offset + }) + ); + } + if (i === matches.length - 1) { + modifiers.push(() => value.substring(matchEnd)); + } + } + ); + return (theme) => { + const results = modifiers + .filter(Boolean) + .map((modify) => modify(theme)); + if (results.some((r) => r instanceof Promise)) { + return Promise.all(results).then((asyncResults) => { + return asyncResults.filter(Boolean).join(""); + }); + } + const combinedResult = results.join(""); + if (combinedResult.endsWith(", initial")) { + return combinedResult.slice(0, -9); + } + return combinedResult; + }; + } catch (err) { + logWarn(`Unable to parse gradient ${value}`, err); + return null; + } +} +function getShadowModifierWithInfo(value) { + try { + let index = 0; + const colorMatches = getMatches( + /(^|\s)(?!calc)([a-z]+\(.+?\)|#[0-9a-f]+|[a-z]+)(.*?(inset|outset)?($|,))/gi, + value, + 2 + ); + let notParsed = 0; + const modifiers = colorMatches.map((match, i) => { + const prefixIndex = index; + const matchIndex = value.indexOf(match, index); + const matchEnd = matchIndex + match.length; + index = matchEnd; + const rgb = parseColorWithCache(match); + if (!rgb) { + notParsed++; + return () => value.substring(prefixIndex, matchEnd); + } + return (theme) => + `${value.substring(prefixIndex, matchIndex)}${modifyShadowColor(rgb, theme)}${i === colorMatches.length - 1 ? value.substring(matchEnd) : ""}`; + }); + return (theme) => { + const modified = modifiers.map((modify) => modify(theme)).join(""); + return { + matchesLength: colorMatches.length, + unparsableMatchesLength: notParsed, + result: modified + }; + }; + } catch (err) { + logWarn(`Unable to parse shadow ${value}`, err); + return null; + } +} +function getShadowModifier(value) { + const shadowModifier = getShadowModifierWithInfo(value); + if (!shadowModifier) { + return null; + } + return (theme) => shadowModifier(theme).result; +} +function getScrollbarColorModifier(value) { + const colorsMatch = value.match( + /^\s*([a-z]+(\(.*\))?)\s+([a-z]+(\(.*\))?)\s*$/ + ); + if (!colorsMatch) { + return value; + } + const thumb = parseColorWithCache(colorsMatch[1]); + const track = parseColorWithCache(colorsMatch[3]); + if (!thumb || !track) { + logWarn("Couldn't parse color", ...[thumb, track].filter((c) => !c)); + return null; + } + return (theme) => + `${modifyForegroundColor(thumb, theme)} ${modifyBackgroundColor(track, theme)}`; +} +function getColorSchemeModifier() { + return (theme) => (theme.mode === 0 ? "dark light" : "dark"); +} +function getVariableModifier( + variablesStore, + prop, + value, + rule, + ignoredImgSelectors, + isCancelled +) { + return variablesStore.getModifierForVariable({ + varName: prop, + sourceValue: value, + rule, + ignoredImgSelectors, + isCancelled + }); +} +function getVariableDependantModifier(variablesStore, prop, value, rule) { + return variablesStore.getModifierForVarDependant(prop, value, rule); +} +function cleanModificationCache() { + clearColorModificationCache(); + imageDetailsCache.clear(); + cleanImageProcessingCache(); + awaitingForImageLoading.clear(); + imageSelectorQueue.clear(); + classObserver?.disconnect(); + classObserver = null; +} + +const VAR_TYPE_BG_COLOR = 1 << 0; +const VAR_TYPE_TEXT_COLOR = 1 << 1; +const VAR_TYPE_BORDER_COLOR = 1 << 2; +const VAR_TYPE_BG_IMG = 1 << 3; +const shouldSetDefaultColor = + !location.hostname.startsWith("www.ebay.") && + !location.hostname.includes(".ebay."); +class VariablesStore { + constructor() { + this.varTypes = new Map(); + this.rulesQueue = new Set(); + this.inlineStyleQueue = []; + this.definedVars = new Set(); + this.varRefs = new Map(); + this.unknownColorVars = new Set(); + this.unknownBgVars = new Set(); + this.undefinedVars = new Set(); + this.initialVarTypes = new Map(); + this.changedTypeVars = new Set(); + this.typeChangeSubscriptions = new Map(); + this.unstableVarValues = new Map(); + this.varFilterTypes = new Map(); + this.notifyingVarFilterTypes = new Set(); + this.varsChanged = false; + } + clear() { + this.varTypes.clear(); + this.rulesQueue.clear(); + this.inlineStyleQueue.splice(0); + this.definedVars.clear(); + this.varRefs.clear(); + this.unknownColorVars.clear(); + this.unknownBgVars.clear(); + this.undefinedVars.clear(); + this.initialVarTypes.clear(); + this.changedTypeVars.clear(); + this.typeChangeSubscriptions.clear(); + this.unstableVarValues.clear(); + this.varFilterTypes.clear(); + this.notifyingVarFilterTypes.clear(); + } + isVarType(varName, typeNum) { + return ( + this.varTypes.has(varName) && + (this.varTypes.get(varName) & typeNum) > 0 + ); + } + addRulesForMatching(rules) { + this.rulesQueue.add(rules); + } + addInlineStyleForMatching(style) { + this.inlineStyleQueue.push(style); + } + matchVariablesAndDependents() { + if (this.rulesQueue.size === 0 && this.inlineStyleQueue.length === 0) { + return; + } + this.changedTypeVars.clear(); + this.initialVarTypes = new Map(this.varTypes); + this.varsChanged = false; + this.collectRootVariables(); + this.collectVariablesAndVarDep(); + this.collectRootVarDependents(); + if (!this.varsChanged) { + return; + } + this.varRefs.forEach((refs, v) => { + refs.forEach((r) => { + if (this.varTypes.has(v)) { + this.resolveVariableType(r, this.varTypes.get(v)); + } + }); + }); + this.unknownColorVars.forEach((v) => { + if (this.unknownBgVars.has(v)) { + this.unknownColorVars.delete(v); + this.unknownBgVars.delete(v); + this.resolveVariableType(v, VAR_TYPE_BG_COLOR); + } else if ( + this.isVarType( + v, + VAR_TYPE_BG_COLOR | + VAR_TYPE_TEXT_COLOR | + VAR_TYPE_BORDER_COLOR + ) + ) { + this.unknownColorVars.delete(v); + } else { + this.undefinedVars.add(v); + } + }); + this.unknownBgVars.forEach((v) => { + const hasColor = + this.findVarRef(v, (ref) => { + return ( + this.unknownColorVars.has(ref) || + this.isVarType( + ref, + VAR_TYPE_BG_COLOR | + VAR_TYPE_TEXT_COLOR | + VAR_TYPE_BORDER_COLOR + ) + ); + }) != null; + if (hasColor) { + this.iterateVarRefs(v, (ref) => { + this.resolveVariableType(ref, VAR_TYPE_BG_COLOR); + }); + } else if (this.isVarType(v, VAR_TYPE_BG_COLOR | VAR_TYPE_BG_IMG)) { + this.unknownBgVars.delete(v); + } else { + this.undefinedVars.add(v); + } + }); + this.changedTypeVars.forEach((varName) => { + if (this.typeChangeSubscriptions.has(varName)) { + this.typeChangeSubscriptions + .get(varName) + .forEach((callback) => { + callback(); + }); + } + }); + this.changedTypeVars.clear(); + } + getModifierForVariable(options) { + return (theme) => { + const { + varName, + sourceValue, + rule, + ignoredImgSelectors, + isCancelled + } = options; + const getDeclarations = () => { + const declarations = []; + const addModifiedValue = ( + typeNum, + varNameWrapper, + colorModifier + ) => { + if (!this.isVarType(varName, typeNum)) { + return; + } + const property = varNameWrapper(varName); + let modifiedValue; + if (isVarDependant(sourceValue)) { + if (isConstructedColorVar(sourceValue)) { + let value = insertVarValues( + sourceValue, + this.unstableVarValues + ); + if (!value) { + value = + typeNum === VAR_TYPE_BG_COLOR + ? "#ffffff" + : "#000000"; + } + modifiedValue = colorModifier(value, theme); + } else { + modifiedValue = replaceCSSVariablesNames( + sourceValue, + (v) => varNameWrapper(v), + (fallback) => colorModifier(fallback, theme) + ); + } + } else { + modifiedValue = colorModifier(sourceValue, theme); + } + declarations.push({ + property, + value: modifiedValue + }); + }; + addModifiedValue( + VAR_TYPE_BG_COLOR, + wrapBgColorVariableName, + tryModifyBgColor + ); + addModifiedValue( + VAR_TYPE_TEXT_COLOR, + wrapTextColorVariableName, + tryModifyTextColor + ); + addModifiedValue( + VAR_TYPE_BORDER_COLOR, + wrapBorderColorVariableName, + tryModifyBorderColor + ); + if (this.isVarType(varName, VAR_TYPE_BG_IMG)) { + const property = wrapBgImgVariableName(varName); + let modifiedValue = sourceValue; + if (isVarDependant(sourceValue)) { + modifiedValue = replaceCSSVariablesNames( + sourceValue, + (v) => wrapBgColorVariableName(v), + (fallback) => tryModifyBgColor(fallback, theme) + ); + } + const pushFilter = rule.selectorText + ? (type) => this.setVarFilterType(varName, type) + : null; + const bgModifier = getBgImageModifier( + modifiedValue, + rule, + ignoredImgSelectors, + isCancelled, + pushFilter + ); + modifiedValue = + typeof bgModifier === "function" + ? bgModifier(theme) + : bgModifier; + declarations.push({ + property, + value: modifiedValue + }); + } + return declarations; + }; + const callbacks = new Set(); + const addListener = (onTypeChange) => { + if (!rule.selectorText) { + return; + } + const callback = () => { + const decs = getDeclarations(); + onTypeChange(decs); + }; + callbacks.add(callback); + this.subscribeForVarTypeChange(varName, callback); + }; + const removeListeners = () => { + callbacks.forEach((callback) => { + this.unsubscribeFromVariableTypeChanges(varName, callback); + }); + }; + return { + declarations: getDeclarations(), + onTypeChange: {addListener, removeListeners} + }; + }; + } + getModifierForVarDependant(property, sourceValue, rule) { + if (rule && rule.selectorText && isFilterCompatibleProp(property)) { + this.watchFilterVars(sourceValue, rule.selectorText); + } + const isConstructedColor = sourceValue.match(/^\s*(rgb|hsl)a?\(/); + const isSimpleConstructedColor = sourceValue.match( + /^rgba?\(var\(--[\-_A-Za-z0-9]+\)(\s*,?\/?\s*0?\.\d+)?\)$/ + ); + if (isConstructedColor && !isSimpleConstructedColor) { + const isBg = property.startsWith("background"); + const isText = isTextColorProperty(property); + return (theme) => { + let value = insertVarValues( + sourceValue, + this.unstableVarValues + ); + if (!value) { + value = isBg ? "#ffffff" : "#000000"; + } + const modifier = isBg + ? tryModifyBgColor + : isText + ? tryModifyTextColor + : tryModifyBorderColor; + return modifier(value, theme); + }; + } + if ( + property === "background-color" || + (isSimpleConstructedColor && property === "background") + ) { + return (theme) => { + const defaultFallback = shouldSetDefaultColor + ? tryModifyBgColor( + isConstructedColor ? "255, 255, 255" : "#ffffff", + theme + ) + : "transparent"; + return replaceCSSVariablesNames( + sourceValue, + (v) => wrapBgColorVariableName(v), + (fallback) => tryModifyBgColor(fallback, theme), + defaultFallback + ); + }; + } + if (isTextColorProperty(property)) { + return (theme) => { + const defaultFallback = tryModifyTextColor( + isConstructedColor ? "0, 0, 0" : "#000000", + theme + ); + return replaceCSSVariablesNames( + sourceValue, + (v) => wrapTextColorVariableName(v), + (fallback) => tryModifyTextColor(fallback, theme), + defaultFallback + ); + }; + } + if ( + property === "background" || + property === "background-image" || + property === "box-shadow" + ) { + return (theme) => { + const unknownVars = new Set(); + const modify = () => { + const variableReplaced = replaceCSSVariablesNames( + sourceValue, + (v) => { + if (this.isVarType(v, VAR_TYPE_BG_COLOR)) { + return wrapBgColorVariableName(v); + } + if (this.isVarType(v, VAR_TYPE_BG_IMG)) { + return wrapBgImgVariableName(v); + } + unknownVars.add(v); + return v; + }, + (fallback) => tryModifyBgColor(fallback, theme) + ); + if (property === "box-shadow") { + const shadowModifier = + getShadowModifierWithInfo(variableReplaced); + const modifiedShadow = shadowModifier(theme); + if ( + modifiedShadow.unparsableMatchesLength !== + modifiedShadow.matchesLength + ) { + return modifiedShadow.result; + } + } + return variableReplaced; + }; + const modified = modify(); + if (unknownVars.size > 0) { + if (isFallbackResolved(modified)) { + return modified; + } + return new Promise((resolve) => { + for (const unknownVar of unknownVars.values()) { + const callback = () => { + this.unsubscribeFromVariableTypeChanges( + unknownVar, + callback + ); + const newValue = modify(); + resolve(newValue); + }; + this.subscribeForVarTypeChange( + unknownVar, + callback + ); + } + }); + } + return modified; + }; + } + if (property.startsWith("border") || property.startsWith("outline")) { + return (theme) => { + return replaceCSSVariablesNames( + sourceValue, + (v) => wrapBorderColorVariableName(v), + (fallback) => tryModifyBorderColor(fallback, theme) + ); + }; + } + return null; + } + subscribeForVarTypeChange(varName, callback) { + if (!this.typeChangeSubscriptions.has(varName)) { + this.typeChangeSubscriptions.set(varName, new Set()); + } + const rootStore = this.typeChangeSubscriptions.get(varName); + if (!rootStore.has(callback)) { + rootStore.add(callback); + } + } + unsubscribeFromVariableTypeChanges(varName, callback) { + if (this.typeChangeSubscriptions.has(varName)) { + this.typeChangeSubscriptions.get(varName).delete(callback); + } + } + setVarFilterType(varName, type) { + if (this.varFilterTypes.get(varName) === type) { + return; + } + this.varFilterTypes.set(varName, type); + if (this.notifyingVarFilterTypes.has(varName)) { + return; + } + const subs = this.typeChangeSubscriptions.get(varName); + if (subs && subs.size > 0) { + this.notifyingVarFilterTypes.add(varName); + subs.forEach((callback) => callback()); + this.notifyingVarFilterTypes.delete(varName); + } + } + pushFilterSelectorsForValue(sourceValue, selector) { + const directRefs = new Set(); + iterateVarDependencies(sourceValue, (v) => directRefs.add(v)); + const allRefs = new Set(); + directRefs.forEach((v) => { + allRefs.add(v); + this.iterateVarRefs(v, (ref) => allRefs.add(ref)); + }); + allRefs.forEach((v) => { + const type = this.varFilterTypes.get(v); + if (type) { + pushFilterSelector(selector, type); + } + }); + } + watchFilterVars(sourceValue, selector) { + const directRefs = new Set(); + iterateVarDependencies(sourceValue, (v) => directRefs.add(v)); + const allRefs = new Set(); + directRefs.forEach((v) => { + allRefs.add(v); + this.iterateVarRefs(v, (ref) => allRefs.add(ref)); + }); + this.pushFilterSelectorsForValue(sourceValue, selector); + const callback = () => + this.pushFilterSelectorsForValue(sourceValue, selector); + allRefs.forEach((v) => this.subscribeForVarTypeChange(v, callback)); + } + collectVariablesAndVarDep() { + this.rulesQueue.forEach((rules) => { + iterateCSSRules(rules, (rule) => { + if (rule.style) { + this.collectVarsFromCSSDeclarations(rule.style); + } + }); + }); + this.inlineStyleQueue.forEach((style) => { + this.collectVarsFromCSSDeclarations(style); + }); + this.rulesQueue.clear(); + this.inlineStyleQueue.splice(0); + } + collectVarsFromCSSDeclarations(style) { + iterateCSSDeclarations(style, (property, value) => { + if (isVariable(property)) { + this.inspectVariable(property, value); + } + if (isVarDependant(value)) { + this.inspectVarDependant(property, value); + } + }); + } + shouldProcessRootVariables() { + return ( + this.rulesQueue.size > 0 && + document.documentElement.getAttribute("style")?.includes("--") + ); + } + collectRootVariables() { + if (!this.shouldProcessRootVariables()) { + return; + } + iterateCSSDeclarations( + document.documentElement.style, + (property, value) => { + if (isVariable(property)) { + this.inspectVariable(property, value); + } + } + ); + } + inspectVariable(varName, value) { + this.unstableVarValues.set(varName, value); + if (isVarDependant(value) && isConstructedColorVar(value)) { + if (!this.unknownColorVars.has(varName)) { + this.unknownColorVars.add(varName); + this.varsChanged = true; + } + this.definedVars.add(varName); + } + if (this.definedVars.has(varName)) { + return; + } + this.definedVars.add(varName); + this.varsChanged = true; + const isColor = Boolean( + getRGBValues(value) || parseColorWithCache(value) + ); + if (isColor) { + this.unknownColorVars.add(varName); + } else if ( + value.includes("url(") || + value.includes("linear-gradient(") || + value.includes("radial-gradient(") + ) { + this.resolveVariableType(varName, VAR_TYPE_BG_IMG); + } + } + resolveVariableType(varName, typeNum) { + const initialType = this.initialVarTypes.get(varName) || 0; + const currentType = this.varTypes.get(varName) || 0; + const newType = currentType | typeNum; + this.varTypes.set(varName, newType); + if (newType !== initialType || this.undefinedVars.has(varName)) { + this.changedTypeVars.add(varName); + this.undefinedVars.delete(varName); + } + if ( + newType !== currentType || + this.unknownColorVars.has(varName) || + this.unknownBgVars.has(varName) + ) { + this.varsChanged = true; + } + this.unknownColorVars.delete(varName); + this.unknownBgVars.delete(varName); + } + collectRootVarDependents() { + if (!this.shouldProcessRootVariables()) { + return; + } + iterateCSSDeclarations( + document.documentElement.style, + (property, value) => { + if (isVarDependant(value)) { + this.inspectVarDependant(property, value); + } + } + ); + } + inspectVarDependant(property, value) { + if (isVariable(property)) { + this.iterateVarDeps(value, (ref) => { + if (!this.varRefs.has(property)) { + this.varRefs.set(property, new Set()); + } + const refs = this.varRefs.get(property); + if (!refs.has(ref)) { + refs.add(ref); + this.varsChanged = true; + } + }); + } else if ( + property === "background-color" || + property === "box-shadow" + ) { + this.iterateVarDeps(value, (v) => + this.resolveVariableType(v, VAR_TYPE_BG_COLOR) + ); + } else if (isTextColorProperty(property)) { + this.iterateVarDeps(value, (v) => + this.resolveVariableType(v, VAR_TYPE_TEXT_COLOR) + ); + } else if ( + property.startsWith("border") || + property.startsWith("outline") + ) { + this.iterateVarDeps(value, (v) => + this.resolveVariableType(v, VAR_TYPE_BORDER_COLOR) + ); + } else if ( + property === "background" || + property === "background-image" + ) { + this.iterateVarDeps(value, (v) => { + if (this.isVarType(v, VAR_TYPE_BG_COLOR | VAR_TYPE_BG_IMG)) { + return; + } + const isBgColor = + this.findVarRef(v, (ref) => { + return ( + this.unknownColorVars.has(ref) || + this.isVarType( + ref, + VAR_TYPE_BG_COLOR | + VAR_TYPE_TEXT_COLOR | + VAR_TYPE_BORDER_COLOR + ) + ); + }) != null; + this.iterateVarRefs(v, (ref) => { + if (isBgColor) { + this.resolveVariableType(ref, VAR_TYPE_BG_COLOR); + } else if (!this.unknownBgVars.has(ref)) { + this.unknownBgVars.add(ref); + this.varsChanged = true; + } + }); + }); + } + } + iterateVarDeps(value, iterator) { + const varDeps = new Set(); + iterateVarDependencies(value, (v) => varDeps.add(v)); + varDeps.forEach((v) => iterator(v)); + } + findVarRef(varName, iterator, visited = new Set()) { + const queue = [varName]; + while (queue.length > 0) { + const v = queue.pop(); + if (visited.has(v)) { + continue; + } + visited.add(v); + if (iterator(v)) { + return v; + } + const refs = this.varRefs.get(v); + if (refs) { + refs.forEach((ref) => queue.push(ref)); + } + } + return null; + } + iterateVarRefs(varName, iterator) { + this.findVarRef(varName, (ref) => { + iterator(ref); + return false; + }); + } + setOnRootVariableChange(callback) { + this.onRootVariableDefined = callback; + } + putRootVars(styleElement, theme) { + const declarations = new Map(); + iterateCSSDeclarations( + document.documentElement.style, + (property, value) => { + if (isVariable(property)) { + if (this.isVarType(property, VAR_TYPE_BG_COLOR)) { + declarations.set( + wrapBgColorVariableName(property), + tryModifyBgColor(value, theme) + ); + } + if (this.isVarType(property, VAR_TYPE_TEXT_COLOR)) { + declarations.set( + wrapTextColorVariableName(property), + tryModifyTextColor(value, theme) + ); + } + if (this.isVarType(property, VAR_TYPE_BORDER_COLOR)) { + declarations.set( + wrapBorderColorVariableName(property), + tryModifyBorderColor(value, theme) + ); + } + this.subscribeForVarTypeChange( + property, + this.onRootVariableDefined + ); + } + } + ); + const cssLines = []; + cssLines.push(":root {"); + for (const [property, value] of declarations) { + cssLines.push(` ${property}: ${value};`); + } + cssLines.push("}"); + const cssText = cssLines.join("\n"); + const sheet = styleElement.sheet; + if (sheet) { + if (sheet.cssRules.length > 0) { + sheet.deleteRule(0); + } + sheet.insertRule(cssText); + } else { + styleElement.textContent = cssText; + } + } +} +const variablesStore = new VariablesStore(); +function getVariableRange(input, searchStart = 0) { + const start = input.indexOf("var(", searchStart); + if (start >= 0) { + const range = getParenthesesRange(input, start + 3); + if (range) { + return {start, end: range.end}; + } + } + return null; +} +function getVariablesMatches(input) { + const ranges = []; + let i = 0; + let range; + while ((range = getVariableRange(input, i))) { + const {start, end} = range; + ranges.push({start, end, value: input.substring(start, end)}); + i = range.end + 1; + } + return ranges; +} +function replaceVariablesMatches(input, replacer) { + const matches = getVariablesMatches(input); + const matchesCount = matches.length; + if (matchesCount === 0) { + return input; + } + const inputLength = input.length; + const replacements = matches.map((m) => replacer(m.value, matches.length)); + const parts = []; + parts.push(input.substring(0, matches[0].start)); + for (let i = 0; i < matchesCount; i++) { + parts.push(replacements[i]); + const start = matches[i].end; + const end = i < matchesCount - 1 ? matches[i + 1].start : inputLength; + parts.push(input.substring(start, end)); + } + return parts.join(""); +} +function getVariableNameAndFallback(match) { + const commaIndex = match.indexOf(","); + let name; + let fallback; + if (commaIndex >= 0) { + name = match.substring(4, commaIndex).trim(); + fallback = match.substring(commaIndex + 1, match.length - 1).trim(); + } else { + name = match.substring(4, match.length - 1).trim(); + fallback = ""; + } + return {name, fallback}; +} +function replaceCSSVariablesNames( + value, + nameReplacer, + fallbackReplacer, + finalFallback +) { + const matchReplacer = (match) => { + const {name, fallback} = getVariableNameAndFallback(match); + const newName = nameReplacer(name); + if (!fallback) { + if (finalFallback) { + return `var(${newName}, ${finalFallback})`; + } + return `var(${newName})`; + } + let newFallback; + if (isVarDependant(fallback)) { + newFallback = replaceCSSVariablesNames( + fallback, + nameReplacer, + fallbackReplacer + ); + } else if (fallbackReplacer) { + newFallback = fallbackReplacer(fallback); + } else { + newFallback = fallback; + } + return `var(${newName}, ${newFallback})`; + }; + return replaceVariablesMatches(value, matchReplacer); +} +function iterateVarDependencies(value, iterator) { + replaceCSSVariablesNames(value, (varName) => { + iterator(varName); + return varName; + }); +} +function wrapBgColorVariableName(name) { + return `--darkreader-bg${name}`; +} +function wrapTextColorVariableName(name) { + return `--darkreader-text${name}`; +} +function wrapBorderColorVariableName(name) { + return `--darkreader-border${name}`; +} +function wrapBgImgVariableName(name) { + return `--darkreader-bgimg${name}`; +} +function isVariable(property) { + return property.startsWith("--"); +} +function isVarDependant(value) { + return value.includes("var("); +} +function isConstructedColorVar(value) { + return ( + value.match(/^\s*(rgb|hsl)a?\(/) || + value.match(/^(((\d{1,3})|(var\([\-_A-Za-z0-9]+\))),?\s*?){3}$/) + ); +} +function isFallbackResolved(modified) { + if (modified.startsWith("var(") && modified.endsWith(")")) { + const hasNestedBrackets = modified.endsWith("))"); + const hasDoubleNestedBrackets = modified.endsWith(")))"); + const lastOpenBracketIndex = hasNestedBrackets + ? modified.lastIndexOf("(") + : -1; + const firstOpenBracketIndex = hasDoubleNestedBrackets + ? modified.lastIndexOf("(", lastOpenBracketIndex - 1) + : lastOpenBracketIndex; + const commaIndex = modified.lastIndexOf( + ",", + hasNestedBrackets ? firstOpenBracketIndex : modified.length + ); + if (commaIndex < 0 || modified[commaIndex + 1] !== " ") { + return false; + } + const fallback = modified.slice(commaIndex + 2, modified.length - 1); + if (hasNestedBrackets) { + return ( + fallback.startsWith("rgb(") || + fallback.startsWith("rgba(") || + fallback.startsWith("hsl(") || + fallback.startsWith("hsla(") || + fallback.startsWith("var(--darkreader-bg--") || + fallback.startsWith("var(--darkreader-background-") || + (hasDoubleNestedBrackets && + fallback.includes("var(--darkreader-background-")) + ); + } + return fallback.match(/^(#[0-9a-f]+)|([a-z]+)$/i); + } + return false; +} +const textColorProps = [ + "color", + "caret-color", + "-webkit-text-fill-color", + "fill", + "stroke" +]; +function isTextColorProperty(property) { + return textColorProps.includes(property); +} +function parseRawColorValue(input) { + const v = getRGBValues(input); + if (v) { + const color = + v[3] < 1 + ? `rgb(${v[0]} ${v[1]} ${v[2]} / ${v[3]})` + : `rgb(${v[0]} ${v[1]} ${v[2]})`; + return {isRaw: true, color}; + } + return {isRaw: false, color: input}; +} +function handleRawColorValue(input, theme, modifyFunction) { + const {isRaw, color} = parseRawColorValue(input); + const rgb = parseColorWithCache(color); + if (rgb) { + const outputColor = modifyFunction(rgb, theme, !isRaw); + if (isRaw) { + const outputInRGB = parseColorWithCache(outputColor); + return outputInRGB + ? Number.isNaN(outputInRGB.a) || outputInRGB.a === 1 + ? `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}` + : `${outputInRGB.r}, ${outputInRGB.g}, ${outputInRGB.b}, ${outputInRGB.a}` + : outputColor; + } + return outputColor; + } + return color; +} +function tryModifyBgColor(color, theme) { + return handleRawColorValue(color, theme, modifyBackgroundColor); +} +function tryModifyTextColor(color, theme) { + return handleRawColorValue(color, theme, modifyForegroundColor); +} +function tryModifyBorderColor(color, theme) { + return handleRawColorValue(color, theme, modifyBorderColor); +} +const MAX_VARIABLE_SUBSTITUTIONS = 100000; +const MAX_VARIABLE_DEPTH = 1000; +function insertVarValues( + source, + varValues, + stack = new Set(), + cache = new Map(), + depth = 0 +) { + if (depth > MAX_VARIABLE_DEPTH) { + return null; + } + let containsUnresolvedVar = false; + const matchReplacer = (match) => { + const {name, fallback} = getVariableNameAndFallback(match); + const varValue = varValues.get(name); + let inserted = null; + if (varValue) { + if (cache.has(name)) { + inserted = cache.get(name); + } else if (!stack.has(name)) { + stack.add(name); + if (isVarDependant(varValue)) { + inserted = insertVarValues( + varValue, + varValues, + stack, + cache, + depth + 1 + ); + } else { + inserted = varValue; + } + stack.delete(name); + cache.set(name, inserted); + } + } else if (fallback) { + if (isVarDependant(fallback)) { + inserted = insertVarValues( + fallback, + varValues, + stack, + cache, + depth + 1 + ); + } else { + inserted = fallback; + } + } + if (!inserted) { + containsUnresolvedVar = true; + return null; + } + return inserted; + }; + const replaced = replaceVariablesMatches(source, matchReplacer); + if (containsUnresolvedVar || replaced.length > MAX_VARIABLE_SUBSTITUTIONS) { + return null; + } + return replaced; +} + +function getThemeKey$1(theme) { + let resultKey = ""; + themeCacheKeys.forEach((key) => { + resultKey += `${key}:${theme[key]};`; + }); + return resultKey; +} +const asyncQueue = createAsyncTasksQueue(); +let cssTextCounter = 0; +const cssTextIds = new Map(); +function getCSSTextKey(cssText) { + const existing = cssTextIds.get(cssText); + if (existing !== undefined) { + return existing; + } + let n = ++cssTextCounter; + let key = ""; + do { + key = String.fromCharCode(n & 0xffff) + key; + n >>>= 16; + } while (n > 0); + cssTextIds.set(cssText, key); + return key; +} +function createStyleSheetModifier() { + let renderId = 0; + function getStyleRuleKey(rule) { + let key = getCSSTextKey(rule.cssText); + if (isMediaRule(rule.parentRule)) { + key = `${getCSSTextKey(rule.parentRule.media.mediaText)}{${key}}`; + } + if (isLayerRule(rule.parentRule)) { + key = `${getCSSTextKey(rule.parentRule.name)}{${key}}`; + } + if (isContainerRule(rule.parentRule)) { + const {containerName, containerQuery} = rule.parentRule; + key = `${getCSSTextKey(`${containerName} ${containerQuery}`)}{${key}}`; + } + return key; + } + const rulesTextCache = new Set(); + const rulesModCache = new Map(); + const varTypeChangeCleaners = new Set(); + let prevFilterKey = null; + let hasNonLoadedLink = false; + let wasRebuilt = false; + function shouldRebuildStyle() { + return hasNonLoadedLink && !wasRebuilt; + } + function modifySheet(options) { + const rules = options.sourceCSSRules; + const { + theme, + ignoreImageAnalysis, + force, + prepareSheet, + isAsyncCancelled + } = options; + let rulesChanged = rulesModCache.size === 0; + const notFoundCacheKeys = new Set(rulesModCache.keys()); + const themeKey = getThemeKey$1(theme); + const themeChanged = themeKey !== prevFilterKey; + if (hasNonLoadedLink) { + wasRebuilt = true; + } + const modRules = []; + iterateCSSRules( + rules, + (rule) => { + const key = getStyleRuleKey(rule); + let textDiffersFromPrev = false; + notFoundCacheKeys.delete(key); + if (!rulesTextCache.has(key)) { + rulesTextCache.add(key); + textDiffersFromPrev = true; + } + if (textDiffersFromPrev) { + rulesChanged = true; + } else { + modRules.push(rulesModCache.get(key)); + return; + } + if (rule.style.all === "revert") { + return; + } + const modDecs = []; + rule.style && + iterateCSSDeclarations(rule.style, (property, value) => { + const mod = getModifiableCSSDeclaration( + property, + value, + rule, + variablesStore, + ignoreImageAnalysis, + isAsyncCancelled + ); + if (mod) { + modDecs.push(mod); + } + }); + let modRule = null; + if (modDecs.length > 0) { + const parentRule = rule.parentRule; + modRule = { + selector: rule.selectorText, + declarations: modDecs, + parentRule + }; + modRules.push(modRule); + } + rulesModCache.set(key, modRule); + }, + () => { + hasNonLoadedLink = true; + } + ); + notFoundCacheKeys.forEach((key) => { + rulesTextCache.delete(key); + rulesModCache.delete(key); + }); + prevFilterKey = themeKey; + if (!force && !rulesChanged && !themeChanged) { + return; + } + renderId++; + function setRule(target, index, rule) { + const {selector, declarations} = rule; + let selectorText = selector; + const emptyIsWhereSelector = + isChromium && + selector.startsWith(":is(") && + (selector.includes(":is()") || + selector.includes(":where()") || + (selector.includes(":where(") && + selector.includes(":-moz"))); + const viewTransitionSelector = + selector.includes("::view-transition-"); + if (emptyIsWhereSelector || viewTransitionSelector) { + selectorText = ".darkreader-unsupported-selector"; + } + if (isChromium && selectorText.endsWith("::picker")) { + selectorText = selectorText.replaceAll( + "::picker", + "::picker(select)" + ); + } + let ruleText = `${selectorText} {`; + for (const dec of declarations) { + const {property, value, important} = dec; + if (value) { + ruleText += ` ${property}: ${value}${important ? " !important" : ""};`; + } + } + ruleText += " }"; + target.insertRule(ruleText, index); + } + const asyncDeclarations = new Map(); + const varDeclarations = new Map(); + let asyncDeclarationCounter = 0; + let varDeclarationCounter = 0; + const rootReadyGroup = {rule: null, rules: [], isGroup: true}; + const groupRefs = new WeakMap(); + function getGroup(rule) { + if (rule == null) { + return rootReadyGroup; + } + if (groupRefs.has(rule)) { + return groupRefs.get(rule); + } + const group = {rule, rules: [], isGroup: true}; + groupRefs.set(rule, group); + const parentGroup = getGroup(rule.parentRule); + parentGroup.rules.push(group); + return group; + } + varTypeChangeCleaners.forEach((clear) => clear()); + varTypeChangeCleaners.clear(); + modRules + .filter((r) => r) + .forEach(({selector, declarations, parentRule}) => { + const group = getGroup(parentRule); + const readyStyleRule = { + selector, + declarations: [], + isGroup: false + }; + const readyDeclarations = readyStyleRule.declarations; + group.rules.push(readyStyleRule); + function handleAsyncDeclaration( + property, + modified, + important, + sourceValue + ) { + const asyncKey = ++asyncDeclarationCounter; + const asyncDeclaration = { + property, + value: null, + important, + asyncKey, + sourceValue + }; + readyDeclarations.push(asyncDeclaration); + const currentRenderId = renderId; + modified.then((asyncValue) => { + if ( + !asyncValue || + isAsyncCancelled() || + currentRenderId !== renderId + ) { + return; + } + asyncDeclaration.value = asyncValue; + asyncQueue.add(() => { + if ( + isAsyncCancelled() || + currentRenderId !== renderId + ) { + return; + } + rebuildAsyncRule(asyncKey); + }); + }); + } + function handleVarDeclarations( + property, + modified, + important, + sourceValue + ) { + const {declarations: varDecs, onTypeChange} = modified; + const varKey = ++varDeclarationCounter; + const currentRenderId = renderId; + const initialIndex = readyDeclarations.length; + let oldDecs = []; + if (varDecs.length === 0) { + const tempDec = { + property, + value: sourceValue, + important, + sourceValue, + varKey + }; + readyDeclarations.push(tempDec); + oldDecs = [tempDec]; + } + varDecs.forEach((mod) => { + if (mod.value instanceof Promise) { + handleAsyncDeclaration( + mod.property, + mod.value, + important, + sourceValue + ); + } else { + const readyDec = { + property: mod.property, + value: mod.value, + important, + sourceValue, + varKey + }; + readyDeclarations.push(readyDec); + oldDecs.push(readyDec); + } + }); + onTypeChange.addListener((newDecs) => { + if ( + isAsyncCancelled() || + currentRenderId !== renderId + ) { + return; + } + const readyVarDecs = newDecs.map((mod) => { + return { + property: mod.property, + value: mod.value, + important, + sourceValue, + varKey + }; + }); + const index = readyDeclarations.indexOf( + oldDecs[0], + initialIndex + ); + readyDeclarations.splice( + index, + oldDecs.length, + ...readyVarDecs + ); + oldDecs = readyVarDecs; + rebuildVarRule(varKey); + }); + varTypeChangeCleaners.add(() => + onTypeChange.removeListeners() + ); + } + declarations.forEach( + ({property, value, important, sourceValue}) => { + if (typeof value === "function") { + const modified = value(theme); + if (modified instanceof Promise) { + handleAsyncDeclaration( + property, + modified, + important, + sourceValue + ); + } else if (property.startsWith("--")) { + handleVarDeclarations( + property, + modified, + important, + sourceValue + ); + } else { + readyDeclarations.push({ + property, + value: modified, + important, + sourceValue + }); + } + } else { + readyDeclarations.push({ + property, + value, + important, + sourceValue + }); + } + } + ); + }); + const sheet = prepareSheet(); + function buildStyleSheet() { + function createTarget(group, parent) { + const {rule} = group; + if (isStyleRule(rule)) { + const {selectorText} = rule; + const index = parent.cssRules.length; + parent.insertRule(`${selectorText} {}`, index); + return parent.cssRules[index]; + } + if (isMediaRule(rule)) { + const {media} = rule; + const index = parent.cssRules.length; + parent.insertRule(`@media ${media.mediaText} {}`, index); + return parent.cssRules[index]; + } + if (isLayerRule(rule)) { + const {name} = rule; + const index = parent.cssRules.length; + parent.insertRule(`@layer ${name} {}`, index); + return parent.cssRules[index]; + } + if (isContainerRule(rule)) { + const {containerName, containerQuery} = rule; + const index = parent.cssRules.length; + const query = containerName + ? `${containerName} ${containerQuery}` + : containerQuery; + parent.insertRule(`@container ${query} {}`, index); + return parent.cssRules[index]; + } + return parent; + } + function iterateReadyRules(group, target, styleIterator) { + group.rules.forEach((r) => { + if (r.isGroup) { + const t = createTarget(r, target); + iterateReadyRules(r, t, styleIterator); + } else { + styleIterator(r, target); + } + }); + } + iterateReadyRules(rootReadyGroup, sheet, (rule, target) => { + const index = target.cssRules.length; + rule.declarations.forEach(({asyncKey, varKey}) => { + if (asyncKey != null) { + asyncDeclarations.set(asyncKey, {rule, target, index}); + } + if (varKey != null) { + varDeclarations.set(varKey, {rule, target, index}); + } + }); + setRule(target, index, rule); + }); + } + function rebuildAsyncRule(key) { + const {rule, target, index} = asyncDeclarations.get(key); + target.deleteRule(index); + setRule(target, index, rule); + asyncDeclarations.delete(key); + } + function rebuildVarRule(key) { + const {rule, target, index} = varDeclarations.get(key); + target.deleteRule(index); + setRule(target, index, rule); + } + buildStyleSheet(); + } + return {modifySheet, shouldRebuildStyle}; +} + +let canUseSheetProxy$1 = false; +document.addEventListener( + "__darkreader__inlineScriptsAllowed", + () => (canUseSheetProxy$1 = true), + {once: true} +); +const overrides$1 = new WeakSet(); +const overridesBySource = new WeakMap(); +function canHaveAdoptedStyleSheets(node) { + return Array.isArray(node.adoptedStyleSheets); +} +const getAdoptedSheets = isFirefox + ? (node) => + node.adoptedStyleSheets.wrappedJSObject ?? node.adoptedStyleSheets + : (node) => node.adoptedStyleSheets; +const createOverrideSheet = isFirefox + ? () => { + const pageWindow = window.wrappedJSObject ?? window; + return new pageWindow.CSSStyleSheet(); + } + : () => new CSSStyleSheet(); +function createAdoptedStyleSheetOverride(node) { + let cancelAsyncOperations = false; + function iterateSourceSheets(iterator) { + forEach(getAdoptedSheets(node), (sheet) => { + if (!overrides$1.has(sheet)) { + iterator(sheet); + } + defineSheetScope(sheet, node); + }); + } + function injectSheet(sheet, override) { + const newSheets = isFirefox + ? getAdoptedSheets(node) + : [...node.adoptedStyleSheets]; + const sheetIndex = newSheets.indexOf(sheet); + const overrideIndex = newSheets.indexOf(override); + if (overrideIndex >= 0) { + newSheets.splice(overrideIndex, 1); + } + newSheets.splice(sheetIndex + 1, 0, override); + if (!isFirefox) { + node.adoptedStyleSheets = newSheets; + } + } + function clear() { + const newSheets = isFirefox + ? getAdoptedSheets(node) + : [...node.adoptedStyleSheets]; + for (let i = newSheets.length - 1; i >= 0; i--) { + const sheet = newSheets[i]; + if (overrides$1.has(sheet)) { + newSheets.splice(i, 1); + } + } + if (!isFirefox && node.adoptedStyleSheets.length !== newSheets.length) { + node.adoptedStyleSheets = newSheets; + } + sourceSheets = new WeakSet(); + sourceDeclarations = new WeakSet(); + } + const cleaners = []; + function destroy() { + cleaners.forEach((c) => c()); + cleaners.splice(0); + cancelAsyncOperations = true; + clear(); + if (frameId) { + cancelAnimationFrame(frameId); + frameId = null; + } + } + let rulesChangeKey = 0; + function getRulesChangeKey() { + let count = 0; + iterateSourceSheets((sheet) => { + count += sheet.cssRules.length; + }); + if (count === 1) { + const rule = getAdoptedSheets(node)[0].cssRules[0]; + return rule instanceof CSSStyleRule ? rule.style.length : count; + } + return count; + } + let sourceSheets = new WeakSet(); + let sourceDeclarations = new WeakSet(); + function render(theme, ignoreImageAnalysis) { + clear(); + const sheets = getAdoptedSheets(node); + for (let i = sheets.length - 1; i >= 0; i--) { + const sheet = sheets[i]; + if (overrides$1.has(sheet)) { + continue; + } + sourceSheets.add(sheet); + const readyOverride = overridesBySource.get(sheet); + if (readyOverride) { + rulesChangeKey = getRulesChangeKey(); + injectSheet(sheet, readyOverride); + continue; + } + const rules = sheet.cssRules; + const override = createOverrideSheet(); + overridesBySource.set(sheet, override); + iterateCSSRules(rules, (rule) => + sourceDeclarations.add(rule.style) + ); + const prepareSheet = () => { + for (let i = override.cssRules.length - 1; i >= 0; i--) { + override.deleteRule(i); + } + override.insertRule("#__darkreader__adoptedOverride {}"); + injectSheet(sheet, override); + overrides$1.add(override); + return override; + }; + const sheetModifier = createStyleSheetModifier(); + sheetModifier.modifySheet({ + prepareSheet, + sourceCSSRules: rules, + theme, + ignoreImageAnalysis, + force: false, + isAsyncCancelled: () => cancelAsyncOperations + }); + } + rulesChangeKey = getRulesChangeKey(); + } + let callbackRequested = false; + function handleArrayChange(callback) { + if (callbackRequested) { + return; + } + callbackRequested = true; + queueMicrotask(() => { + callbackRequested = false; + const sheets = getAdoptedSheets(node).filter( + (s) => !overrides$1.has(s) + ); + sheets.forEach((sheet) => overridesBySource.delete(sheet)); + callback(sheets); + }); + } + function checkForUpdates() { + return getRulesChangeKey() !== rulesChangeKey; + } + let frameId = null; + function watchUsingRAF(callback) { + frameId = requestAnimationFrame(() => { + if (canUseSheetProxy$1) { + return; + } + if (checkForUpdates()) { + handleArrayChange(callback); + } + watchUsingRAF(callback); + }); + } + function addSheetChangeEventListener(type, listener) { + node.addEventListener(type, listener); + cleaners.push(() => node.removeEventListener(type, listener)); + } + function watch(callback) { + const onAdoptedSheetsChange = () => { + canUseSheetProxy$1 = true; + handleArrayChange(callback); + }; + addSheetChangeEventListener( + "__darkreader__adoptedStyleSheetsChange", + onAdoptedSheetsChange + ); + addSheetChangeEventListener( + "__darkreader__adoptedStyleSheetChange", + onAdoptedSheetsChange + ); + addSheetChangeEventListener( + "__darkreader__adoptedStyleDeclarationChange", + onAdoptedSheetsChange + ); + if (canUseSheetProxy$1) { + return; + } + watchUsingRAF(callback); + } + return { + render, + destroy, + watch + }; +} + +const hostsBreakingOnStylePosition = [ + "chat.google.com", + "gogoprivate.com", + "gprivate.com", + "www.berlingske.dk", + "www.bloomberg.com", + "www.diffusioneshop.com", + "www.weekendavisen.dk", + "zhale.me" +]; +const mode = hostsBreakingOnStylePosition.includes(location.hostname) + ? "away" + : "next"; +function getStyleInjectionMode() { + return mode; +} +const stylesWaitingForBody = new Set(); +let bodyObserver = null; +function injectStyleAway(styleElement) { + if (!document.body) { + stylesWaitingForBody.add(styleElement); + if (!bodyObserver) { + bodyObserver = new MutationObserver(() => { + if (document.body) { + bodyObserver.disconnect(); + bodyObserver = null; + stylesWaitingForBody.forEach((el) => injectStyleAway(el)); + stylesWaitingForBody.clear(); + } + }); + bodyObserver.observe(document, {childList: true, subtree: true}); + } + return; + } + let container = document.body.querySelector(".darkreader-style-container"); + if (!container) { + container = document.createElement("div"); + container.classList.add("darkreader"); + container.classList.add("darkreader-style-container"); + container.style.display = "none"; + document.body.append(container); + containerObserver = new MutationObserver(() => { + if (container?.nextElementSibling != null) { + container + .querySelectorAll(".darkreader--sync") + .forEach((el) => { + if (el.sheet.cssRules.length > 0) { + let cssText = ""; + for (const rule of el.sheet.cssRules) { + cssText += rule.cssText; + } + el.textContent = cssText; + } + }); + document.body.append(container); + } + }); + containerObserver.observe(document.body, {childList: true}); + } + container.append(styleElement); +} +let containerObserver; +function removeStyleContainer() { + bodyObserver?.disconnect(); + bodyObserver = null; + stylesWaitingForBody.clear(); + containerObserver?.disconnect(); + document.querySelector(".darkreader-style-container")?.remove(); +} + +const overrides = { + "background-color": { + customProp: "--darkreader-inline-bgcolor", + cssProp: "background-color", + dataAttr: "data-darkreader-inline-bgcolor" + }, + "background-image": { + customProp: "--darkreader-inline-bgimage", + cssProp: "background-image", + dataAttr: "data-darkreader-inline-bgimage" + }, + "border-color": { + customProp: "--darkreader-inline-border", + cssProp: "border-color", + dataAttr: "data-darkreader-inline-border" + }, + "border-bottom-color": { + customProp: "--darkreader-inline-border-bottom", + cssProp: "border-bottom-color", + dataAttr: "data-darkreader-inline-border-bottom" + }, + "border-left-color": { + customProp: "--darkreader-inline-border-left", + cssProp: "border-left-color", + dataAttr: "data-darkreader-inline-border-left" + }, + "border-right-color": { + customProp: "--darkreader-inline-border-right", + cssProp: "border-right-color", + dataAttr: "data-darkreader-inline-border-right" + }, + "border-top-color": { + customProp: "--darkreader-inline-border-top", + cssProp: "border-top-color", + dataAttr: "data-darkreader-inline-border-top" + }, + "box-shadow": { + customProp: "--darkreader-inline-boxshadow", + cssProp: "box-shadow", + dataAttr: "data-darkreader-inline-boxshadow" + }, + "color": { + customProp: "--darkreader-inline-color", + cssProp: "color", + dataAttr: "data-darkreader-inline-color" + }, + "fill": { + customProp: "--darkreader-inline-fill", + cssProp: "fill", + dataAttr: "data-darkreader-inline-fill" + }, + "stroke": { + customProp: "--darkreader-inline-stroke", + cssProp: "stroke", + dataAttr: "data-darkreader-inline-stroke" + }, + "outline-color": { + customProp: "--darkreader-inline-outline", + cssProp: "outline-color", + dataAttr: "data-darkreader-inline-outline" + }, + "stop-color": { + customProp: "--darkreader-inline-stopcolor", + cssProp: "stop-color", + dataAttr: "data-darkreader-inline-stopcolor" + } +}; +const shorthandOverrides = { + "background": { + customProp: "--darkreader-inline-bg", + cssProp: "background", + dataAttr: "data-darkreader-inline-bg" + }, + "border": { + customProp: "--darkreader-inline-border-short", + cssProp: "border", + dataAttr: "data-darkreader-inline-border-short" + }, + "border-bottom": { + customProp: "--darkreader-inline-border-bottom-short", + cssProp: "border-bottom", + dataAttr: "data-darkreader-inline-border-bottom-short" + }, + "border-left": { + customProp: "--darkreader-inline-border-left-short", + cssProp: "border-left", + dataAttr: "data-darkreader-inline-border-left-short" + }, + "border-right": { + customProp: "--darkreader-inline-border-right-short", + cssProp: "border-right", + dataAttr: "data-darkreader-inline-border-right-short" + }, + "border-top": { + customProp: "--darkreader-inline-border-top-short", + cssProp: "border-top", + dataAttr: "data-darkreader-inline-border-top-short" + } +}; +const overridesList = Object.values(overrides); +const normalizedPropList = {}; +overridesList.forEach( + ({cssProp, customProp}) => (normalizedPropList[customProp] = cssProp) +); +const INLINE_STYLE_ATTRS = [ + "style", + "fill", + "stop-color", + "stroke", + "bgcolor", + "color", + "background" +]; +const INLINE_STYLE_SELECTOR = INLINE_STYLE_ATTRS.map( + (attr) => `[${attr}]` +).join(", "); +function getInlineOverrideStyle() { + const allOverrides = overridesList.concat( + Object.values(shorthandOverrides) + ); + return allOverrides + .map(({dataAttr, customProp, cssProp}) => { + return [ + `[${dataAttr}] {`, + ` ${cssProp}: var(${customProp}) !important;`, + "}" + ].join("\n"); + }) + .concat([ + "[data-darkreader-inline-invert] {", + " filter: invert(100%) hue-rotate(180deg);", + "}" + ]) + .join("\n"); +} +function getInlineStyleElements(root) { + const results = []; + if (root instanceof Element && root.matches(INLINE_STYLE_SELECTOR)) { + results.push(root); + } + if ( + root instanceof Element || + (isShadowDomSupported && root instanceof ShadowRoot) || + root instanceof Document + ) { + push(results, root.querySelectorAll(INLINE_STYLE_SELECTOR)); + } + return results; +} +const treeObservers = new Map(); +const attrObservers = new Map(); +let asyncCancelled = true; +function isAsyncCancelled() { + return asyncCancelled; +} +function watchForInlineStyles(elementStyleDidChange, shadowRootDiscovered) { + deepWatchForInlineStyles( + document, + elementStyleDidChange, + shadowRootDiscovered + ); + iterateShadowHosts(document.documentElement, (host) => { + deepWatchForInlineStyles( + host.shadowRoot, + elementStyleDidChange, + shadowRootDiscovered + ); + }); +} +function deepWatchForInlineStyles( + root, + elementStyleDidChange, + shadowRootDiscovered +) { + if (treeObservers.has(root)) { + treeObservers.get(root).disconnect(); + attrObservers.get(root).disconnect(); + } + const discoveredNodes = new WeakSet(); + function discoverNodes(node) { + getInlineStyleElements(node).forEach((el) => { + if (discoveredNodes.has(el)) { + return; + } + discoveredNodes.add(el); + elementStyleDidChange(el); + }); + iterateShadowHosts(node, (n) => { + if (discoveredNodes.has(n)) { + return; + } + discoveredNodes.add(n); + shadowRootDiscovered(n.shadowRoot); + deepWatchForInlineStyles( + n.shadowRoot, + elementStyleDidChange, + shadowRootDiscovered + ); + }); + } + const treeObserver = createOptimizedTreeObserver(root, { + onMinorMutations: (_root, {additions}) => { + additions.forEach((added) => discoverNodes(added)); + variablesStore.matchVariablesAndDependents(); + }, + onHugeMutations: () => { + discoverNodes(root); + variablesStore.matchVariablesAndDependents(); + } + }); + treeObservers.set(root, treeObserver); + let attemptCount = 0; + let start = null; + const ATTEMPTS_INTERVAL = getDuration({seconds: 10}); + const RETRY_TIMEOUT = getDuration({seconds: 2}); + const MAX_ATTEMPTS_COUNT = 50; + let cache = []; + let timeoutId = null; + const handleAttributeMutations = throttle((mutations) => { + const handledTargets = new Set(); + mutations.forEach((m) => { + const target = m.target; + if (handledTargets.has(target)) { + return; + } + if (INLINE_STYLE_ATTRS.includes(m.attributeName)) { + handledTargets.add(target); + elementStyleDidChange(target); + } + }); + variablesStore.matchVariablesAndDependents(); + }); + const attrObserver = new MutationObserver((mutations) => { + if (timeoutId) { + cache.push(...mutations); + return; + } + attemptCount++; + const now = Date.now(); + if (start == null) { + start = now; + } else if (attemptCount >= MAX_ATTEMPTS_COUNT) { + if (now - start < ATTEMPTS_INTERVAL) { + timeoutId = setTimeout(() => { + start = null; + attemptCount = 0; + timeoutId = null; + const attributeCache = cache; + cache = []; + handleAttributeMutations(attributeCache); + }, RETRY_TIMEOUT); + cache.push(...mutations); + return; + } + start = now; + attemptCount = 1; + } + handleAttributeMutations(mutations); + }); + attrObserver.observe(root, { + attributes: true, + attributeFilter: INLINE_STYLE_ATTRS.concat( + overridesList.map(({dataAttr}) => dataAttr) + ), + subtree: true + }); + attrObservers.set(root, attrObserver); +} +function stopWatchingForInlineStyles() { + asyncCancelled = true; + treeObservers.forEach((o) => o.disconnect()); + attrObservers.forEach((o) => o.disconnect()); + treeObservers.clear(); + attrObservers.clear(); + inlineStringValueCache.clear(); +} +const inlineStyleCache = new WeakMap(); +const svgInversionCache = new WeakSet(); +const svgAnalysisConditionCache = new WeakMap(); +const themeProps = ["brightness", "contrast", "grayscale", "sepia", "mode"]; +function shouldAnalyzeSVGAsImage(svg) { + if (svgAnalysisConditionCache.has(svg)) { + return svgAnalysisConditionCache.get(svg); + } + const shouldAnalyze = Boolean( + svg && + (svg.getAttribute("class")?.includes("logo") || + svg.parentElement?.getAttribute("class")?.includes("logo")) + ); + svgAnalysisConditionCache.set(svg, shouldAnalyze); + return shouldAnalyze; +} +let prevTheme$1 = null; +let themeKey = ""; +function getThemeKey(theme) { + if (theme === prevTheme$1) { + return themeKey; + } + themeKey = ""; + for (let i = 0; i < themeProps.length; i++) { + const prop = themeProps[i]; + themeKey += `${prop}="${theme[prop]}" `; + } + prevTheme$1 = theme; + return themeKey; +} +function getInlineStyleCacheKey(el, theme) { + const attrKey = INLINE_STYLE_ATTRS.map( + (attr) => `${attr}="${el.getAttribute(attr)}"` + ); + return `${attrKey} ${getThemeKey(theme)}`; +} +function shouldIgnoreInlineStyle(element, selectors) { + for (let i = 0, len = selectors.length; i < len; i++) { + const ingnoredSelector = selectors[i]; + if (element.matches(ingnoredSelector)) { + return true; + } + } + return false; +} +const LOOP_DETECTION_THRESHOLD = 1000; +const MAX_LOOP_CYCLES = 10; +const elementsLastChanges = new WeakMap(); +const elementsLoopCycles = new WeakMap(); +const SMALL_SVG_THRESHOLD = 32; +const svgNodesRoots = new WeakMap(); +const svgRootSizeTestResults = new WeakMap(); +function getSVGElementRoot(svgElement) { + if (!svgElement) { + return null; + } + if (svgNodesRoots.has(svgElement)) { + return svgNodesRoots.get(svgElement); + } + if (svgElement instanceof SVGSVGElement) { + return svgElement; + } + const parent = svgElement.parentNode; + const root = getSVGElementRoot(parent); + svgNodesRoots.set(svgElement, root); + return root; +} +const inlineStringValueCache = new Map(); +function overrideInlineStyle( + element, + theme, + ignoreInlineSelectors, + ignoreImageSelectors +) { + if (elementsLastChanges.has(element)) { + if ( + Date.now() - elementsLastChanges.get(element) < + LOOP_DETECTION_THRESHOLD + ) { + const cycles = elementsLoopCycles.get(element) ?? 0; + elementsLoopCycles.set(element, cycles + 1); + } else { + elementsLoopCycles.delete(element); + } + if ((elementsLoopCycles.get(element) ?? 0) >= MAX_LOOP_CYCLES) { + return; + } + } + if (element.parentElement?.dataset.nodeViewContent) { + return; + } + const cacheKey = getInlineStyleCacheKey(element, theme); + if (cacheKey === inlineStyleCache.get(element)) { + return; + } + const unsetProps = new Set(Object.keys(overrides)); + function setCustomProp(targetCSSProp, modifierCSSProp, cssVal) { + const cachedStringValue = inlineStringValueCache + .get(modifierCSSProp) + ?.get(cssVal); + if (cachedStringValue) { + setStaticValue(cachedStringValue); + return; + } + asyncCancelled = false; + const mod = getModifiableCSSDeclaration( + modifierCSSProp, + cssVal, + {style: element.style}, + variablesStore, + ignoreImageSelectors, + isAsyncCancelled + ); + if (!mod) { + return; + } + function setStaticValue(value) { + const {customProp, dataAttr} = + overrides[targetCSSProp] ?? shorthandOverrides[targetCSSProp]; + element.style.setProperty(customProp, value); + if (!element.hasAttribute(dataAttr)) { + element.setAttribute(dataAttr, ""); + } + unsetProps.delete(targetCSSProp); + } + function setVarDeclaration(mod) { + let prevDeclarations = []; + function setProps(declarations) { + prevDeclarations.forEach(({property}) => { + element.style.removeProperty(property); + }); + declarations.forEach(({property, value}) => { + if (!(value instanceof Promise)) { + element.style.setProperty(property, value); + } + }); + prevDeclarations = declarations; + } + setProps(mod.declarations); + mod.onTypeChange.addListener(setProps); + } + function setAsyncValue(promise, sourceValue) { + promise.then((value) => { + if ( + value && + targetCSSProp === "background" && + value.startsWith("var(--darkreader-bg--") + ) { + setStaticValue(value); + } + if (value && targetCSSProp === "background-image") { + if ( + (element === document.documentElement || + element === document.body) && + value === sourceValue + ) { + value = "none"; + } + setStaticValue(value); + } + inlineStyleCache.set( + element, + getInlineStyleCacheKey(element, theme) + ); + }); + } + const value = + typeof mod.value === "function" ? mod.value(theme) : mod.value; + if (typeof value === "string") { + setStaticValue(value); + if (!inlineStringValueCache.has(modifierCSSProp)) { + inlineStringValueCache.set(modifierCSSProp, new Map()); + } + inlineStringValueCache.get(modifierCSSProp).set(cssVal, value); + } else if (value instanceof Promise) { + setAsyncValue(value, cssVal); + } else if (typeof value === "object") { + setVarDeclaration(value); + } + } + if (ignoreInlineSelectors.length > 0) { + if (shouldIgnoreInlineStyle(element, ignoreInlineSelectors)) { + unsetProps.forEach((cssProp) => { + element.removeAttribute(overrides[cssProp].dataAttr); + }); + return; + } + } + const isSVGElement = element instanceof SVGElement; + const svg = isSVGElement + ? (element.ownerSVGElement ?? + (element instanceof SVGSVGElement ? element : null)) + : null; + if (isSVGElement && theme.mode === 1 && svg) { + if (svgInversionCache.has(svg)) { + return; + } + if (shouldAnalyzeSVGAsImage(svg)) { + svgInversionCache.add(svg); + const analyzeSVGAsImage = () => { + let svgString = svg.outerHTML; + svgString = svgString.replaceAll( + '', + "" + ); + const dataURL = `data:image/svg+xml;base64,${btoa(svgString)}`; + getImageDetails(dataURL).then((details) => { + if ( + (details.isDark && details.isTransparent) || + (details.isLarge && + details.isLight && + !details.isTransparent) + ) { + svg.setAttribute("data-darkreader-inline-invert", ""); + } else { + svg.removeAttribute("data-darkreader-inline-invert"); + } + }); + }; + analyzeSVGAsImage(); + if (!isDOMReady()) { + addDOMReadyListener(analyzeSVGAsImage); + } + return; + } + } + if (element.hasAttribute("bgcolor")) { + let value = element.getAttribute("bgcolor"); + if (value.match(/^[0-9a-f]{3}$/i) || value.match(/^[0-9a-f]{6}$/i)) { + value = `#${value}`; + } + setCustomProp("background-color", "background-color", value); + } + if ( + (element === document.documentElement || element === document.body) && + element.hasAttribute("background") && + element.getAttribute("background") !== "" + ) { + const url = getAbsoluteURL( + location.href, + element.getAttribute("background") ?? "" + ); + const value = `url("${url}")`; + setCustomProp("background-image", "background-image", value); + } + if (element.hasAttribute("color") && element.rel !== "mask-icon") { + let value = element.getAttribute("color"); + if (value.match(/^[0-9a-f]{3}$/i) || value.match(/^[0-9a-f]{6}$/i)) { + value = `#${value}`; + } else if (value.match(/^#?[0-9a-f]{4}$/i)) { + const hex = value.startsWith("#") ? value.substring(1) : value; + value = `#${hex}00`; + } + setCustomProp("color", "color", value); + } + if (isSVGElement) { + if (element.hasAttribute("fill")) { + const value = element.getAttribute("fill"); + if (value !== "none" && value !== "currentColor") { + if (!(element instanceof SVGTextElement)) { + const handleSVGElement = () => { + let isSVGSmall = false; + const root = getSVGElementRoot(element); + if (!root) { + return; + } + if (svgRootSizeTestResults.has(root)) { + isSVGSmall = svgRootSizeTestResults.get(root); + } else { + const svgBounds = root.getBoundingClientRect(); + isSVGSmall = + svgBounds.width * svgBounds.height <= + SMALL_SVG_THRESHOLD ** 2; + svgRootSizeTestResults.set(root, isSVGSmall); + } + let isBg; + if (isSVGSmall) { + isBg = false; + } else { + const {width, height} = + element.getBoundingClientRect(); + isBg = + width > SMALL_SVG_THRESHOLD || + height > SMALL_SVG_THRESHOLD; + } + setCustomProp( + "fill", + isBg ? "background-color" : "color", + value + ); + }; + if (isReadyStateComplete()) { + handleSVGElement(); + } else { + addReadyStateCompleteListener(handleSVGElement); + } + } else { + setCustomProp("fill", "color", value); + } + } + } + if (element.hasAttribute("stop-color")) { + setCustomProp( + "stop-color", + "background-color", + element.getAttribute("stop-color") + ); + } + } + if (element.hasAttribute("stroke")) { + const value = element.getAttribute("stroke"); + setCustomProp( + "stroke", + element instanceof SVGLineElement || + element instanceof SVGTextElement + ? "border-color" + : "color", + value + ); + } + element.style && + iterateCSSDeclarations(element.style, (property, value) => { + if (property === "background-image" && value.includes("url")) { + if ( + element === document.documentElement || + element === document.body + ) { + setCustomProp(property, property, value); + } + return; + } + if ( + overrides.hasOwnProperty(property) || + (property.startsWith("--") && !normalizedPropList[property]) + ) { + setCustomProp(property, property, value); + } else if (shorthandOverrides[property] && value.includes("var(")) { + setCustomProp(property, property, value); + } else { + const overriddenProp = normalizedPropList[property]; + if ( + overriddenProp && + !element.style.getPropertyValue(overriddenProp) && + !element.hasAttribute(overriddenProp) + ) { + if ( + overriddenProp === "background-color" && + element.hasAttribute("bgcolor") + ) { + return; + } + element.style.setProperty(property, ""); + } + } + }); + if ( + element.style && + element instanceof SVGTextElement && + element.style.fill + ) { + setCustomProp("fill", "color", element.style.getPropertyValue("fill")); + } + if (element.getAttribute("style")?.includes("--")) { + variablesStore.addInlineStyleForMatching(element.style); + } + forEach(unsetProps, (cssProp) => { + element.removeAttribute(overrides[cssProp].dataAttr); + }); + inlineStyleCache.set(element, getInlineStyleCacheKey(element, theme)); + elementsLastChanges.set(element, Date.now()); +} + +const metaThemeColorName = "theme-color"; +const metaThemeColorSelector = `meta[name="${metaThemeColorName}"]`; +let srcMetaThemeColor = null; +let observer = null; +function changeMetaThemeColor(meta, theme) { + srcMetaThemeColor = srcMetaThemeColor || meta.content; + const color = parseColorWithCache(srcMetaThemeColor); + if (!color) { + logWarn("Invalid meta color", color); + return; + } + meta.content = modifyBackgroundColor(color, theme, false); +} +function changeMetaThemeColorWhenAvailable(theme) { + const meta = document.querySelector(metaThemeColorSelector); + if (meta) { + changeMetaThemeColor(meta, theme); + } else { + if (observer) { + observer.disconnect(); + } + observer = new MutationObserver((mutations) => { + loop: for (let i = 0; i < mutations.length; i++) { + const {addedNodes} = mutations[i]; + for (let j = 0; j < addedNodes.length; j++) { + const node = addedNodes[j]; + if ( + node instanceof HTMLMetaElement && + node.name === metaThemeColorName + ) { + observer.disconnect(); + observer = null; + changeMetaThemeColor(node, theme); + break loop; + } + } + } + }); + observer.observe(document.head, {childList: true}); + } +} +function restoreMetaThemeColor() { + if (observer) { + observer.disconnect(); + observer = null; + } + const meta = document.querySelector(metaThemeColorSelector); + if (meta && srcMetaThemeColor) { + meta.content = srcMetaThemeColor; + } +} + +const filterSelectors = { + invert: new Set(), + dim: new Set(), + none: new Set() +}; +function addFilterSelector(selector, type) { + if (!selector) { + return; + } + const selectors = filterSelectors[type]; + let changed = false; + selector.split(",").forEach((part) => { + const s = part.trim(); + if (!s || selectors.has(s)) { + return; + } + for (const existing of selectors) { + if (isSelectorWithin(s, existing)) { + return; + } + } + for (const existing of [...selectors]) { + if (isSelectorWithin(existing, s)) { + selectors.delete(existing); + } + } + selectors.add(s); + changed = true; + }); + return changed; +} +function isSelectorWithin(sub, parent) { + const parentLength = parent.length; + const subLength = sub.length; + if (subLength < parentLength || !sub.startsWith(parent)) { + return false; + } + if (subLength === parentLength) { + return true; + } + let i = parentLength; + const c = sub[i]; + if (c === "." || c === ":" || c === "#" || c === "[" || c === ">") { + return true; + } + if (c === "+" || c === "~" || c !== " ") { + return false; + } + while (sub[i] === " ") { + i++; + } + return sub[i] !== "+" && sub[i] !== "~"; +} +function cleanFilterSelectors() { + filterSelectors.invert.clear(); + filterSelectors.dim.clear(); + filterSelectors.none.clear(); +} + +const cssCommentsRegex = /\/\*[\s\S]*?\*\//g; +function removeCSSComments(cssText) { + return cssText.replace(cssCommentsRegex, ""); +} + +let canUseSheetProxy = false; +document.addEventListener( + "__darkreader__inlineScriptsAllowed", + () => (canUseSheetProxy = true), + {once: true} +); +function createSheetWatcher(element, safeGetSheetRules, callback, isCancelled) { + let rafSheetWatcher = null; + function watchForSheetChanges() { + watchForSheetChangesUsingProxy(); + if (!(canUseSheetProxy && element.sheet)) { + rafSheetWatcher = createRAFSheetWatcher( + element, + safeGetSheetRules, + callback, + isCancelled + ); + rafSheetWatcher.start(); + } + } + let areSheetChangesPending = false; + function onSheetChange() { + canUseSheetProxy = true; + rafSheetWatcher?.stop(); + if (areSheetChangesPending) { + return; + } + function handleSheetChanges() { + areSheetChangesPending = false; + if (isCancelled()) { + return; + } + callback(); + } + areSheetChangesPending = true; + queueMicrotask(handleSheetChanges); + } + function watchForSheetChangesUsingProxy() { + element.addEventListener("__darkreader__updateSheet", onSheetChange); + } + function stopWatchingForSheetChangesUsingProxy() { + element.removeEventListener("__darkreader__updateSheet", onSheetChange); + } + function stopWatchingForSheetChanges() { + stopWatchingForSheetChangesUsingProxy(); + rafSheetWatcher?.stop(); + } + return { + start: watchForSheetChanges, + stop: stopWatchingForSheetChanges + }; +} +function createRAFSheetWatcher( + element, + safeGetSheetRules, + callback, + isCancelled +) { + let rulesChangeKey = null; + let rulesCheckFrameId = null; + function getRulesChangeKey() { + const rules = safeGetSheetRules(); + return rules ? rules.length : null; + } + function didRulesKeyChange() { + return getRulesChangeKey() !== rulesChangeKey; + } + function watchForSheetChangesUsingRAF() { + rulesChangeKey = getRulesChangeKey(); + stopWatchingForSheetChangesUsingRAF(); + const checkForUpdate = () => { + const cancelled = isCancelled(); + if (!cancelled && didRulesKeyChange()) { + rulesChangeKey = getRulesChangeKey(); + callback(); + } + if (cancelled || (canUseSheetProxy && element.sheet)) { + stopWatchingForSheetChangesUsingRAF(); + return; + } + rulesCheckFrameId = requestAnimationFrame(checkForUpdate); + }; + checkForUpdate(); + } + function stopWatchingForSheetChangesUsingRAF() { + rulesCheckFrameId && cancelAnimationFrame(rulesCheckFrameId); + } + return { + start: watchForSheetChangesUsingRAF, + stop: stopWatchingForSheetChangesUsingRAF + }; +} + +const STYLE_SELECTOR = 'style, link[rel*="stylesheet" i]:not([disabled])'; +let ignoredCSSURLPatterns = []; +function setIgnoredCSSURLs(patterns) { + ignoredCSSURLPatterns = patterns || []; +} +function shouldIgnoreCSSURL(url) { + if (!url || ignoredCSSURLPatterns.length === 0) { + return false; + } + for (const pattern of ignoredCSSURLPatterns) { + if (pattern.startsWith("^")) { + if (url.startsWith(pattern.slice(1))) { + return true; + } + } else if (pattern.endsWith("$")) { + if (url.endsWith(pattern.slice(0, -1))) { + return true; + } + } else if (url.includes(pattern)) { + return true; + } + } + return false; +} +const hostsBreakingOnSVGStyleOverride = [ + "account.containerstore.com", + "containerstore.com", + "www.onet.pl" +]; +function shouldManageStyle(element) { + return ( + (element instanceof HTMLStyleElement || + (element instanceof SVGStyleElement && + !hostsBreakingOnSVGStyleOverride.includes(location.hostname)) || + (element instanceof HTMLLinkElement && + Boolean(element.rel) && + element.rel.toLowerCase().includes("stylesheet") && + Boolean(element.href) && + !element.disabled && + (isFirefox + ? !element.href.startsWith("moz-extension://") + : true) && + !shouldIgnoreCSSURL(element.href))) && + !element.classList.contains("darkreader") && + !ignoredMedia.includes(element.media.toLowerCase()) && + !element.classList.contains("stylus") + ); +} +function getManageableStyles(node, results = [], deep = true) { + if (shouldManageStyle(node)) { + results.push(node); + } else if ( + node instanceof Element || + (isShadowDomSupported && node instanceof ShadowRoot) || + node === document + ) { + forEach(node.querySelectorAll(STYLE_SELECTOR), (style) => + getManageableStyles(style, results, false) + ); + if (deep && (node.children?.length > 0 || node.shadowRoot)) { + iterateShadowHosts(node, (host) => + getManageableStyles(host.shadowRoot, results, false) + ); + } + } + return results; +} +const syncStyleSet = new WeakSet(); +const corsCopies = new WeakMap(); +const corsCopiesTextLengths = new WeakMap(); +let loadingLinkCounter = 0; +const rejectorsForLoadingLinks = new Map(); +function cleanLoadingLinks() { + rejectorsForLoadingLinks.clear(); +} +function manageStyle(element, {update, loadingStart, loadingEnd}) { + const inMode = getStyleInjectionMode(); + let syncStyle = null; + if (inMode === "next") { + const prevStyles = []; + let next = element; + while ( + (next = next.nextElementSibling) && + next.matches(".darkreader") + ) { + prevStyles.push(next); + } + syncStyle = + prevStyles.find( + (el) => el.matches(".darkreader--sync") && !syncStyleSet.has(el) + ) || null; + } + let syncStylePositionWatcher = null; + let cancelAsyncOperations = false; + let isOverrideEmpty = true; + const isAsyncCancelled = () => cancelAsyncOperations; + const sheetModifier = createStyleSheetModifier(); + const observer = new MutationObserver((mutations) => { + if ( + mutations.some((m) => m.type === "characterData") && + containsCSSImport() + ) { + const cssText = (element.textContent ?? "").trim(); + createOrUpdateCORSCopy(cssText, location.href).then(update); + } else { + update(); + } + }); + const observerOptions = { + attributes: true, + childList: true, + subtree: true, + characterData: true + }; + function containsCSSImport() { + if (!(element instanceof HTMLStyleElement)) { + return false; + } + const cssText = removeCSSComments(element.textContent ?? "").trim(); + return cssText.match(cssImportRegex); + } + function hasImports(cssRules, checkCrossOrigin) { + let result = false; + if (cssRules) { + let rule; + cssRulesLoop: for (let i = 0, len = cssRules.length; i < len; i++) { + rule = cssRules[i]; + if (rule.href) { + if (checkCrossOrigin) { + if ( + !rule.href.startsWith( + "https://fonts.googleapis.com/" + ) && + rule.href.startsWith("http") && + !rule.href.startsWith(location.origin) + ) { + result = true; + break cssRulesLoop; + } + } else { + result = true; + break cssRulesLoop; + } + } + } + } + return result; + } + function getRulesSync() { + if (corsCopies.has(element)) { + return corsCopies.get(element).cssRules; + } + if (containsCSSImport()) { + return null; + } + const cssRules = safeGetSheetRules(); + if ( + element instanceof HTMLLinkElement && + !isRelativeHrefOnAbsolutePath(element.href) && + hasImports(cssRules, false) + ) { + return null; + } + if (hasImports(cssRules, true)) { + return null; + } + !cssRules && logWarn("[getRulesSync] cssRules is null, trying again."); + return cssRules; + } + function insertStyle() { + if (inMode === "next") { + if (element.nextSibling !== syncStyle) { + element.parentNode.insertBefore(syncStyle, element.nextSibling); + } + } else if (inMode === "away") { + injectStyleAway(syncStyle); + } + } + function createSyncStyle() { + syncStyle = + element instanceof SVGStyleElement + ? document.createElementNS( + "http://www.w3.org/2000/svg", + "style" + ) + : document.createElement("style"); + syncStyle.classList.add("darkreader"); + syncStyle.classList.add("darkreader--sync"); + syncStyle.media = "screen"; + if (element.title) { + syncStyle.title = element.title; + } + syncStyleSet.add(syncStyle); + } + let isLoadingRules = false; + let wasLoadingError = false; + const loadingLinkId = ++loadingLinkCounter; + async function getRulesAsync() { + let cssText; + let cssBasePath; + if (element instanceof HTMLLinkElement) { + let [cssRules, accessError] = getRulesOrError(); + if (accessError) { + logWarn(accessError); + } + if ( + (isSafari && !element.sheet) || + (!isSafari && !cssRules && !accessError) || + isStillLoadingError(accessError) + ) { + try { + logInfo( + `Linkelement ${loadingLinkId} is not loaded yet and thus will be await for`, + element + ); + await linkLoading(element, loadingLinkId); + } catch (err) { + logWarn(err); + wasLoadingError = true; + } + if (cancelAsyncOperations) { + return null; + } + [cssRules, accessError] = getRulesOrError(); + if (accessError) { + logWarn(accessError); + } + } + if (cssRules) { + if (!hasImports(cssRules, false)) { + return cssRules; + } + } + try { + cssText = await loadText(element.href); + } catch (err) { + logWarn(err); + cssText = ""; + } + cssBasePath = getCSSBaseBath(element.href); + if (cancelAsyncOperations) { + return null; + } + } else if (containsCSSImport()) { + cssText = element.textContent.trim(); + cssBasePath = getCSSBaseBath(location.href); + } else { + return null; + } + await createOrUpdateCORSCopy(cssText, cssBasePath); + if (corsCopies.has(element)) { + return corsCopies.get(element).cssRules; + } + return null; + } + async function createOrUpdateCORSCopy(cssText, cssBasePath) { + if (cssText) { + try { + const fullCSSText = await replaceCSSImports( + cssText, + cssBasePath + ); + if (corsCopies.has(element)) { + if ( + (corsCopiesTextLengths.get(element) ?? 0) < + fullCSSText.length + ) { + corsCopies.get(element).replaceSync(fullCSSText); + corsCopiesTextLengths.set(element, fullCSSText.length); + } + } else { + const corsCopy = new CSSStyleSheet(); + corsCopy.replaceSync(fullCSSText); + corsCopies.set(element, corsCopy); + } + } catch (err) { + logWarn(err); + } + } + } + function details(options) { + const rules = getRulesSync(); + if (!rules) { + if (options.secondRound) { + logWarn( + "Detected dead-lock at details(), returning early to prevent it." + ); + return null; + } + if (isLoadingRules || wasLoadingError) { + return null; + } + isLoadingRules = true; + loadingStart(); + getRulesAsync() + .then((results) => { + isLoadingRules = false; + loadingEnd(); + if (results) { + update(); + } + }) + .catch((err) => { + logWarn(err); + isLoadingRules = false; + loadingEnd(); + }); + return null; + } + return {rules}; + } + let forceRenderStyle = false; + function render(theme, ignoreImageAnalysis) { + const rules = getRulesSync(); + if (!rules) { + return; + } + cancelAsyncOperations = false; + function removeCSSRulesFromSheet(sheet) { + if (!sheet) { + return; + } + for (let i = sheet.cssRules.length - 1; i >= 0; i--) { + sheet.deleteRule(i); + } + } + function prepareOverridesSheet() { + if (!syncStyle) { + createSyncStyle(); + } + syncStylePositionWatcher && syncStylePositionWatcher.stop(); + insertStyle(); + if (syncStyle.sheet == null) { + syncStyle.textContent = ""; + } + const sheet = syncStyle.sheet; + removeCSSRulesFromSheet(sheet); + if (syncStylePositionWatcher) { + syncStylePositionWatcher.run(); + } else if (inMode === "next") { + syncStylePositionWatcher = watchForNodePosition( + syncStyle, + "prev-sibling", + () => { + forceRenderStyle = true; + buildOverrides(); + } + ); + } + return syncStyle.sheet; + } + function buildOverrides() { + const force = forceRenderStyle; + forceRenderStyle = false; + sheetModifier.modifySheet({ + prepareSheet: prepareOverridesSheet, + sourceCSSRules: rules, + theme, + ignoreImageAnalysis, + force, + isAsyncCancelled + }); + isOverrideEmpty = + !syncStyle.sheet || syncStyle.sheet.cssRules.length === 0; + if (sheetModifier.shouldRebuildStyle()) { + addReadyStateCompleteListener(() => update()); + } + } + buildOverrides(); + } + function getRulesOrError() { + try { + if (element.sheet == null) { + return [null, null]; + } + return [element.sheet.cssRules, null]; + } catch (err) { + return [null, err]; + } + } + function isStillLoadingError(error) { + return error && error.message && error.message.includes("loading"); + } + function safeGetSheetRules() { + const [cssRules, err] = getRulesOrError(); + if (err) { + logWarn(err); + return null; + } + return cssRules; + } + const sheetChangeWatcher = createSheetWatcher( + element, + safeGetSheetRules, + update, + isAsyncCancelled + ); + function pause() { + observer.disconnect(); + cancelAsyncOperations = true; + syncStylePositionWatcher && syncStylePositionWatcher.stop(); + sheetChangeWatcher.stop(); + } + function destroy() { + pause(); + corsCopies.delete(element); + removeNode(syncStyle); + loadingEnd(); + if (rejectorsForLoadingLinks.has(loadingLinkId)) { + const reject = rejectorsForLoadingLinks.get(loadingLinkId); + rejectorsForLoadingLinks.delete(loadingLinkId); + reject && reject(); + } + } + function watch() { + observer.observe(element, observerOptions); + if (element instanceof HTMLStyleElement) { + sheetChangeWatcher.start(); + } + } + const maxMoveCount = 10; + let moveCount = 0; + function restore() { + if (!syncStyle) { + return; + } + moveCount++; + if (moveCount > maxMoveCount) { + logWarn("Style sheet was moved multiple times", element); + return; + } + logWarn("Restore style", syncStyle, element); + insertStyle(); + syncStylePositionWatcher && syncStylePositionWatcher.skip(); + if (!isOverrideEmpty) { + forceRenderStyle = true; + update(); + } + } + return { + details, + render, + pause, + destroy, + watch, + restore + }; +} +async function linkLoading(link, loadingId) { + return new Promise((resolve, reject) => { + const cleanUp = () => { + link.removeEventListener("load", onLoad); + link.removeEventListener("error", onError); + rejectorsForLoadingLinks.delete(loadingId); + }; + const onLoad = () => { + cleanUp(); + resolve(); + }; + const onError = () => { + cleanUp(); + reject(`Linkelement ${loadingId} couldn't be loaded. ${link.href}`); + }; + rejectorsForLoadingLinks.set(loadingId, () => { + cleanUp(); + reject(); + }); + link.addEventListener("load", onLoad, {passive: true}); + link.addEventListener("error", onError, {passive: true}); + if (!link.href) { + onError(); + } + }); +} +function getCSSImportURL(importDeclaration) { + return getCSSURLValue( + importDeclaration + .substring(7) + .trim() + .replace(/;$/, "") + .replace(/screen$/, "") + ); +} +async function loadText(url) { + if (url.startsWith("data:")) { + return await (await fetch(url)).text(); + } + const cache = readCSSFetchCache(url); + if (cache) { + return cache; + } + const parsedURL = new URL(url); + let text; + if (parsedURL.origin === location.origin) { + text = await loadAsText(url, "text/css", location.origin); + } else { + text = await bgFetch({ + url, + responseType: "text", + mimeType: "text/css", + origin: location.origin + }); + } + if (parsedURL.origin === location.origin) { + writeCSSFetchCache(url, text); + } + return text; +} +const MAX_CSS_IMPORT_DEPTH = 32; +async function replaceCSSImports( + cssText, + basePath, + cache = new Map(), + depth = 0 +) { + if (depth > MAX_CSS_IMPORT_DEPTH) { + return ""; + } + cssText = removeCSSComments(cssText); + cssText = replaceCSSFontFace(cssText); + cssText = replaceCSSRelativeURLsWithAbsolute(cssText, basePath); + const importMatches = getMatchesWithOffsets(cssImportRegex, cssText); + let prev = null; + let shouldIgnoreImportsInBetween = false; + let diff = 0; + for (const match of importMatches) { + let importedCSS; + const prevImportEnd = prev ? prev.offset + prev.text.length : 0; + const nextImportStart = match.offset; + const openBraceIndex = cssText.indexOf("{", prevImportEnd); + const closeBraceIndex = cssText.indexOf("}", prevImportEnd); + if ( + shouldIgnoreImportsInBetween || + (openBraceIndex >= 0 && + openBraceIndex < nextImportStart && + closeBraceIndex >= 0 && + closeBraceIndex < nextImportStart) + ) { + shouldIgnoreImportsInBetween = true; + importedCSS = ""; + } else { + const importURL = getCSSImportURL(match.text); + const absoluteURL = getAbsoluteURL(basePath, importURL); + if (cache.has(absoluteURL)) { + importedCSS = cache.get(absoluteURL); + } else { + try { + importedCSS = await loadText(absoluteURL); + cache.set(absoluteURL, importedCSS); + importedCSS = await replaceCSSImports( + importedCSS, + getCSSBaseBath(absoluteURL), + cache, + depth + 1 + ); + } catch (err) { + logWarn(err); + importedCSS = ""; + } + } + } + cssText = + cssText.substring(0, match.offset + diff) + + importedCSS + + cssText.substring(match.offset + match.text.length + diff); + diff += importedCSS.length - match.text.length; + prev = match; + } + cssText = cssText.trim(); + return cssText; +} + +function injectProxy(enableStyleSheetsProxy, enableCustomElementRegistryProxy) { + document.dispatchEvent( + new CustomEvent("__darkreader__inlineScriptsAllowed") + ); + const cleaners = []; + function cleanUp() { + cleaners.forEach((clean) => clean()); + cleaners.splice(0); + } + function documentEventListener(type, listener, options) { + document.addEventListener(type, listener, options); + cleaners.push(() => document.removeEventListener(type, listener)); + } + function disableConflictingPlugins() { + const disableWPDarkMode = () => { + if (window?.WPDarkMode?.deactivate) { + window.WPDarkMode.deactivate(); + } + }; + disableWPDarkMode(); + } + documentEventListener("__darkreader__cleanUp", cleanUp); + documentEventListener( + "__darkreader__disableConflictingPlugins", + disableConflictingPlugins + ); + function overrideProperty(cls, prop, overrides) { + const proto = cls.prototype; + const oldDescriptor = Object.getOwnPropertyDescriptor(proto, prop); + if (!oldDescriptor) { + return; + } + const newDescriptor = {...oldDescriptor}; + Object.keys(overrides).forEach((key) => { + const factory = overrides[key]; + newDescriptor[key] = factory(oldDescriptor[key]); + }); + Object.defineProperty(proto, prop, newDescriptor); + cleaners.push(() => Object.defineProperty(proto, prop, oldDescriptor)); + } + function override(cls, prop, factory) { + overrideProperty(cls, prop, {value: factory}); + } + function isDRElement(element) { + return element?.classList?.contains("darkreader"); + } + function isDRSheet(sheet) { + return isDRElement(sheet.ownerNode); + } + const updateSheetEvent = new CustomEvent("__darkreader__updateSheet"); + const adoptedSheetChangeEvent = new CustomEvent( + "__darkreader__adoptedStyleSheetChange" + ); + const shadowDomAttachingEvent = new CustomEvent( + "__darkreader__shadowDomAttaching", + {bubbles: true} + ); + const adoptedSheetOwners = new WeakMap(); + const adoptedDeclarationSheets = new WeakMap(); + function onAdoptedSheetChange(sheet) { + const owners = adoptedSheetOwners.get(sheet); + owners?.forEach((node) => { + if (node.isConnected) { + node.dispatchEvent(adoptedSheetChangeEvent); + } else { + owners.delete(node); + } + }); + } + function reportSheetChange(sheet) { + if (sheet.ownerNode && !isDRSheet(sheet)) { + sheet.ownerNode.dispatchEvent(updateSheetEvent); + } + if (adoptedSheetOwners.has(sheet)) { + onAdoptedSheetChange(sheet); + } + } + function reportSheetChangeAsync(sheet, promise) { + const {ownerNode} = sheet; + if ( + ownerNode && + !isDRSheet(sheet) && + promise && + promise instanceof Promise + ) { + promise.then(() => ownerNode.dispatchEvent(updateSheetEvent)); + } + if (adoptedSheetOwners.has(sheet)) { + if (promise && promise instanceof Promise) { + promise.then(() => onAdoptedSheetChange(sheet)); + } + } + } + override( + CSSStyleSheet, + "addRule", + (native) => + function (selector, style, index) { + native.call(this, selector, style, index); + reportSheetChange(this); + return -1; + } + ); + override( + CSSStyleSheet, + "insertRule", + (native) => + function (rule, index) { + const returnValue = native.call(this, rule, index); + reportSheetChange(this); + return returnValue; + } + ); + override( + CSSStyleSheet, + "deleteRule", + (native) => + function (index) { + native.call(this, index); + reportSheetChange(this); + } + ); + override( + CSSStyleSheet, + "removeRule", + (native) => + function (index) { + native.call(this, index); + reportSheetChange(this); + } + ); + override( + CSSStyleSheet, + "replace", + (native) => + function (cssText) { + const returnValue = native.call(this, cssText); + reportSheetChangeAsync(this, returnValue); + return returnValue; + } + ); + override( + CSSStyleSheet, + "replaceSync", + (native) => + function (cssText) { + native.call(this, cssText); + reportSheetChange(this); + } + ); + override( + Element, + "attachShadow", + (native) => + function (options) { + this.dispatchEvent(shadowDomAttachingEvent); + return native.call(this, options); + } + ); + const shouldWrapHTMLElement = + location.hostname === "baidu.com" || + location.hostname.endsWith(".baidu.com"); + if (shouldWrapHTMLElement) { + override( + Element, + "getElementsByTagName", + (native) => + function (tagName) { + if (tagName !== "style") { + return native.call(this, tagName); + } + const getCurrentElementValue = () => { + const elements = native.call(this, tagName); + return Object.setPrototypeOf( + [...elements].filter( + (element) => element && !isDRElement(element) + ), + NodeList.prototype + ); + }; + let elements = getCurrentElementValue(); + const nodeListBehavior = { + get: function (_, property) { + return getCurrentElementValue()[ + Number(property) || property + ]; + } + }; + elements = new Proxy(elements, nodeListBehavior); + return elements; + } + ); + } + const shouldProxyChildNodes = ["brilliant.org", "www.vy.no"].includes( + location.hostname + ); + if (shouldProxyChildNodes) { + overrideProperty(Node, "childNodes", { + get: (native) => + function () { + const childNodes = native.call(this); + return Object.setPrototypeOf( + [...childNodes].filter((element) => { + return !isDRElement(element); + }), + NodeList.prototype + ); + } + }); + } + function resolveCustomElement(tag) { + customElements.whenDefined(tag).then(() => { + document.dispatchEvent( + new CustomEvent("__darkreader__isDefined", {detail: {tag}}) + ); + }); + } + documentEventListener("__darkreader__addUndefinedResolver", (e) => + resolveCustomElement(e.detail.tag) + ); + if (enableCustomElementRegistryProxy) { + override( + CustomElementRegistry, + "define", + (native) => + function (name, constructor, options) { + resolveCustomElement(name); + native.call(this, name, constructor, options); + } + ); + } + let blobURLAllowed = null; + function checkBlobURLSupport() { + if (blobURLAllowed != null) { + document.dispatchEvent( + new CustomEvent("__darkreader__blobURLCheckResponse", { + detail: {blobURLAllowed} + }) + ); + return; + } + const svg = + ''; + const bytes = new Uint8Array(svg.length); + for (let i = 0; i < svg.length; i++) { + bytes[i] = svg.charCodeAt(i); + } + const blob = new Blob([bytes], {type: "image/svg+xml"}); + const objectURL = URL.createObjectURL(blob); + const image = new Image(); + image.onload = () => { + blobURLAllowed = true; + sendBlobURLCheckResponse(); + }; + image.onerror = () => { + blobURLAllowed = false; + sendBlobURLCheckResponse(); + }; + image.src = objectURL; + } + function sendBlobURLCheckResponse() { + document.dispatchEvent( + new CustomEvent("__darkreader__blobURLCheckResponse", { + detail: {blobURLAllowed} + }) + ); + } + documentEventListener( + "__darkreader__blobURLCheckRequest", + checkBlobURLSupport + ); + if (enableStyleSheetsProxy) { + overrideProperty(Document, "styleSheets", { + get: (native) => + function () { + const getCurrentValue = () => { + const docSheets = native.call(this); + const filteredSheets = [...docSheets].filter( + (styleSheet) => + styleSheet.ownerNode && !isDRSheet(styleSheet) + ); + filteredSheets.item = (item) => filteredSheets[item]; + return Object.setPrototypeOf( + filteredSheets, + StyleSheetList.prototype + ); + }; + let elements = getCurrentValue(); + const styleSheetListBehavior = { + get: function (_, property) { + return getCurrentValue()[property]; + } + }; + elements = new Proxy(elements, styleSheetListBehavior); + return elements; + } + }); + } + const adoptedSheetsSourceProxies = new WeakMap(); + const adoptedSheetsProxySources = new WeakMap(); + const adoptedSheetsChangeEvent = new CustomEvent( + "__darkreader__adoptedStyleSheetsChange" + ); + const adoptedSheetOverrideCache = new WeakSet(); + const adoptedSheetsSnapshots = new WeakMap(); + const isDRAdoptedSheetOverride = (sheet) => { + if (!sheet || !sheet.cssRules) { + return false; + } + if (adoptedSheetOverrideCache.has(sheet)) { + return true; + } + if ( + sheet.cssRules.length > 0 && + sheet.cssRules[0].cssText.startsWith( + "#__darkreader__adoptedOverride" + ) + ) { + adoptedSheetOverrideCache.add(sheet); + return true; + } + return false; + }; + const areArraysEqual = (a, b) => { + return a.length === b.length && a.every((x, i) => x === b[i]); + }; + const onAdoptedSheetsChange = (node) => { + const prev = adoptedSheetsSnapshots.get(node); + const curr = (node.adoptedStyleSheets || []).filter( + (s) => !isDRAdoptedSheetOverride(s) + ); + adoptedSheetsSnapshots.set(node, curr); + if (!prev || !areArraysEqual(prev, curr)) { + curr.forEach((sheet) => { + if (!adoptedSheetOwners.has(sheet)) { + adoptedSheetOwners.set(sheet, new Set()); + } + adoptedSheetOwners.get(sheet).add(node); + for (const rule of sheet.cssRules) { + const declaration = rule.style; + if (declaration) { + adoptedDeclarationSheets.set(declaration, sheet); + } + } + }); + node.dispatchEvent(adoptedSheetsChangeEvent); + } + }; + const proxyAdoptedSheetsArray = (node, source) => { + if (adoptedSheetsProxySources.has(source)) { + return source; + } + if (adoptedSheetsSourceProxies.has(source)) { + return adoptedSheetsSourceProxies.get(source); + } + const proxy = new Proxy(source, { + deleteProperty(target, property) { + delete target[property]; + return true; + }, + set(target, property, value) { + target[property] = value; + if (property === "length") { + onAdoptedSheetsChange(node); + } + return true; + } + }); + adoptedSheetsSourceProxies.set(source, proxy); + adoptedSheetsProxySources.set(proxy, source); + return proxy; + }; + [Document, ShadowRoot].forEach((ctor) => { + overrideProperty(ctor, "adoptedStyleSheets", { + get: (native) => + function () { + const source = native.call(this); + return proxyAdoptedSheetsArray(this, source); + }, + set: (native) => + function (source) { + if (adoptedSheetsProxySources.has(source)) { + source = adoptedSheetsProxySources.get(source); + } + native.call(this, source); + onAdoptedSheetsChange(this); + } + }); + }); + const adoptedDeclarationChangeEvent = new CustomEvent( + "__darkreader__adoptedStyleDeclarationChange" + ); + ["setProperty", "removeProperty"].forEach((key) => { + override(CSSStyleDeclaration, key, (native) => { + return function (...args) { + const returnValue = native.apply(this, args); + const sheet = adoptedDeclarationSheets.get(this); + if (sheet) { + const owners = adoptedSheetOwners.get(sheet); + if (owners) { + owners.forEach((node) => { + node.dispatchEvent(adoptedDeclarationChangeEvent); + }); + } + } + return returnValue; + }; + }); + }); +} + +const definedCustomElements = new Set(); +const undefinedGroups = new Map(); +let elementsDefinitionCallback; +function isCustomElement(element) { + if (element.tagName.includes("-") || element.getAttribute("is")) { + return true; + } + return false; +} +function recordUndefinedElement(element) { + let tag = element.tagName.toLowerCase(); + if (!tag.includes("-")) { + const extendedTag = element.getAttribute("is"); + if (extendedTag) { + tag = extendedTag; + } else { + return; + } + } + if (!undefinedGroups.has(tag)) { + undefinedGroups.set(tag, new Set()); + customElementsWhenDefined(tag).then(() => { + if (elementsDefinitionCallback) { + const elements = undefinedGroups.get(tag); + undefinedGroups.delete(tag); + elementsDefinitionCallback(Array.from(elements)); + } + }); + } + undefinedGroups.get(tag).add(element); +} +function collectUndefinedElements(root) { + if (!isDefinedSelectorSupported) { + return; + } + forEach(root.querySelectorAll(":not(:defined)"), recordUndefinedElement); +} +let canOptimizeUsingProxy = false; +document.addEventListener( + "__darkreader__inlineScriptsAllowed", + () => { + canOptimizeUsingProxy = true; + }, + {once: true, passive: true} +); +const unhandledShadowHosts = new Set(); +document.addEventListener("__darkreader__shadowDomAttaching", (e) => { + const host = e.target; + if (unhandledShadowHosts.size === 0) { + queueMicrotask(() => { + const hosts = [...unhandledShadowHosts].filter( + (el) => el.shadowRoot + ); + elementsDefinitionCallback?.(hosts); + unhandledShadowHosts.clear(); + }); + } + unhandledShadowHosts.add(host); +}); +const resolvers = new Map(); +function handleIsDefined(e) { + canOptimizeUsingProxy = true; + const tag = e.detail.tag; + definedCustomElements.add(tag); + if (resolvers.has(tag)) { + const r = resolvers.get(tag); + resolvers.delete(tag); + r.forEach((r) => r()); + } +} +async function customElementsWhenDefined(tag) { + if (definedCustomElements.has(tag)) { + return; + } + return new Promise((resolve) => { + if ( + window.customElements && + typeof customElements.whenDefined === "function" + ) { + customElements.whenDefined(tag).then(() => resolve()); + } else if (canOptimizeUsingProxy) { + if (resolvers.has(tag)) { + resolvers.get(tag).push(resolve); + } else { + resolvers.set(tag, [resolve]); + } + document.dispatchEvent( + new CustomEvent("__darkreader__addUndefinedResolver", { + detail: {tag} + }) + ); + } else { + const checkIfDefined = () => { + const elements = undefinedGroups.get(tag); + if (elements && elements.size > 0) { + if (elements.values().next().value.matches(":defined")) { + resolve(); + } else { + requestAnimationFrame(checkIfDefined); + } + } + }; + requestAnimationFrame(checkIfDefined); + } + }); +} +function watchWhenCustomElementsDefined(callback) { + elementsDefinitionCallback = callback; +} +function unsubscribeFromDefineCustomElements() { + elementsDefinitionCallback = null; + undefinedGroups.clear(); + document.removeEventListener("__darkreader__isDefined", handleIsDefined); +} + +const observers = []; +let observedRoots; +let handledShadowHosts; +function watchForStylePositions(currentStyles, update, shadowRootDiscovered) { + stopWatchingForStylePositions(); + const prevStylesByRoot = new WeakMap(); + const getPrevStyles = (root) => { + if (!prevStylesByRoot.has(root)) { + prevStylesByRoot.set(root, new Set()); + } + return prevStylesByRoot.get(root); + }; + currentStyles.forEach((node) => { + let root = node; + while ((root = root.parentNode)) { + if ( + root === document || + root.nodeType === Node.DOCUMENT_FRAGMENT_NODE + ) { + const prevStyles = getPrevStyles(root); + prevStyles.add(node); + break; + } + } + }); + const prevStyleSiblings = new WeakMap(); + const nextStyleSiblings = new WeakMap(); + function saveStylePosition(style) { + prevStyleSiblings.set(style, style.previousElementSibling); + nextStyleSiblings.set(style, style.nextElementSibling); + } + function forgetStylePosition(style) { + prevStyleSiblings.delete(style); + nextStyleSiblings.delete(style); + } + function didStylePositionChange(style) { + return ( + style.previousElementSibling !== prevStyleSiblings.get(style) || + style.nextElementSibling !== nextStyleSiblings.get(style) + ); + } + currentStyles.forEach(saveStylePosition); + function handleStyleOperations(root, operations) { + const {createdStyles, removedStyles, movedStyles} = operations; + createdStyles.forEach((s) => saveStylePosition(s)); + movedStyles.forEach((s) => saveStylePosition(s)); + removedStyles.forEach((s) => forgetStylePosition(s)); + const prevStyles = getPrevStyles(root); + createdStyles.forEach((s) => prevStyles.add(s)); + removedStyles.forEach((s) => prevStyles.delete(s)); + if (createdStyles.size + removedStyles.size + movedStyles.size > 0) { + update({ + created: Array.from(createdStyles), + removed: Array.from(removedStyles), + moved: Array.from(movedStyles), + updated: [] + }); + } + } + function handleMinorTreeMutations(root, {additions, moves, deletions}) { + const createdStyles = new Set(); + const removedStyles = new Set(); + const movedStyles = new Set(); + additions.forEach((node) => + getManageableStyles(node).forEach((style) => + createdStyles.add(style) + ) + ); + deletions.forEach((node) => + getManageableStyles(node).forEach((style) => + removedStyles.add(style) + ) + ); + moves.forEach((node) => + getManageableStyles(node).forEach((style) => movedStyles.add(style)) + ); + handleStyleOperations(root, { + createdStyles, + removedStyles, + movedStyles + }); + const potentialHosts = new Set(); + additions.forEach((n) => { + if (n.parentElement) { + potentialHosts.add(n.parentElement); + } + if (n.previousElementSibling) { + potentialHosts.add(n.previousElementSibling); + } + deepObserve(n); + collectUndefinedElements(n); + }); + potentialHosts.forEach((el) => { + if (el.shadowRoot && !observedRoots.has(el)) { + subscribeForShadowRootChanges(el); + deepObserve(el.shadowRoot); + } + }); + additions.forEach( + (node) => isCustomElement(node) && recordUndefinedElement(node) + ); + additions.forEach((node) => checkImageSelectors(node)); + } + function handleHugeTreeMutations(root) { + const styles = new Set(getManageableStyles(root)); + const createdStyles = new Set(); + const removedStyles = new Set(); + const movedStyles = new Set(); + const prevStyles = getPrevStyles(root); + styles.forEach((s) => { + if (!prevStyles.has(s)) { + createdStyles.add(s); + } + }); + prevStyles.forEach((s) => { + if (!styles.has(s)) { + removedStyles.add(s); + } + }); + styles.forEach((s) => { + if ( + !createdStyles.has(s) && + !removedStyles.has(s) && + didStylePositionChange(s) + ) { + movedStyles.add(s); + } + }); + handleStyleOperations(root, { + createdStyles, + removedStyles, + movedStyles + }); + deepObserve(root); + collectUndefinedElements(root); + checkImageSelectors(root); + } + function handleAttributeMutations(mutations) { + const updatedStyles = new Set(); + const removedStyles = new Set(); + mutations.forEach((m) => { + const {target} = m; + if (target.isConnected) { + if (shouldManageStyle(target)) { + updatedStyles.add(target); + } else if ( + target instanceof HTMLLinkElement && + target.disabled + ) { + removedStyles.add(target); + } + } + }); + if (updatedStyles.size + removedStyles.size > 0) { + update({ + updated: Array.from(updatedStyles), + created: [], + removed: Array.from(removedStyles), + moved: [] + }); + } + } + function observe(root) { + if (observedRoots.has(root)) { + return; + } + const treeObserver = createOptimizedTreeObserver(root, { + onMinorMutations: handleMinorTreeMutations, + onHugeMutations: handleHugeTreeMutations + }); + const attrObserver = new MutationObserver(handleAttributeMutations); + attrObserver.observe(root, { + attributeFilter: ["rel", "disabled", "media", "href"], + subtree: true + }); + observers.push(treeObserver, attrObserver); + observedRoots.add(root); + } + function subscribeForShadowRootChanges(node) { + const {shadowRoot} = node; + if (shadowRoot == null || observedRoots.has(shadowRoot)) { + return; + } + observe(shadowRoot); + shadowRootDiscovered(shadowRoot); + } + function deepObserve(node) { + iterateShadowHosts(node, subscribeForShadowRootChanges); + } + observe(document); + deepObserve(document.documentElement); + watchWhenCustomElementsDefined((hosts) => { + hosts = hosts.filter((node) => !handledShadowHosts.has(node)); + const newStyles = []; + hosts.forEach((host) => + push(newStyles, getManageableStyles(host.shadowRoot)) + ); + update({created: newStyles, updated: [], removed: [], moved: []}); + hosts.forEach((host) => { + const {shadowRoot} = host; + if (shadowRoot == null) { + return; + } + subscribeForShadowRootChanges(host); + deepObserve(shadowRoot); + collectUndefinedElements(shadowRoot); + }); + hosts.forEach((node) => handledShadowHosts.add(node)); + }); + document.addEventListener("__darkreader__isDefined", handleIsDefined); + collectUndefinedElements(document); + addDOMReadyListener(() => { + forEach(document.body.children, (el) => { + if (el.shadowRoot && !observedRoots.has(el)) { + subscribeForShadowRootChanges(el); + deepObserve(el.shadowRoot); + } + }); + }); +} +function resetObservers() { + observers.forEach((o) => o.disconnect()); + observers.splice(0, observers.length); + observedRoots = new WeakSet(); + handledShadowHosts = new WeakSet(); +} +function stopWatchingForStylePositions() { + resetObservers(); + unsubscribeFromDefineCustomElements(); +} + +function watchForStyleChanges(currentStyles, update, shadowRootDiscovered) { + watchForStylePositions(currentStyles, update, shadowRootDiscovered); +} +function stopWatchingForStyleChanges() { + stopWatchingForStylePositions(); +} + +const INSTANCE_ID = generateUID(); +const styleManagers = new Map(); +const adoptedStyleManagers = []; +let theme = null; +let fixes = null; +let isIFrame$1 = null; +let ignoredImageAnalysisSelectors = []; +let ignoredInlineSelectors = []; +let staticStyleMap = new WeakMap(); +function createOrUpdateStyle(className, root = document.head || document) { + let element = root.querySelector(`.${className}`); + if (!staticStyleMap.has(root)) { + staticStyleMap.set(root, new Map()); + } + const classMap = staticStyleMap.get(root); + if (element) { + classMap.set(className, element); + } else if (classMap.has(className)) { + element = classMap.get(className); + } else { + element = document.createElement("style"); + element.classList.add("darkreader"); + element.classList.add(className); + element.media = "screen"; + element.textContent = ""; + classMap.set(className, element); + } + return element; +} +function createOrUpdateScript(className, root = document.head || document) { + let element = root.querySelector(`.${className}`); + if (!element) { + element = document.createElement("script"); + element.classList.add("darkreader"); + element.classList.add(className); + } + return element; +} +const nodePositionWatchers = new Map(); +function setupNodePositionWatcher(node, alias, callback) { + nodePositionWatchers.has(alias) && nodePositionWatchers.get(alias).stop(); + nodePositionWatchers.set( + alias, + watchForNodePosition(node, "head", callback) + ); +} +function stopStylePositionWatchers() { + forEach(nodePositionWatchers.values(), (watcher) => watcher.stop()); + nodePositionWatchers.clear(); +} +function injectStaticStyle(style, prevNode, watchAlias, callback) { + const mode = getStyleInjectionMode(); + if (mode === "next") { + document.head.insertBefore( + style, + prevNode ? prevNode.nextSibling : document.head.firstChild + ); + setupNodePositionWatcher(style, watchAlias, callback); + } else if (mode === "away") { + injectStyleAway(style); + } +} +const scheduleInversionStyleUpdate = throttle(() => { + const invertStyle = document.head?.querySelector(".darkreader--invert"); + if (invertStyle) { + setInversionStyleValue(invertStyle); + } + shadowRootsWithOverrides.forEach((root) => { + const shadowInvertStyle = root.querySelector(".darkreader--invert"); + if (shadowInvertStyle) { + setInversionStyleValue(shadowInvertStyle); + } + }); +}); +setFilterSelectorHandler((selector, type) => { + const changed = addFilterSelector(selector, type); + if (changed) { + scheduleInversionStyleUpdate(); + } +}); +function setInversionStyleValue(invertStyle) { + if (!theme) { + return; + } + const rules = []; + const appendRule = (selectors, filter) => { + if (!filter || selectors.length === 0) { + return; + } + rules.push( + [ + `${selectors.join(", ")} {`, + ` filter: ${filter} !important;`, + "}" + ].join("\n") + ); + }; + const appendCounterInversion = (selectors) => { + if (theme.mode === 0 || selectors.length === 0) { + return; + } + rules.push( + [ + `${selectors.join(", ")} {`, + ` color: black !important;`, + "}", + `${selectors.map((s) => `${s} > *`).join(", ")} {`, + ` filter: invert(100%) hue-rotate(180deg) !important;`, + "}" + ].join("\n") + ); + }; + const appendInversionCancellation = (selectors) => { + if (theme.mode === 0 || selectors.length === 0) { + return; + } + rules.push( + [ + `${selectors.join(", ")} {`, + ` filter: none !important;`, + ` color: var(--darkreader-neutral-text) !important;`, + "}", + `${selectors.map((s) => `${s} > *`).join(", ")} {`, + ` filter: none !important;`, + "}" + ].join("\n") + ); + }; + if ( + (fixes && Array.isArray(fixes.invert) && fixes.invert.length > 0) || + filterSelectors.invert.size > 0 + ) { + const extraInversionSelectors = [...filterSelectors.invert]; + const invertSelectors = [ + ...(fixes?.invert ?? []), + ...extraInversionSelectors + ]; + const invertFilter = getCSSFilterValue({ + ...theme, + contrast: + theme.mode === 0 + ? theme.contrast + : clamp(theme.contrast - 10, 0, 100) + }); + appendRule(invertSelectors, invertFilter); + appendCounterInversion(extraInversionSelectors); + if (filterSelectors.none.size > 0) { + const noneSelectors = [...filterSelectors.none]; + appendInversionCancellation(noneSelectors); + if (theme.mode === 1) { + const invertedChildSelectors = []; + noneSelectors.forEach((parent) => { + extraInversionSelectors.forEach((child) => + invertedChildSelectors.push(`${parent} > ${child}`) + ); + }); + appendRule(invertedChildSelectors, invertFilter); + } + } + } + if (filterSelectors.dim.size > 0) { + appendRule( + [...filterSelectors.dim], + getCSSFilterValue({ + ...theme, + brightness: clamp(theme.brightness - 10, 5, 200), + sepia: clamp(theme.sepia + 10, 0, 100) + }) + ); + } + invertStyle.textContent = rules.join("\n"); +} +function createStaticStyleOverrides() { + const fallbackStyle = createOrUpdateStyle("darkreader--fallback", document); + fallbackStyle.textContent = getModifiedFallbackStyle(theme, {strict: true}); + injectStaticStyle(fallbackStyle, null, "fallback"); + const userAgentStyle = createOrUpdateStyle("darkreader--user-agent"); + userAgentStyle.textContent = getModifiedUserAgentStyle( + theme, + isIFrame$1, + theme.styleSystemControls + ); + injectStaticStyle(userAgentStyle, fallbackStyle, "user-agent"); + const textStyle = createOrUpdateStyle("darkreader--text"); + if (theme.useFont || theme.textStroke > 0) { + textStyle.textContent = createTextStyle(theme); + } else { + textStyle.textContent = ""; + } + injectStaticStyle(textStyle, userAgentStyle, "text"); + const invertStyle = createOrUpdateStyle("darkreader--invert"); + setInversionStyleValue(invertStyle); + injectStaticStyle(invertStyle, textStyle, "invert"); + const inlineStyle = createOrUpdateStyle("darkreader--inline"); + inlineStyle.textContent = getInlineOverrideStyle(); + injectStaticStyle(inlineStyle, invertStyle, "inline"); + const variableStyle = createOrUpdateStyle("darkreader--variables"); + const selectionColors = theme?.selectionColor + ? getSelectionColor(theme) + : null; + const neutralBackgroundColor = modifyBackgroundColor( + parseColorWithCache("#ffffff"), + theme + ); + const neutralTextColor = modifyForegroundColor( + parseColorWithCache("#000000"), + theme + ); + variableStyle.textContent = [ + `:root {`, + ` --darkreader-neutral-background: ${neutralBackgroundColor};`, + ` --darkreader-neutral-text: ${neutralTextColor};`, + ` --darkreader-selection-background: ${selectionColors?.backgroundColorSelection ?? "initial"};`, + ` --darkreader-selection-text: ${selectionColors?.foregroundColorSelection ?? "initial"};`, + `}` + ].join("\n"); + injectStaticStyle(variableStyle, inlineStyle, "variables", () => + registerVariablesSheet(variableStyle.sheet) + ); + registerVariablesSheet(variableStyle.sheet); + const rootVarsStyle = createOrUpdateStyle("darkreader--root-vars"); + injectStaticStyle(rootVarsStyle, variableStyle, "root-vars"); + const enableStyleSheetsProxy = !(fixes && fixes.disableStyleSheetsProxy); + const enableCustomElementRegistryProxy = !( + fixes && fixes.disableCustomElementRegistryProxy + ); + document.dispatchEvent(new CustomEvent("__darkreader__cleanUp")); + { + const proxyScript = createOrUpdateScript("darkreader--proxy"); + proxyScript.append( + `(${injectProxy})(${enableStyleSheetsProxy}, ${enableCustomElementRegistryProxy})` + ); + document.head.insertBefore(proxyScript, rootVarsStyle.nextSibling); + proxyScript.remove(); + } + const overrideStyle = createOrUpdateStyle("darkreader--override"); + overrideStyle.textContent = + fixes && fixes.css ? replaceCSSTemplates(fixes.css) : ""; + injectStaticStyle(overrideStyle, document.head.lastChild, "override"); +} +const shadowRootsWithOverrides = new Set(); +function createShadowStaticStyleOverridesInner(root) { + const inlineStyle = createOrUpdateStyle("darkreader--inline", root); + inlineStyle.textContent = getInlineOverrideStyle(); + root.insertBefore(inlineStyle, root.firstChild); + const overrideStyle = createOrUpdateStyle("darkreader--override", root); + overrideStyle.textContent = + fixes && fixes.css ? replaceCSSTemplates(fixes.css) : ""; + root.insertBefore(overrideStyle, inlineStyle.nextSibling); + const invertStyle = createOrUpdateStyle("darkreader--invert", root); + setInversionStyleValue(invertStyle); + root.insertBefore(invertStyle, overrideStyle.nextSibling); + shadowRootsWithOverrides.add(root); +} +function delayedCreateShadowStaticStyleOverrides(root) { + const observer = new MutationObserver((mutations, observer) => { + observer.disconnect(); + for (const {type, removedNodes} of mutations) { + if (type === "childList") { + for (const {nodeName, className} of removedNodes) { + if ( + nodeName === "STYLE" && + [ + "darkreader darkreader--inline", + "darkreader darkreader--override", + "darkreader darkreader--invert" + ].includes(className) + ) { + createShadowStaticStyleOverridesInner(root); + return; + } + } + } + } + }); + observer.observe(root, {childList: true}); +} +function createShadowStaticStyleOverrides(root) { + const delayed = root.firstChild === null; + createShadowStaticStyleOverridesInner(root); + if (delayed) { + delayedCreateShadowStaticStyleOverrides(root); + } +} +function replaceCSSTemplates($cssText) { + return $cssText.replace(/\${(.+?)}/g, (_, $color) => { + const color = parseColorWithCache($color); + if (color) { + const lightness = getSRGBLightness(color.r, color.g, color.b); + if (lightness > 0.5) { + return modifyBackgroundColor(color, theme); + } + return modifyForegroundColor(color, theme); + } + logWarn("Couldn't parse CSSTemplate's color."); + return $color; + }); +} +function cleanFallbackStyle() { + const fallback = + staticStyleMap.get(document.head)?.get("darkreader--fallback") || + staticStyleMap.get(document)?.get("darkreader--fallback") || + document.querySelector(".darkreader--fallback"); + if (fallback) { + fallback.textContent = ""; + } +} +function createDynamicStyleOverrides() { + cancelRendering(); + const allStyles = getManageableStyles(document); + const newManagers = allStyles + .filter((style) => !styleManagers.has(style)) + .map((style) => createManager(style)); + newManagers + .map((manager) => manager.details({secondRound: false})) + .filter((detail) => detail && detail.rules.length > 0) + .forEach((detail) => { + variablesStore.addRulesForMatching(detail.rules); + }); + variablesStore.matchVariablesAndDependents(); + variablesStore.setOnRootVariableChange(() => { + const rootVarsStyle = createOrUpdateStyle("darkreader--root-vars"); + variablesStore.putRootVars(rootVarsStyle, theme); + }); + const rootVarsStyle = createOrUpdateStyle("darkreader--root-vars"); + variablesStore.putRootVars(rootVarsStyle, theme); + styleManagers.forEach((manager) => + manager.render(theme, ignoredImageAnalysisSelectors) + ); + if (loadingStyles.size === 0) { + cleanFallbackStyle(); + } + newManagers.forEach((manager) => manager.watch()); + const inlineStyleElements = toArray( + document.querySelectorAll(INLINE_STYLE_SELECTOR) + ); + iterateShadowHosts(document.documentElement, (host) => { + createShadowStaticStyleOverrides(host.shadowRoot); + const elements = host.shadowRoot.querySelectorAll( + INLINE_STYLE_SELECTOR + ); + if (elements.length > 0) { + push(inlineStyleElements, elements); + } + }); + inlineStyleElements.forEach((el) => + overrideInlineStyle( + el, + theme, + ignoredInlineSelectors, + ignoredImageAnalysisSelectors + ) + ); + handleAdoptedStyleSheets(document); + variablesStore.matchVariablesAndDependents(); + tryInvertChromePDF(); +} +let loadingStylesCounter = 0; +const loadingStyles = new Set(); +function createManager(element) { + const loadingStyleId = ++loadingStylesCounter; + function loadingStart() { + if (!isDOMReady() || !documentIsVisible()) { + loadingStyles.add(loadingStyleId); + logInfo(`Current amount of styles loading: ${loadingStyles.size}`); + const fallbackStyle = createOrUpdateStyle("darkreader--fallback"); + if (!fallbackStyle.textContent) { + fallbackStyle.textContent = getModifiedFallbackStyle(theme, { + strict: false + }); + } + } + } + function loadingEnd() { + loadingStyles.delete(loadingStyleId); + logInfo( + `Removed loadingStyle ${loadingStyleId}, now awaiting: ${loadingStyles.size}` + ); + if (loadingStyles.size === 0 && isDOMReady()) { + cleanFallbackStyle(); + } + } + function update() { + const details = manager.details({secondRound: true}); + if (!details) { + return; + } + variablesStore.addRulesForMatching(details.rules); + variablesStore.matchVariablesAndDependents(); + manager.render(theme, ignoredImageAnalysisSelectors); + } + const manager = manageStyle(element, {update, loadingStart, loadingEnd}); + styleManagers.set(element, manager); + return manager; +} +function removeManager(element) { + const manager = styleManagers.get(element); + if (manager) { + manager.destroy(); + styleManagers.delete(element); + } +} +const throttledRenderAllStyles = throttle((callback) => { + styleManagers.forEach((manager) => + manager.render(theme, ignoredImageAnalysisSelectors) + ); + adoptedStyleManagers.forEach((manager) => + manager.render(theme, ignoredImageAnalysisSelectors) + ); + callback && callback(); +}); +const cancelRendering = function () { + throttledRenderAllStyles.cancel(); +}; +function onDOMReady() { + if (loadingStyles.size === 0) { + cleanFallbackStyle(); + return; + } + logWarn(`DOM is ready, but still have styles being loaded.`, loadingStyles); +} +function runDynamicStyle() { + createDynamicStyleOverrides(); + watchForUpdates(); +} +function createThemeAndWatchForUpdates() { + createStaticStyleOverrides(); + if (!documentIsVisible() && !theme.immediateModify) { + setDocumentVisibilityListener(runDynamicStyle); + } else { + runDynamicStyle(); + } + changeMetaThemeColorWhenAvailable(theme); +} +function unwrap(value) { + return value?.wrappedJSObject ?? value; +} +function handleAdoptedStyleSheets(node) { + if (canHaveAdoptedStyleSheets(node)) { + forEach( + isFirefox + ? unwrap(node.adoptedStyleSheets) + : node.adoptedStyleSheets, + (s) => { + variablesStore.addRulesForMatching(s.cssRules); + } + ); + const newManger = createAdoptedStyleSheetOverride(node); + adoptedStyleManagers.push(newManger); + newManger.render(theme, ignoredImageAnalysisSelectors); + newManger.watch((sheets) => { + sheets.forEach((s) => { + variablesStore.addRulesForMatching(s.cssRules); + }); + variablesStore.matchVariablesAndDependents(); + newManger.render(theme, ignoredImageAnalysisSelectors); + }); + } +} +function watchForUpdates() { + const managedStyles = Array.from(styleManagers.keys()); + watchForStyleChanges( + managedStyles, + ({created, updated, removed, moved}) => { + const stylesToRemove = removed; + const stylesToManage = created + .concat(updated) + .concat(moved) + .filter((style) => !styleManagers.has(style)); + const stylesToRestore = moved.filter((style) => + styleManagers.has(style) + ); + stylesToRemove.forEach((style) => removeManager(style)); + const newManagers = stylesToManage.map((style) => + createManager(style) + ); + newManagers + .map((manager) => manager.details({secondRound: false})) + .filter((detail) => detail && detail.rules.length > 0) + .forEach((detail) => { + variablesStore.addRulesForMatching(detail.rules); + }); + variablesStore.matchVariablesAndDependents(); + newManagers.forEach((manager) => + manager.render(theme, ignoredImageAnalysisSelectors) + ); + newManagers.forEach((manager) => manager.watch()); + stylesToRestore.forEach((style) => + styleManagers.get(style).restore() + ); + }, + (shadowRoot) => { + createShadowStaticStyleOverrides(shadowRoot); + handleAdoptedStyleSheets(shadowRoot); + } + ); + watchForInlineStyles( + (element) => { + overrideInlineStyle( + element, + theme, + ignoredInlineSelectors, + ignoredImageAnalysisSelectors + ); + if (element === document.documentElement) { + const styleAttr = element.getAttribute("style") || ""; + if (styleAttr.includes("--")) { + variablesStore.matchVariablesAndDependents(); + const rootVarsStyle = createOrUpdateStyle( + "darkreader--root-vars" + ); + variablesStore.putRootVars(rootVarsStyle, theme); + } + } + }, + (root) => { + createShadowStaticStyleOverrides(root); + const inlineStyleElements = root.querySelectorAll( + INLINE_STYLE_SELECTOR + ); + if (inlineStyleElements.length > 0) { + forEach(inlineStyleElements, (el) => + overrideInlineStyle( + el, + theme, + ignoredInlineSelectors, + ignoredImageAnalysisSelectors + ) + ); + } + } + ); + addDOMReadyListener(onDOMReady); + setupDocumentPiPFontFix(); +} +function stopWatchingForUpdates() { + styleManagers.forEach((manager) => manager.pause()); + stopStylePositionWatchers(); + stopWatchingForStyleChanges(); + stopWatchingForInlineStyles(); + removeDOMReadyListener(onDOMReady); + cleanReadyStateCompleteListeners(); +} +let metaObserver; +let headObserver = null; +function addMetaListener() { + metaObserver = new MutationObserver(() => { + if (document.querySelector('meta[name="darkreader-lock"]')) { + metaObserver.disconnect(); + removeDynamicTheme(); + } + }); + metaObserver.observe(document.head, {childList: true, subtree: true}); +} +function createDarkReaderInstanceMarker() { + const metaElement = document.createElement("meta"); + metaElement.name = "darkreader"; + metaElement.content = INSTANCE_ID; + document.head.appendChild(metaElement); +} +function isDRLocked() { + return document.querySelector('meta[name="darkreader-lock"]') != null; +} +function isAnotherDarkReaderInstanceActive() { + const meta = document.querySelector('meta[name="darkreader"]'); + if (meta) { + if (meta.content !== INSTANCE_ID) { + return true; + } + return false; + } + createDarkReaderInstanceMarker(); + addMetaListener(); + return false; +} +let interceptorAttempts = 2; +function interceptOldScript({success, failure}) { + if (--interceptorAttempts <= 0) { + failure(); + return; + } + const oldMeta = document.head.querySelector('meta[name="darkreader"]'); + if (!oldMeta || oldMeta.content === INSTANCE_ID) { + return; + } + const lock = document.createElement("meta"); + lock.name = "darkreader-lock"; + document.head.append(lock); + queueMicrotask(() => { + lock.remove(); + success(); + }); +} +function disableConflictingPlugins() { + if (document.documentElement.hasAttribute("data-wp-dark-mode-preset")) { + const disableWPDarkMode = () => { + document.dispatchEvent( + new CustomEvent("__darkreader__disableConflictingPlugins") + ); + document.documentElement.classList.remove("wp-dark-mode-active"); + document.documentElement.removeAttribute( + "data-wp-dark-mode-active" + ); + }; + disableWPDarkMode(); + const observer = new MutationObserver(() => { + if ( + document.documentElement.classList.contains( + "wp-dark-mode-active" + ) || + document.documentElement.hasAttribute( + "data-wp-dark-mode-active" + ) + ) { + disableWPDarkMode(); + } + }); + observer.observe(document.documentElement, { + attributes: true, + attributeFilter: ["class", "data-wp-dark-mode-active"] + }); + } +} +function tryInvertChromePDF() { + if (!document.body || !chrome.dom) { + return; + } + const root = chrome.dom.openOrClosedShadowRoot(document.body); + if (!root || !root.querySelector('link[href$="/pdf_embedder.css"]')) { + return; + } + if (isChromium && !isMobile) { + const overlay = document.createElement("div"); + overlay.classList.add("darkreader"); + overlay.classList.add("darkreader--pdf-overlay"); + overlay.style.backdropFilter = "invert(100%) contrast(90%)"; + overlay.style.pointerEvents = "none"; + overlay.style.position = "fixed"; + overlay.style.left = "0px"; + overlay.style.right = "0px"; + overlay.style.bottom = "0px"; + overlay.style.top = "56px"; + root.append(overlay); + cleaners.push(() => { + overlay.remove(); + }); + } else { + const sheet = new CSSStyleSheet(); + sheet.replaceSync( + '[type="application/pdf"] { filter: invert(1) contrast(0.9); }' + ); + root.adoptedStyleSheets.push(sheet); + cleaners.push(() => { + const index = root.adoptedStyleSheets.indexOf(sheet); + if (index >= 0) { + root.adoptedStyleSheets.splice(index, 1); + } + }); + } +} +let prevTheme = null; +let prevFixes = null; +function createOrUpdateDynamicThemeInternal( + themeConfig, + dynamicThemeFixes, + iframe +) { + theme = themeConfig; + fixes = dynamicThemeFixes; + const colorAffectingKeys = [ + "brightness", + "contrast", + "darkSchemeBackgroundColor", + "darkSchemeTextColor", + "grayscale", + "lightSchemeBackgroundColor", + "lightSchemeTextColor", + "sepia" + ]; + if (prevTheme && prevFixes) { + const themeKeys = new Set([ + ...Object.keys(theme), + ...Object.keys(prevTheme) + ]); + let onlyColorsChanged = true; + for (const key of themeKeys) { + if ( + theme[key] !== prevTheme[key] && + !colorAffectingKeys.includes(key) + ) { + onlyColorsChanged = false; + break; + } + } + if ( + onlyColorsChanged && + JSON.stringify(fixes) !== JSON.stringify(prevFixes) + ) { + onlyColorsChanged = false; + } + if (onlyColorsChanged) { + const palette = getColorPalette(); + clearColorPalette(); + palette.background.forEach((color) => + modifyBackgroundColor(color, theme) + ); + palette.text.forEach((color) => + modifyForegroundColor(color, theme) + ); + palette.border.forEach((color) => modifyBorderColor(color, theme)); + return; + } + clearColorPalette(); + } + if (fixes) { + ignoredImageAnalysisSelectors = Array.isArray(fixes.ignoreImageAnalysis) + ? fixes.ignoreImageAnalysis + : []; + ignoredInlineSelectors = Array.isArray(fixes.ignoreInlineStyle) + ? fixes.ignoreInlineStyle + : []; + setIgnoredCSSURLs( + Array.isArray(fixes.ignoreCSSUrl) ? fixes.ignoreCSSUrl : [] + ); + } else { + ignoredImageAnalysisSelectors = []; + ignoredInlineSelectors = []; + setIgnoredCSSURLs([]); + } + if (theme.immediateModify) { + setIsDOMReady(() => { + return true; + }); + } + isIFrame$1 = iframe; + const ready = () => { + const success = () => { + disableConflictingPlugins(); + document.documentElement.setAttribute( + "data-darkreader-mode", + "dynamic" + ); + document.documentElement.setAttribute( + "data-darkreader-scheme", + theme.mode ? "dark" : "dimmed" + ); + createThemeAndWatchForUpdates(); + }; + const failure = () => { + removeDynamicTheme(); + }; + if (isDRLocked()) { + removeNode(document.querySelector(".darkreader--fallback")); + } else if (isAnotherDarkReaderInstanceActive()) { + interceptOldScript({ + success, + failure + }); + } else { + success(); + } + }; + if (document.head) { + ready(); + } else { + if (!isFirefox) { + const fallbackStyle = createOrUpdateStyle("darkreader--fallback"); + document.documentElement.appendChild(fallbackStyle); + fallbackStyle.textContent = getModifiedFallbackStyle(theme, { + strict: true + }); + } + headObserver?.disconnect(); + headObserver = new MutationObserver(() => { + if (document.head) { + headObserver?.disconnect(); + ready(); + } + }); + cleaners.push(() => { + headObserver?.disconnect(); + headObserver = null; + }); + headObserver.observe(document, {childList: true, subtree: true}); + } + prevTheme = theme; + prevFixes = fixes; +} +function removeProxy() { + document.dispatchEvent(new CustomEvent("__darkreader__cleanUp")); + removeNode(document.head.querySelector(".darkreader--proxy")); +} +const cleaners = []; +let pipListenerRegistered = false; +function setupDocumentPiPFontFix() { + if (pipListenerRegistered) { + return; + } + const docPiP = window.documentPictureInPicture; + if (!docPiP) { + return; + } + pipListenerRegistered = true; + function collectFontSheetCSS() { + const fontSheetRules = []; + for (const sheet of document.styleSheets) { + try { + const rules = Array.from(sheet.cssRules); + if (rules.some((rule) => rule instanceof CSSFontFaceRule)) { + rules.forEach((rule) => fontSheetRules.push(rule.cssText)); + } + } catch (e) {} + } + return fontSheetRules.join("\n"); + } + function getPipDoc() { + return docPiP.window?.document ?? null; + } + function injectFontCSS(fontCSS) { + const pipDoc = getPipDoc(); + if (!pipDoc || pipDoc.querySelector(".darkreader--font-fix")) { + return; + } + const style = pipDoc.createElement("style"); + style.classList.add("darkreader"); + style.classList.add("darkreader--font-fix"); + style.textContent = fontCSS; + (pipDoc.head || pipDoc.documentElement).appendChild(style); + } + function removeFontCSS() { + getPipDoc()?.querySelector(".darkreader--font-fix")?.remove(); + } + function onPiPEnter() { + const pipDoc = getPipDoc(); + if (!pipDoc || pipDoc.querySelector('meta[name="darkreader-lock"]')) { + return; + } + const fontCSS = collectFontSheetCSS(); + if (!fontCSS) { + return; + } + injectFontCSS(fontCSS); + const observer = new MutationObserver(() => { + if (pipDoc.querySelector('meta[name="darkreader-lock"]')) { + observer.disconnect(); + docPiP.removeEventListener("enter", onPiPEnter); + removeFontCSS(); + return; + } + injectFontCSS(fontCSS); + }); + observer.observe(pipDoc, {childList: true, subtree: true}); + cleaners.push(() => observer.disconnect()); + docPiP.window.addEventListener("unload", () => observer.disconnect()); + } + docPiP.addEventListener("enter", onPiPEnter); + cleaners.push(() => { + docPiP.removeEventListener("enter", onPiPEnter); + removeFontCSS(); + pipListenerRegistered = false; + }); +} +function removeDynamicTheme() { + document.documentElement.removeAttribute(`data-darkreader-mode`); + document.documentElement.removeAttribute(`data-darkreader-scheme`); + cleanDynamicThemeCache(); + removeNode(document.querySelector(".darkreader--fallback")); + if (document.head) { + const selectors = [ + ".darkreader--user-agent", + ".darkreader--text", + ".darkreader--invert", + ".darkreader--inline", + ".darkreader--override", + ".darkreader--variables", + ".darkreader--root-vars", + 'meta[name="darkreader"]' + ]; + restoreMetaThemeColor(); + selectors.forEach((selector) => + removeNode(document.head.querySelector(selector)) + ); + staticStyleMap = new WeakMap(); + removeProxy(); + } + shadowRootsWithOverrides.forEach((root) => { + removeNode(root.querySelector(".darkreader--inline")); + removeNode(root.querySelector(".darkreader--override")); + }); + shadowRootsWithOverrides.clear(); + forEach(styleManagers.keys(), (el) => removeManager(el)); + loadingStyles.clear(); + cleanLoadingLinks(); + forEach(document.querySelectorAll(".darkreader"), removeNode); + removeStyleContainer(); + adoptedStyleManagers.forEach((manager) => manager.destroy()); + adoptedStyleManagers.splice(0); + metaObserver && metaObserver.disconnect(); + scheduleInversionStyleUpdate.cancel(); + cleaners.forEach((clean) => clean()); + cleaners.splice(0); +} +function cleanDynamicThemeCache() { + variablesStore.clear(); + parsedURLCache.clear(); + cleanFilterSelectors(); + removeDocumentVisibilityListener(); + cancelRendering(); + stopWatchingForUpdates(); + cleanModificationCache(); + clearColorCache(); + releaseVariablesSheet(); + prevTheme = null; + prevFixes = null; +} + +function parseCSS(cssText) { + cssText = removeCSSComments(cssText); + cssText = cssText.trim(); + if (!cssText) { + return []; + } + const rules = []; + const excludeRanges = getTokenExclusionRanges(cssText); + const bracketRanges = getAllOpenCloseRanges( + cssText, + "{", + "}", + excludeRanges + ); + let ruleStart = 0; + bracketRanges.forEach((brackets) => { + const key = cssText.substring(ruleStart, brackets.start).trim(); + const content = cssText.substring(brackets.start + 1, brackets.end - 1); + if (key.startsWith("@")) { + const typeEndIndex = key.search(/[\s\(]/); + const rule = { + type: typeEndIndex < 0 ? key : key.substring(0, typeEndIndex), + query: + typeEndIndex < 0 ? "" : key.substring(typeEndIndex).trim(), + rules: parseCSS(content) + }; + rules.push(rule); + } else { + const rule = { + selectors: parseSelectors(key), + declarations: parseDeclarations(content) + }; + rules.push(rule); + } + ruleStart = brackets.end; + }); + return rules; +} +function getAllOpenCloseRanges( + input, + openToken, + closeToken, + excludeRanges = [] +) { + const ranges = []; + let i = 0; + let range; + while ( + (range = getOpenCloseRange( + input, + i, + openToken, + closeToken, + excludeRanges + )) + ) { + ranges.push(range); + i = range.end; + } + return ranges; +} +function getTokenExclusionRanges(cssText) { + const singleQuoteGoesFirst = cssText.indexOf("'") < cssText.indexOf('"'); + const firstQuote = singleQuoteGoesFirst ? "'" : '"'; + const secondQuote = singleQuoteGoesFirst ? '"' : "'"; + const excludeRanges = getAllOpenCloseRanges( + cssText, + firstQuote, + firstQuote + ); + excludeRanges.push( + ...getAllOpenCloseRanges( + cssText, + secondQuote, + secondQuote, + excludeRanges + ) + ); + excludeRanges.push( + ...getAllOpenCloseRanges(cssText, "[", "]", excludeRanges) + ); + excludeRanges.push( + ...getAllOpenCloseRanges(cssText, "(", ")", excludeRanges) + ); + return excludeRanges; +} +function parseSelectors(selectorText) { + const excludeRanges = getTokenExclusionRanges(selectorText); + return splitExcluding(selectorText, ",", excludeRanges); +} +function parseDeclarations(cssDeclarationsText) { + const declarations = []; + const excludeRanges = getTokenExclusionRanges(cssDeclarationsText); + splitExcluding(cssDeclarationsText, ";", excludeRanges).forEach((part) => { + const colonIndex = part.indexOf(":"); + if (colonIndex > 0) { + const importantIndex = part.indexOf("!important"); + declarations.push({ + property: part.substring(0, colonIndex).trim(), + value: part + .substring( + colonIndex + 1, + importantIndex > 0 ? importantIndex : part.length + ) + .trim(), + important: importantIndex > 0 + }); + } + }); + return declarations; +} +function isParsedStyleRule(rule) { + return "selectors" in rule; +} + +function formatCSS(cssText) { + const parsed = parseCSS(cssText); + return formatParsedCSS(parsed); +} +function formatParsedCSS(parsed) { + const lines = []; + const tab = " "; + function formatRule(rule, indent) { + if (isParsedStyleRule(rule)) { + formatStyleRule(rule, indent); + } else { + formatAtRule(rule, indent); + } + } + function formatAtRule({type, query, rules}, indent) { + lines.push(`${indent}${type} ${query} {`); + rules.forEach((child) => formatRule(child, `${indent}${tab}`)); + lines.push(`${indent}}`); + } + function formatStyleRule({selectors, declarations}, indent) { + const lastSelectorIndex = selectors.length - 1; + selectors.forEach((selector, i) => { + lines.push( + `${indent}${selector}${i < lastSelectorIndex ? "," : " {"}` + ); + }); + const sorted = sortDeclarations(declarations); + sorted.forEach(({property, value, important}) => { + lines.push( + `${indent}${tab}${property}: ${value}${important ? " !important" : ""};` + ); + }); + lines.push(`${indent}}`); + } + clearEmptyRules(parsed); + parsed.forEach((rule) => formatRule(rule, "")); + return lines.join("\n"); +} +function sortDeclarations(declarations) { + const prefixRegex = /^-[a-z]-/; + return [...declarations].sort((a, b) => { + const aProp = a.property; + const bProp = b.property; + const aPrefix = aProp.match(prefixRegex)?.[0] ?? ""; + const bPrefix = bProp.match(prefixRegex)?.[0] ?? ""; + const aNorm = aPrefix ? aProp.replace(prefixRegex, "") : aProp; + const bNorm = bPrefix ? bProp.replace(prefixRegex, "") : bProp; + if (aNorm === bNorm) { + return aPrefix.localeCompare(bPrefix); + } + return aNorm.localeCompare(bNorm); + }); +} +function clearEmptyRules(rules) { + for (let i = rules.length - 1; i >= 0; i--) { + const rule = rules[i]; + if (isParsedStyleRule(rule)) { + if (rule.declarations.length === 0) { + rules.splice(i, 1); + } + } else { + clearEmptyRules(rule.rules); + if (rule.rules.length === 0) { + rules.splice(i, 1); + } + } + } +} + +const blobRegex = /url\(\"(blob\:.*?)\"\)/g; +async function replaceBlobs(text) { + const promises = []; + getMatches(blobRegex, text, 1).forEach((url) => { + const promise = loadAsDataURL(url); + promises.push(promise); + }); + const data = await Promise.all(promises); + return text.replace(blobRegex, () => `url("${data.shift()}")`); +} +const banner = `/* + _______ + / \\ + .==. .==. + (( ))==(( )) + / "==" "=="\\ + /____|| || ||___\\ + ________ ____ ________ ___ ___ + | ___ \\ / \\ | ___ \\ | | / / + | | \\ \\ / /\\ \\ | | \\ \\| |_/ / + | | ) / /__\\ \\ | |__/ /| ___ \\ + | |__/ / ______ \\| ____ \\| | \\ \\ +_______|_______/__/ ____ \\__\\__|___\\__\\__|___\\__\\____ +| ___ \\ | ____/ / \\ | ___ \\ | ____| ___ \\ +| | \\ \\| |___ / /\\ \\ | | \\ \\| |___| | \\ \\ +| |__/ /| ____/ /__\\ \\ | | ) | ____| |__/ / +| ____ \\| |__/ ______ \\| |__/ /| |___| ____ \\ +|__| \\__\\____/__/ \\__\\_______/ |______|__| \\__\\ + https://darkreader.org +*/ + +/*! Dark reader generated CSS | Licensed under MIT https://github.com/darkreader/darkreader/blob/main/LICENSE */ +`; +async function collectCSS() { + const css = [banner]; + function addStaticCSS(selector, comment) { + const staticStyle = document.querySelector(selector); + if (staticStyle && staticStyle.textContent) { + css.push(`/* ${comment} */`); + css.push(staticStyle.textContent); + css.push(""); + } + } + addStaticCSS(".darkreader--fallback", "Fallback Style"); + addStaticCSS(".darkreader--user-agent", "User-Agent Style"); + addStaticCSS(".darkreader--text", "Text Style"); + addStaticCSS(".darkreader--invert", "Invert Style"); + addStaticCSS(".darkreader--variables", "Variables Style"); + const modifiedCSS = []; + document.querySelectorAll(".darkreader--sync").forEach((element) => { + forEach(element.sheet.cssRules, (rule) => { + rule && rule.cssText && modifiedCSS.push(rule.cssText); + }); + }); + if (modifiedCSS.length) { + const formattedCSS = formatCSS(modifiedCSS.join("\n")); + css.push("/* Modified CSS */"); + css.push(await replaceBlobs(formattedCSS)); + css.push(""); + } + addStaticCSS(".darkreader--override", "Override Style"); + return css.join("\n"); +} + +let isDarkReaderEnabled = false; +const isIFrame = (() => { + try { + return window.self !== window.top; + } catch (err) { + console.warn(err); + return true; + } +})(); +function enable(themeOptions = {}, fixes = null) { + const theme = {...DEFAULT_THEME, ...themeOptions}; + if (theme.engine !== ThemeEngine.dynamicTheme) { + throw new Error("Theme engine is not supported."); + } + createOrUpdateDynamicThemeInternal(theme, fixes, isIFrame); + isDarkReaderEnabled = true; +} +function isEnabled() { + return isDarkReaderEnabled; +} +function disable() { + removeDynamicTheme(); + isDarkReaderEnabled = false; +} +const darkScheme = + typeof matchMedia === "function" + ? matchMedia("(prefers-color-scheme: dark)") + : undefined; +let store = { + themeOptions: null, + fixes: null +}; +function handleColorScheme() { + if (darkScheme?.matches) { + enable(store.themeOptions, store.fixes); + } else { + disable(); + } +} +function auto(themeOptions = {}, fixes = null) { + if (themeOptions) { + store = {themeOptions, fixes}; + handleColorScheme(); + if (isMatchMediaChangeEventListenerSupported) { + darkScheme?.addEventListener("change", handleColorScheme); + } else { + darkScheme?.addListener(handleColorScheme); + } + } else { + if (isMatchMediaChangeEventListenerSupported) { + darkScheme?.removeEventListener("change", handleColorScheme); + } else { + darkScheme?.removeListener(handleColorScheme); + } + disable(); + } +} +async function exportGeneratedCSS() { + return await collectCSS(); +} +const setFetchMethod = setFetchMethod$1; + +export {auto, disable, enable, exportGeneratedCSS, isEnabled, setFetchMethod}; diff --git a/ui/vendor/darkreader/index.d.ts b/ui/vendor/darkreader/index.d.ts new file mode 100644 index 0000000..093103e --- /dev/null +++ b/ui/vendor/darkreader/index.d.ts @@ -0,0 +1,173 @@ +declare namespace DarkReader { + /** + * Enables dark mode for current web page. + * @param theme Theme options. + * @param fixes Fixes for the generated theme. + */ + function enable(theme: Partial, fixes?: DynamicThemeFix): void; + + /** + * Disables dark mode for current web page. + */ + function disable(): void; + + /** + * Enables dark mode when system color scheme is dark. + * @param theme Theme options. + * @param fixes Fixes for the generated theme. + */ + function auto(theme: Partial | false, fixes?: DynamicThemeFix): void; + + /** + * Stops watching for system color scheme. + * @param isEnabled Boolean `false` value. + */ + function auto(isEnabled: false): void; + /** + * Returns if darkreader is enabled. + */ + function isEnabled(): boolean; + + /** + * Sets a function for making CORS requests. + * @param fetch A fetch function. + */ + function setFetchMethod(fetch: (url: string) => Promise): void; + + /** + * Returns the generated CSS by Dark Reader as a string. + */ + function exportGeneratedCSS(): Promise; + + /** + * Theme options. + */ + interface Theme { + /** + * 1 - dark mode, 0 - dimmed mode. + * Default 1. + */ + mode: 0 | 1; + /** + * Brightness (0 - 100+). + * Default 100. + */ + brightness: number; + /** + * Contrast (0 - 100+). + * Default 100. + */ + contrast: number; + /** + * Grayscale (0 - 100). + * Default 0. + */ + grayscale: number; + /** + * Sepia (0 - 100). + * Default 0. + */ + sepia: number; + /** + * Specifies if custom font should be used. + * Default false. + */ + useFont: boolean; + /** + * Font family to use. + */ + fontFamily: string; + /** + * Makes text look bolder (0 - 1px). + * Default 0. + */ + textStroke: number; + /** + * Background color to use for dark mode. + * Default #181a1b + */ + darkSchemeBackgroundColor: string; + /** + * Text color to use for dark mode. + * Default #e8e6e3 + */ + darkSchemeTextColor: string; + /** + * Background color to use for light mode. + * Default #dcdad7 + */ + lightSchemeBackgroundColor: string; + /** + * Text color to use for light mode. + * Default #181a1b + */ + lightSchemeTextColor: string; + /** + * Scrollbar color + * Default auto + */ + scrollbarColor: string; + /** + * Selection color + * Default auto + */ + selectionColor: string; + /** + * Specifies if it has to style system controls + * Default true + */ + styleSystemControls: boolean; + } + + /** + * Contains fixes for the generated theme. + */ + interface DynamicThemeFix { + /** + * List of CSS selectors that should be inverted. + * Usually icons that are contained in sprites. + */ + invert: string[]; + /** + * Additional CSS. + * ${color} template should be used to apply theme options to a color. + * Example: + * ``` + * body { + * background-color: ${white} !important; + * background-image: none !important; + * } + * ``` + */ + css: string; + /** + * List of CSS selectors where it's inline style should not be analyzed + * Mostly used for color pickers + */ + ignoreInlineStyle: string[]; + /** + * List of CSS selectors where it's image should not be analyzed + * Mostly used for wrongly inverted background-images + */ + ignoreImageAnalysis: string[]; + + /** + * A toggle to disable the proxying of `document.styleSheets`. + * This is a API-Exclusive option, as it can break legitimate websites, + * who are using the Dark Reader API. + */ + disableStyleSheetsProxy: boolean; + /** + * List of stylesheet URL patterns to ignore. + * Stylesheets matching these patterns will not be processed by Dark Reader. + * - Simple string: matches if URL contains the string (e.g., "crisp") + * - Prefix with ^: matches if URL starts with pattern (e.g., "^https://client.crisp") + * - Suffix with $: matches if URL ends with pattern (e.g., ".css$") + */ + ignoreCSSUrl: string[]; + } +} + +declare module 'darkreader' { + export = DarkReader; +} diff --git a/ui/vendor/darkreader/package.json b/ui/vendor/darkreader/package.json new file mode 100644 index 0000000..9675fbd --- /dev/null +++ b/ui/vendor/darkreader/package.json @@ -0,0 +1,9 @@ +{ + "name": "darkreader", + "version": "4.9.129", + "description": "Dark mode for every website", + "main": "darkreader.js", + "module": "darkreader.mjs", + "types": "index.d.ts", + "license": "MIT" +} \ No newline at end of file