PluginProbe
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) / 1.5.4
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) v1.5.4
2.1.3 2.1.2 2.1.1 2.1.0 2.0.4 2.0.3 2.0.2 2.0.1 2.0.0 1.5.5 1.5.4 1.5.3 1.5.2 1.5.1 1.5.0 trunk 1.0.1 1.1.0 1.2.0 1.2.1 1.2.2 1.2.3 1.2.4 1.2.5 1.3.0 All 57 releases
darkify / src / assets / js / client_main.js

client_main.js in Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) 1.5.4, at src/assets/js/client_main.js

1,809 lines 60.1 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 "use strict";
2
3 // Skip ALL Darkify initialization when this page is running inside a frontend
4 // iframe and the parent site has "Frontend Iframe Dark Mode" turned OFF.
5 // This is the only reliable guard for same-origin embedded pages that have
6 // their own copy of Darkify running — they must not self-initialize dark mode.
7 var _dkf_iframe_disabled = (
8 window !== window.top &&
9 typeof darkify_is_this_admin_panel !== "undefined" &&
10 darkify_is_this_admin_panel !== "1" &&
11 typeof darkify_enable_frontend_iframe_dark_mode !== "undefined" &&
12 darkify_enable_frontend_iframe_dark_mode !== "1"
13 );
14
15 let has_process_run_at_least_once = false;
16 let old_transition = "";
17 let has_background_img_url = false;
18 let darken_level = parseInt(darkify_bg_image_darken_to) / 100;
19 darken_level = darken_level.toFixed(1);
20 let darkify_secondary_bg_color = "";
21
22 darkify_init_keyboard_shortcut_listener();
23 darkify_init_os_mode_change_listener();
24
25 const darkify_observer = new MutationObserver(function (mutationsList) {
26 darkify_init_processes();
27
28 darkify_process_iframes(); // �
29 darkify proceed iframe
30 });
31
32 const elements_class_changed = new MutationObserver((mutationsList) => {
33 if (document.readyState !== "loading") {
34 mutationsList.forEach((mutation) => {
35 const target = mutation.target;
36
37 if (target.classList.contains("darkify_processed")) {
38 if (!target.hasAttribute("data-darkify_preserved_classes")) {
39 target.dataset.darkify_preserved_classes =
40 target.classList.toString();
41 } else {
42 if (
43 target.dataset.darkify_preserved_classes ===
44 target.classList.toString()
45 ) {
46 return;
47 }
48 }
49
50 target.dataset.darkify_preserved_classes = target.classList.toString();
51 elements_class_changed.disconnect();
52 target.classList.remove("darkify_processed");
53 darkify_process_element(target);
54
55 document
56 .querySelectorAll(
57 "*:not(head, title, link, meta, script, style, defs, filter)",
58 )
59 .forEach((element) => {
60 elements_class_changed.observe(element, {
61 attributes: true,
62 attributeFilter: ["class"],
63 });
64 });
65 }
66 });
67 }
68 });
69
70 const dark_mode_status_changed = new MutationObserver((mutationsList) => {
71 mutationsList.forEach((mutation) => {
72 if (mutation.type === "attributes" && mutation.attributeName === "class") {
73 document
74 .querySelectorAll(
75 "*:not(head, title, link, meta, script, style, defs, filter)",
76 )
77 .forEach((element) => {
78 if (element.classList.contains("darkify_processed")) {
79 if (
80 darkify_disallowed_elements.length > 0 &&
81 element.matches(darkify_disallowed_elements)
82 ) {
83 return;
84 }
85 if (darkify_enable_bg_image_darken === "1") {
86 darkify_darken_bg_image(element, darken_level);
87 }
88 if (
89 (darkify_enable_low_image_brightness === "1" ||
90 darkify_enable_image_grayscale === "1") &&
91 element.nodeName.toLowerCase() === "img"
92 ) {
93 darkify_img_brightness_and_grayscale(element);
94 }
95 if (
96 darkify_enable_invert_inline_svg === "1" &&
97 element.nodeName.toLowerCase() === "svg"
98 ) {
99 darkify_invert_inline_svg(element);
100 }
101 if (
102 darkify_enable_low_video_brightness === "1" ||
103 darkify_enable_video_grayscale === "1"
104 ) {
105 if (element.nodeName.toLowerCase() === "video") {
106 darkify_video_brightness_and_grayscale(element);
107 }
108 if (
109 element.nodeName.toLowerCase() === "iframe" &&
110 element.getAttribute("src") != null
111 ) {
112 const srcAttribute = element.getAttribute("src");
113 if (
114 srcAttribute.includes("youtube") ||
115 srcAttribute.includes("vimeo") ||
116 srcAttribute.includes("dailymotion")
117 ) {
118 darkify_video_brightness_and_grayscale(element);
119 }
120 }
121 }
122 if (element.hasAttribute("data-darkify_alpha_bg")) {
123 darkify_fix_background_color_alpha(element);
124 }
125 }
126 });
127 }
128 });
129 });
130
131 function darkify_change_state() {
132 if (darkify_is_this_admin_panel === "1") {
133 localStorage.darkify_admin_panel_last_state = document
134 .getElementsByTagName("html")[0]
135 .classList.contains("darkify_dark_mode_enabled")
136 ? "1"
137 : "0";
138 } else {
139 localStorage.darkify_last_state = document
140 .getElementsByTagName("html")[0]
141 .classList.contains("darkify_dark_mode_enabled")
142 ? "1"
143 : "0";
144 }
145 }
146
147 function darkify_switch_trigger() {
148 if (!has_process_run_at_least_once) {
149 darkify_init_processes();
150 darkify_init_observer();
151 }
152
153 const htmlElement = document.getElementsByTagName("html")[0];
154
155 if (htmlElement.classList.contains("darkify_dark_mode_enabled")) {
156 htmlElement.classList.remove("darkify_dark_mode_enabled");
157 } else {
158 htmlElement.classList.add("darkify_dark_mode_enabled");
159 }
160
161 darkify_change_state();
162
163 darkify_process_iframes(); // �
164 darkify proceed iframe
165 }
166
167 function darkify_theme_select(theme) {
168 if (!darkify_is_block_editor_context()) return;
169 if (!has_process_run_at_least_once) {
170 darkify_init_processes();
171 darkify_init_observer();
172 }
173
174 const htmlElement = document.documentElement;
175
176 // �
177 Remove all previous theme classes (IMPORTANT)
178 htmlElement.classList.forEach((cls) => {
179 if (cls.startsWith("darkify-")) {
180 htmlElement.classList.remove(cls);
181 }
182 });
183
184 // �
185 Save in localStorage
186 localStorage.darkify_selected_theme = theme;
187
188 // �
189 Update all select dropdowns
190 darkify_update_theme_selectors(theme);
191
192 if (darkify_is_this_admin_panel === "1") {
193 if (localStorage.darkify_admin_panel_last_state === "1") {
194 htmlElement.classList.add("darkify_dark_mode_enabled");
195 htmlElement.classList.add("darkify-" + theme);
196 } else {
197 htmlElement.classList.remove("darkify_dark_mode_enabled");
198 }
199 }
200
201 darkify_change_state();
202
203 // Apply the new palette to the parent first, then sync iframes so they pick
204 // up the updated variables rather than the previous palette.
205 darkify_apply_palette(theme);
206
207 darkify_process_iframes();
208 }
209
210 document.addEventListener("DOMContentLoaded", function () {
211 if (!darkify_is_block_editor_context()) return;
212
213 let theme = localStorage.getItem("darkify_selected_theme") || "set1";
214
215 darkify_theme_select(theme);
216 });
217
218 function darkify_update_theme_selectors(theme) {
219 document
220 .querySelectorAll(".darkify-theme-selector")
221 .forEach(function (select) {
222 if (select.value !== theme) {
223 select.value = theme;
224 }
225 });
226 }
227
228 function darkify_is_block_editor_context() {
229 return (
230 typeof document !== "undefined" &&
231 document.body &&
232 (document.body.classList.contains("block-editor-page") ||
233 document.querySelector(".edit-post-visual-editor") !== null ||
234 document.querySelector(".block-editor") !== null)
235 );
236 }
237
238 function darkify_restore_selected_theme() {
239 if (!darkify_is_block_editor_context()) return;
240 const storedTheme = localStorage.darkify_selected_theme;
241
242 if (!storedTheme) return;
243
244 darkify_update_theme_selectors(storedTheme);
245 darkify_theme_select(storedTheme);
246 }
247
248 function darkify_apply_palette(theme) {
249 if (!darkify_is_block_editor_context()) return;
250 const palettes = {
251 set1: {
252 bg: "#0F0F0F",
253 secondary_bg: "#171717",
254 text_color: "#BEBEBE",
255 link_color: "#E7E7E7",
256 link_hover_color: "#BEBEBE",
257 input_bg: "#2D2D2D",
258 input_text_color: "#BEBEBE",
259 input_placeholder_color: "#BEBEBE",
260 border_color: "#4A4A4A",
261 btn_text_color: "#BEBEBE",
262 btn_bg: "#4A4A4A",
263 btn_text_hover_color: "#BEBEBE",
264 btn_hover_bg: "#2D2D2D",
265 },
266 set3: {
267 bg: "#211e3c",
268 secondary_bg: "#302C57",
269 text_color: "#B1BBD8",
270 link_color: "#8071fb",
271 link_hover_color: "#B1BBD8",
272 input_bg: "#2A264D",
273 input_text_color: "#B1BBD8",
274 input_placeholder_color: "#B1BBD8",
275 border_color: "#4E478D",
276 btn_text_color: "#B1BBD8",
277 btn_bg: "#4E478D",
278 btn_text_hover_color: "#B1BBD8",
279 btn_hover_bg: "#2A264D",
280 },
281
282 set6: {
283 bg: "#082032",
284 secondary_bg: "#061825",
285 text_color: "#B5D9F3",
286 link_color: "#61bbff",
287 link_hover_color: "#B5D9F3",
288 input_bg: "#0E3755",
289 input_text_color: "#B5D9F3",
290 input_placeholder_color: "#B5D9F3",
291 border_color: "#144E78",
292 btn_text_color: "#B5D9F3",
293 btn_bg: "#144E78",
294 btn_text_hover_color: "#B5D9F3",
295 btn_hover_bg: "#0E3755",
296 },
297
298 set9: {
299 bg: "#04261d",
300 secondary_bg: "#021e16",
301 text_color: "#C1D2BB",
302 link_color: "#00d29a",
303 link_hover_color: "#C1D2BB",
304 input_bg: "#073d2f",
305 input_text_color: "#C1D2BB",
306 input_placeholder_color: "#C1D2BB",
307 border_color: "#095541",
308 btn_text_color: "#C1D2BB",
309 btn_bg: "#095541",
310 btn_text_hover_color: "#C1D2BB",
311 btn_hover_bg: "#073d2f",
312 },
313
314 set10: {
315 bg: "#171004",
316 secondary_bg: "#211706",
317 text_color: "#E0D2BD",
318 link_color: "#e09525",
319 link_hover_color: "#E0D2BD",
320 input_bg: "#372911",
321 input_text_color: "#E0D2BD",
322 input_placeholder_color: "#E0D2BD",
323 border_color: "#5D4010",
324 btn_text_color: "#E0D2BD",
325 btn_bg: "#5D4010",
326 btn_text_hover_color: "#E0D2BD",
327 btn_hover_bg: "#372911",
328 },
329 };
330
331 const palette = palettes[theme] || palettes["set1"];
332
333 document.documentElement.style.setProperty(
334 "--darkify_dark_mode_bg",
335 palette.bg,
336 );
337 document.documentElement.style.setProperty(
338 "--darkify_dark_mode_secondary_bg",
339 palette.secondary_bg,
340 );
341 document.documentElement.style.setProperty(
342 "--darkify_dark_mode_text_color",
343 palette.text_color,
344 );
345 document.documentElement.style.setProperty(
346 "--darkify_dark_mode_link_color",
347 palette.link_color,
348 );
349 document.documentElement.style.setProperty(
350 "--darkify_dark_mode_link_hover_color",
351 palette.link_hover_color,
352 );
353 document.documentElement.style.setProperty(
354 "--darkify_dark_mode_input_bg",
355 palette.input_bg,
356 );
357 document.documentElement.style.setProperty(
358 "--darkify_dark_mode_input_text_color",
359 palette.input_text_color,
360 );
361 document.documentElement.style.setProperty(
362 "--darkify_dark_mode_input_placeholder_color",
363 palette.input_placeholder_color,
364 );
365 document.documentElement.style.setProperty(
366 "--darkify_dark_mode_border_color",
367 palette.border_color,
368 );
369 document.documentElement.style.setProperty(
370 "--darkify_dark_mode_btn_bg",
371 palette.btn_bg,
372 );
373 document.documentElement.style.setProperty(
374 "--darkify_dark_mode_btn_text_color",
375 palette.btn_text_color,
376 );
377 document.documentElement.style.setProperty(
378 "--darkify_dark_mode_btn_hover_bg",
379 palette.btn_hover_bg,
380 );
381 document.documentElement.style.setProperty(
382 "--darkify_dark_mode_btn_text_hover_color",
383 palette.btn_text_hover_color,
384 );
385 }
386
387 // ---------------------------------------------------------------------------
388 // Iframe dark mode
389 //
390 // Same-origin iframes are isolated documents: the parent's :root CSS variables,
391 // stylesheet and dark-mode class do not cascade into them. To keep an iframe in
392 // sync with the parent theme we mirror three things into the iframe document:
393 // 1. the darkify_dark_mode_enabled class on <html>
394 // 2. the theme CSS variables (kept live so colour/palette changes propagate)
395 // 3. the plugin stylesheet, plus a run of the element classifier so inner
396 // content (cards, sections, etc.) is darkened, not just <body>/<a>/inputs
397 // Colour changes only need the variables refreshed; the var-driven class rules
398 // then re-theme everything instantly without re-walking the DOM.
399 // ---------------------------------------------------------------------------
400
401 const DARKIFY_IFRAME_THEME_VARS = [
402 "--darkify_dark_mode_bg", "--darkify_dark_mode_secondary_bg",
403 "--darkify_dark_mode_text_color", "--darkify_dark_mode_link_color",
404 "--darkify_dark_mode_link_hover_color", "--darkify_dark_mode_input_bg",
405 "--darkify_dark_mode_input_text_color", "--darkify_dark_mode_input_placeholder_color",
406 "--darkify_dark_mode_border_color", "--darkify_dark_mode_btn_bg",
407 "--darkify_dark_mode_btn_text_color", "--darkify_dark_mode_btn_hover_bg",
408 "--darkify_dark_mode_btn_text_hover_color",
409 ];
410
411 // Tracks per-iframe-document observers so we don't attach duplicates and can
412 // react to dynamically injected content (e.g. React/SPA pages inside the frame).
413 const darkify_iframe_doc_observers = new WeakMap();
414
415 // Tracks per-iframe-document "keep our style last" observers so we attach only
416 // one per document. Keyed on iframeDoc.
417 const darkify_editor_head_watchers = new WeakMap();
418
419 // Serialise the parent's current theme variables as a :root {} rule. Computed
420 // style is used so the value reflects the active theme regardless of whether it
421 // was set inline (block editor) or via an inline <style> block (frontend).
422 function darkify_serialize_root_vars() {
423 const inline = document.documentElement.style;
424 const computed = getComputedStyle(document.documentElement);
425 let rootVars = ":root {";
426 DARKIFY_IFRAME_THEME_VARS.forEach(function (varName) {
427 const val = (
428 inline.getPropertyValue(varName) || computed.getPropertyValue(varName)
429 ).trim();
430 if (val) rootVars += varName + ": " + val + ";";
431 });
432 return rootVars + "}";
433 }
434
435 // Whether dark mode may apply to iframe content. The "Frontend Iframe Dark Mode"
436 // option only affects the frontend — the Gutenberg editor canvas is unaffected.
437 // Treated as enabled when the flag is absent (backward compatible: existing users
438 // who haven't re-saved settings keep the previous default-on behaviour).
439 function darkify_iframe_dark_enabled() {
440 if (darkify_is_this_admin_panel === "1") return true;
441 return (
442 typeof darkify_enable_frontend_iframe_dark_mode === "undefined" ||
443 darkify_enable_frontend_iframe_dark_mode === "1"
444 );
445 }
446
447 // Build the theme payload broadcast to iframes via postMessage. This is the
448 // only channel that works for CROSS-ORIGIN iframes (e.g. an app served from a
449 // different host/port), where the browser forbids touching contentDocument.
450 // The receiving page applies these variables to its own theme.
451 function darkify_build_theme_payload() {
452 const inline = document.documentElement.style;
453 const computed = getComputedStyle(document.documentElement);
454 const vars = {};
455 DARKIFY_IFRAME_THEME_VARS.forEach(function (varName) {
456 const val = (
457 inline.getPropertyValue(varName) || computed.getPropertyValue(varName)
458 ).trim();
459 if (val) vars[varName] = val;
460 });
461
462 return {
463 source: "darkify",
464 type: "darkify-theme",
465 // Honour the Frontend Iframe Dark Mode option here too, so the handshake
466 // reply can't push dark mode into a cross-origin iframe when it is disabled.
467 enabled:
468 darkify_iframe_dark_enabled() &&
469 document.documentElement.classList.contains("darkify_dark_mode_enabled"),
470 vars: vars,
471 };
472 }
473
474 // Post the current theme to an iframe window. Works regardless of origin and is
475 // silently ignored by frames that don't run the darkify receiver snippet.
476 function darkify_post_theme_to_iframe(iframe, payload) {
477 try {
478 const win = iframe.contentWindow;
479 if (win) win.postMessage(payload || darkify_build_theme_payload(), "*");
480 } catch (e) {
481 // ignore — frame not ready / inaccessible window reference
482 }
483 }
484
485 // Copy / refresh the theme variables inside an iframe document. Cheap and
486 // idempotent — called on every theme change so colours stay in sync.
487 function darkify_sync_iframe_vars(iframeDoc) {
488 const head = iframeDoc.head || iframeDoc.documentElement;
489 let style = iframeDoc.getElementById("darkify-iframe-vars");
490 if (!style) {
491 style = iframeDoc.createElement("style");
492 style.id = "darkify-iframe-vars";
493 head.appendChild(style);
494 }
495 style.textContent = darkify_serialize_root_vars();
496 }
497
498 // Resolve the URL of the plugin's main stylesheet as loaded in the parent, so
499 // the same var-driven .darkify_* rules can be injected into the iframe.
500 function darkify_get_main_css_href() {
501 const link = document.querySelector('link[href*="client_main"]');
502 return link ? link.href : null;
503 }
504
505 // Inject the plugin stylesheet (full class-based engine rules) plus a small
506 // baseline so the frame is themed immediately, before/independent of the
507 // element classifier pass.
508 function darkify_inject_css_into_iframe(iframeDoc) {
509 const head = iframeDoc.head || iframeDoc.documentElement;
510
511 if (!iframeDoc.getElementById("darkify-iframe-main-css")) {
512 const href = darkify_get_main_css_href();
513 if (href) {
514 const link = iframeDoc.createElement("link");
515 link.id = "darkify-iframe-main-css";
516 link.rel = "stylesheet";
517 link.href = href;
518 head.appendChild(link);
519 }
520 }
521
522 if (iframeDoc.getElementById("darkify-iframe-css")) return;
523
524 const style = iframeDoc.createElement("style");
525 style.id = "darkify-iframe-css";
526 style.textContent = `
527 html.darkify_dark_mode_enabled,
528 html.darkify_dark_mode_enabled body {
529 background: var(--darkify_dark_mode_secondary_bg) !important;
530 color: var(--darkify_dark_mode_text_color) !important;
531 }
532
533 html.darkify_dark_mode_enabled a {
534 color: var(--darkify_dark_mode_link_color) !important;
535 }
536 html.darkify_dark_mode_enabled a:hover {
537 color: var(--darkify_dark_mode_link_hover_color) !important;
538 }
539
540 html.darkify_dark_mode_enabled input,
541 html.darkify_dark_mode_enabled select,
542 html.darkify_dark_mode_enabled textarea {
543 background: var(--darkify_dark_mode_input_bg) !important;
544 color: var(--darkify_dark_mode_input_text_color) !important;
545 border-color: var(--darkify_dark_mode_border_color) !important;
546 }
547
548 html.darkify_dark_mode_enabled input::placeholder,
549 html.darkify_dark_mode_enabled textarea::placeholder {
550 color: var(--darkify_dark_mode_input_placeholder_color) !important;
551 }
552
553 /* TinyMCE editor body */
554 html.darkify_dark_mode_enabled body#tinymce,
555 html.darkify_dark_mode_enabled .mce-content-body {
556 background: var(--darkify_dark_mode_secondary_bg) !important;
557 color: var(--darkify_dark_mode_text_color) !important;
558 }
559 `;
560 head.appendChild(style);
561 }
562 function darkify_inject_block_editor_css_into_iframe(iframeDoc) {
563 const head = iframeDoc.head || iframeDoc.documentElement;
564 let style = iframeDoc.getElementById("darkify-block-editor-css");
565 if (!style) {
566 style = iframeDoc.createElement("style");
567 style.id = "darkify-block-editor-css";
568 }
569
570 style.textContent = `
571 /* ── Primary containers (covers all themes) ─────────────────── */
572 html.darkify_dark_mode_enabled,
573 html.darkify_dark_mode_enabled body,
574 html.darkify_dark_mode_enabled body.editor-styles-wrapper,
575 html.darkify_dark_mode_enabled body.block-editor-iframe__body,
576 html.darkify_dark_mode_enabled .editor-styles-wrapper,
577 html.darkify_dark_mode_enabled .is-root-container,
578 html.darkify_dark_mode_enabled .wp-block-post-content,
579 html.darkify_dark_mode_enabled .block-editor-block-list__layout,
580 html.darkify_dark_mode_enabled .block-editor-iframe__body,
581 html.darkify_dark_mode_enabled .wp-site-blocks,
582 html.darkify_dark_mode_enabled .entry-content,
583 html.darkify_dark_mode_enabled .site-content {
584 background: var(--darkify_dark_mode_bg) !important;
585 background-color: var(--darkify_dark_mode_bg) !important;
586 color: var(--darkify_dark_mode_text_color) !important;
587 }
588
589 /* ── Override CSS variables used by themes to drive backgrounds ─
590 Kadence: --global-palette9 (bg), --global-palette1 (text)
591 WordPress Global Styles: --wp--style--color--background */
592 html.darkify_dark_mode_enabled body {
593 --wp--style--color--background: var(--darkify_dark_mode_bg);
594 --wp--preset--color--background: var(--darkify_dark_mode_bg);
595 --wp--preset--color--base: var(--darkify_dark_mode_bg);
596 --wp--preset--color--contrast: var(--darkify_dark_mode_text_color);
597 --global-palette9: var(--darkify_dark_mode_bg);
598 --global-palette8: var(--darkify_dark_mode_secondary_bg);
599 --global-palette7: var(--darkify_dark_mode_secondary_bg);
600 --global-palette1: var(--darkify_dark_mode_text_color);
601 --global-palette2: var(--darkify_dark_mode_text_color);
602 --global-palette3: var(--darkify_dark_mode_link_color);
603 --global-palette6: var(--darkify_dark_mode_border_color);
604 }
605
606 /* ── Text elements ──────────────────────────────────────────── */
607 html.darkify_dark_mode_enabled p,
608 html.darkify_dark_mode_enabled h1,
609 html.darkify_dark_mode_enabled h2,
610 html.darkify_dark_mode_enabled h3,
611 html.darkify_dark_mode_enabled h4,
612 html.darkify_dark_mode_enabled h5,
613 html.darkify_dark_mode_enabled h6,
614 html.darkify_dark_mode_enabled li,
615 html.darkify_dark_mode_enabled td,
616 html.darkify_dark_mode_enabled th,
617 html.darkify_dark_mode_enabled blockquote,
618 html.darkify_dark_mode_enabled pre,
619 html.darkify_dark_mode_enabled span {
620 color: var(--darkify_dark_mode_text_color) !important;
621 }
622
623 /* ── Links ──────────────────────────────────────────────────── */
624 html.darkify_dark_mode_enabled a {
625 color: var(--darkify_dark_mode_link_color) !important;
626 }
627 html.darkify_dark_mode_enabled a:hover {
628 color: var(--darkify_dark_mode_link_hover_color) !important;
629 }
630
631 /* ── Form elements ──────────────────────────────────────────── */
632 html.darkify_dark_mode_enabled input,
633 html.darkify_dark_mode_enabled select,
634 html.darkify_dark_mode_enabled textarea {
635 background: var(--darkify_dark_mode_input_bg) !important;
636 color: var(--darkify_dark_mode_input_text_color) !important;
637 border-color: var(--darkify_dark_mode_border_color) !important;
638 }
639 html.darkify_dark_mode_enabled input::placeholder,
640 html.darkify_dark_mode_enabled textarea::placeholder {
641 color: var(--darkify_dark_mode_input_placeholder_color) !important;
642 }
643
644 /* ── Blocks ─────────────────────────────────────────────────── */
645 html.darkify_dark_mode_enabled .wp-block {
646 color: var(--darkify_dark_mode_text_color) !important;
647 }
648 html.darkify_dark_mode_enabled img {
649 filter: brightness(80%);
650 }
651 `;
652
653 // Always move to end of <head> so our rules load after any theme stylesheet.
654 // head.appendChild is a no-op-safe move: if the element is already in the
655 // tree it is first removed then re-inserted at the end.
656 head.appendChild(style);
657
658 // Keep it last: observe for new <link>/<style> tags Kadence (or any theme)
659 // injects after us and immediately re-append our style to the end.
660 darkify_keep_editor_style_last(iframeDoc, style);
661 }
662
663 // MutationObserver that keeps darkify-block-editor-css as the last stylesheet
664 // in the editor iframe <head>. Called once per iframeDoc (guarded by WeakMap).
665
666 function darkify_keep_editor_style_last(iframeDoc, ourStyle) {
667 if (darkify_editor_head_watchers.has(iframeDoc)) return;
668 const head = iframeDoc.head;
669 if (!head) return;
670
671 const obs = new MutationObserver(function () {
672 // If our style is already last, nothing to do.
673 if (head.lastElementChild === ourStyle) return;
674 // A new sheet was added after ours — move us to the end.
675 head.appendChild(ourStyle);
676 });
677
678 obs.observe(head, { childList: true });
679 darkify_editor_head_watchers.set(iframeDoc, obs);
680 }
681
682 // Run the element classifier across an iframe document and keep watching it for
683 // dynamically added nodes. window.getComputedStyle resolves styles of
684 // same-origin iframe elements, so the existing engine works unchanged.
685 function darkify_run_engine_on_iframe(iframeDoc) {
686 const selector =
687 "* :not(head, title, link, meta, script, style, defs, filter, .darkify_processed)";
688
689 const processAll = function () {
690 iframeDoc.querySelectorAll(selector).forEach(function (element) {
691 try {
692 darkify_process_element(element);
693 } catch (e) {
694 // skip elements that can't be processed
695 }
696 });
697 };
698
699 processAll();
700
701 if (!darkify_iframe_doc_observers.has(iframeDoc)) {
702 const observer = new MutationObserver(processAll);
703 observer.observe(iframeDoc.documentElement, {
704 childList: true,
705 subtree: true,
706 });
707 darkify_iframe_doc_observers.set(iframeDoc, observer);
708 }
709 }
710
711 // Apply CSS invert filter to a cross-origin iframe element as a fallback dark
712 // mode technique — the only browser-allowed approach when the embedded site
713 // does not run the darkify receiver script.
714 function darkify_apply_filter_to_iframe(iframe, enabled) {
715 if (enabled) {
716 iframe.style.filter = "brightness(0.6)";
717 } else {
718 iframe.style.filter = "";
719 }
720 }
721
722 // Detect whether an iframe is cross-origin by attempting to access its document.
723 function darkify_is_cross_origin_iframe(iframe) {
724 try {
725 // Accessing contentDocument throws SecurityError for cross-origin frames.
726 void (iframe.contentDocument || iframe.contentWindow?.document);
727 return false;
728 } catch (e) {
729 return true;
730 }
731 }
732 function darkify_apply_dark_to_iframe(iframe) {
733 const isEditorCanvas = iframe.name === "editor-canvas";
734 if (darkify_is_this_admin_panel === "1" && !isEditorCanvas) return;
735
736 const enabled = document.documentElement.classList.contains(
737 "darkify_dark_mode_enabled",
738 );
739
740 // Same-origin path: directly inject styles + run the engine. Throws a
741 // SecurityError for cross-origin frames, which we swallow — those are handled
742 // via postMessage + CSS filter fallback below.
743 const applyDirect = function () {
744 let iframeDoc;
745 try {
746 iframeDoc = iframe.contentDocument || iframe.contentWindow?.document;
747 } catch (e) {
748 // Cross-origin: apply CSS filter to the iframe element as fallback.
749 if (!isEditorCanvas && darkify_is_this_admin_panel !== "1") {
750 darkify_apply_filter_to_iframe(
751 iframe,
752 document.documentElement.classList.contains(
753 "darkify_dark_mode_enabled",
754 ),
755 );
756 }
757 return;
758 }
759 if (!iframeDoc || !iframeDoc.documentElement) return;
760
761 // Same-origin: clear any filter that was applied before we could access the doc.
762 iframe.style.filter = "";
763
764 if (enabled) {
765 iframeDoc.documentElement.classList.add("darkify_dark_mode_enabled");
766 darkify_inject_css_into_iframe(iframeDoc);
767 darkify_sync_iframe_vars(iframeDoc);
768 if (isEditorCanvas) {
769 darkify_inject_block_editor_css_into_iframe(iframeDoc);
770 } else if (darkify_is_this_admin_panel !== "1") {
771 darkify_run_engine_on_iframe(iframeDoc);
772 }
773 } else {
774 iframeDoc.documentElement.classList.remove("darkify_dark_mode_enabled");
775 }
776 };
777
778 // Always broadcast the current theme first. This MUST run independently of the
779 // (throwing) contentDocument access below, otherwise live toggles/palette
780 // changes never reach a cross-origin iframe — they'd only sync on reload.
781 darkify_post_theme_to_iframe(iframe);
782
783 // Re-broadcast + re-apply on every (re)load/navigation so content swapped
784 // inside the frame is re-themed. Bind once per iframe to avoid stacking.
785 if (!iframe.dataset.darkifyIframeBound) {
786 iframe.dataset.darkifyIframeBound = "1";
787 iframe.addEventListener("load", function () {
788 darkify_post_theme_to_iframe(iframe);
789 applyDirect();
790 });
791 }
792
793 applyDirect();
794 }
795 function darkify_process_iframes() {
796 if (darkify_is_this_admin_panel === "1") {
797 // Admin panel: only target the Gutenberg editor-canvas iframe
798 const editorCanvas = document.querySelector('iframe[name="editor-canvas"]');
799 if (editorCanvas) darkify_apply_dark_to_iframe(editorCanvas);
800 return;
801 }
802
803 // Respect the "Frontend Iframe Dark Mode" setting.
804 // Existing users without this option saved get the default-on behaviour.
805 if (!darkify_iframe_dark_enabled()) return;
806
807 document.querySelectorAll("iframe").forEach(darkify_apply_dark_to_iframe);
808 }
809
810 // Watch the parent for theme changes (dark toggle, palette switch, customizer
811 // live edits) and re-sync every iframe. darkify_process_iframes re-runs the
812 // var sync, which is what propagates new colours into the frames.
813 let darkify_parent_theme_observer = null;
814 let darkify_iframe_sync_scheduled = false;
815 function darkify_schedule_iframe_sync() {
816 if (darkify_iframe_sync_scheduled) return;
817 darkify_iframe_sync_scheduled = true;
818 requestAnimationFrame(function () {
819 darkify_iframe_sync_scheduled = false;
820 darkify_process_iframes();
821 });
822 }
823 function darkify_watch_parent_theme() {
824 if (darkify_parent_theme_observer) return;
825
826 darkify_parent_theme_observer = new MutationObserver(darkify_schedule_iframe_sync);
827
828 // <html> class (dark on/off) and inline style (block-editor palette vars).
829 darkify_parent_theme_observer.observe(document.documentElement, {
830 attributes: true,
831 attributeFilter: ["class", "style"],
832 });
833
834 // Frontend palette variables live in an inline <style> block; watch its text
835 // so customizer / dynamic edits to the :root variables are picked up too.
836 const inlineCss = document.querySelector("style.darkify_inline_css");
837 if (inlineCss) {
838 darkify_parent_theme_observer.observe(inlineCss, {
839 childList: true,
840 characterData: true,
841 subtree: true,
842 });
843 }
844
845 // In the admin panel, Gutenberg inserts the editor-canvas iframe into the DOM
846 // asynchronously — after the initial darkify_process_iframes() call has
847 // already run. Watch document.body so we re-sync the moment it appears.
848 if (darkify_is_this_admin_panel === "1" && document.body) {
849 var darkify_canvas_dom_observer = new MutationObserver(function () {
850 if (document.querySelector('iframe[name="editor-canvas"]')) {
851 darkify_schedule_iframe_sync();
852 }
853 });
854 darkify_canvas_dom_observer.observe(document.body, {
855 childList: true,
856 subtree: true,
857 });
858 }
859 }
860
861 if (!_dkf_iframe_disabled) {
862 if (document.readyState !== "loading") {
863 darkify_watch_parent_theme();
864 } else {
865 document.addEventListener("DOMContentLoaded", darkify_watch_parent_theme);
866 }
867 }
868
869 // Handshake: an iframe that loads after — or before — the parent is ready can
870 // ask for the current theme, and we reply to that frame directly. This makes
871 // initial sync reliable regardless of which side finishes loading first.
872 window.addEventListener("message", function (event) {
873 const data = event.data;
874 if (!data || data.source !== "darkify" || data.type !== "darkify-request-theme")
875 return;
876 try {
877 if (event.source) {
878 event.source.postMessage(darkify_build_theme_payload(), "*");
879 }
880 } catch (e) {
881 // ignore unreachable source window
882 }
883 });
884
885 function darkify_init_keyboard_shortcut_listener() {
886 if (darkify_enable_keyboard_shortcut === "1") {
887 document.onkeydown = function (event) {
888 if (event.ctrlKey && event.altKey && event.keyCode === 0x44) {
889 darkify_switch_trigger();
890 }
891 };
892 }
893 }
894
895 function darkify_init_os_mode_change_listener() {
896 if (darkify_is_this_admin_panel === "0" && darkify_enable_os_aware === "1") {
897 window
898 .matchMedia("(prefers-color-scheme: dark)")
899 .addEventListener("change", (event) => {
900 const mode = event.matches ? "dark" : "light";
901 const htmlElement = document.getElementsByTagName("html")[0];
902 if (mode === "dark") {
903 htmlElement.classList.add("darkify_dark_mode_enabled");
904 } else if (mode === "light") {
905 htmlElement.classList.remove("darkify_dark_mode_enabled");
906 }
907
908 darkify_change_state();
909 });
910 }
911 }
912
913 function darkify_init_alternative_dark_mode_switch() {
914 if (darkify_alternative_dark_mode_switch.length > 0) {
915 const elements = document.querySelectorAll(
916 darkify_alternative_dark_mode_switch,
917 );
918 for (let i = 0; i < elements.length; i++) {
919 const element = elements[i];
920 element.addEventListener("click", () => {
921 darkify_switch_trigger();
922 });
923 }
924 }
925 }
926
927 function darkify_init_attention_effect() {
928 if (darkify_enable_switch_attention !== "1") return;
929 if (!darkify_switch_attention_effect || darkify_switch_attention_effect === "none") return;
930 var switchEl = document.getElementById("darkify_switch_" + darkify_switch_unique_id);
931 if (switchEl) {
932 switchEl.classList.add("darkify_attention_" + darkify_switch_attention_effect);
933 }
934 }
935
936 function get_bg_color_to_preserve(element, fromDataset) {
937 let color = window.getComputedStyle(element, null).backgroundColor;
938 if (!fromDataset) {
939 color = element.dataset.darkify_preserved_bg;
940 }
941 if (
942 (color === "transparent" ||
943 color === "rgba(0, 0, 0, 0)" ||
944 color === "rgba(255,255,255,0)") &&
945 element.parentNode.nodeType === 1
946 ) {
947 color = get_bg_color_to_preserve(element.parentNode, false);
948 } else if (
949 element.parentNode.nodeType === 1 &&
950 element.parentNode.hasAttribute("data-darkify_preserved_bg") &&
951 window.getComputedStyle(element.parentNode, null).backgroundColor === color
952 ) {
953 color = get_bg_color_to_preserve(element.parentNode, false);
954 }
955 return color;
956 }
957
958 function get_txt_color_to_preserve(element, fromDataset) {
959 let color = window.getComputedStyle(element, null).color;
960 if (!fromDataset) {
961 color = element.dataset.darkify_preserved_color;
962 }
963 if (
964 (color === "transparent" ||
965 color === "rgba(0, 0, 0, 0)" ||
966 color === "rgba(255,255,255,0)") &&
967 element.parentNode.nodeType === 1
968 ) {
969 color = get_txt_color_to_preserve(element.parentNode, false);
970 } else if (
971 element.parentNode.nodeType === 1 &&
972 element.parentNode.hasAttribute("data-darkify_preserved_color") &&
973 window.getComputedStyle(element.parentNode, null).color === color
974 ) {
975 color = get_txt_color_to_preserve(element.parentNode, false);
976 }
977 return color;
978 }
979
980 function darkify_darken_bg_image(element, level) {
981 if (
982 document
983 .getElementsByTagName("html")[0]
984 .classList.contains("darkify_dark_mode_enabled")
985 ) {
986 const mainStyle = window.getComputedStyle(element, null);
987 const beforeStyle = window.getComputedStyle(element, ":before");
988 const afterStyle = window.getComputedStyle(element, ":after");
989
990 if (
991 mainStyle.backgroundImage !== "none" &&
992 mainStyle.backgroundImage.includes("url") &&
993 !mainStyle.backgroundImage.includes("rgba(0, 0, 0, " + level + ")")
994 ) {
995 element.style.setProperty(
996 "background-image",
997 "linear-gradient(rgba(0, 0, 0, " +
998 level +
999 "), rgba(0, 0, 0, " +
1000 level +
1001 ")), " +
1002 mainStyle.backgroundImage,
1003 );
1004 }
1005
1006 // Process :before pseudo-element
1007 if (
1008 beforeStyle.backgroundImage !== "none" &&
1009 beforeStyle.backgroundImage.includes("url") &&
1010 !beforeStyle.backgroundImage.includes("rgba(0, 0, 0, " + level + ")")
1011 ) {
1012 // Create a style element for this specific element
1013 const styleId = `darkify-before-${Math.random()
1014 .toString(36)
1015 .substr(2, 9)}`;
1016 element.setAttribute("data-darkify-style-id", styleId);
1017
1018 let styleElement = document.getElementById(styleId);
1019 if (!styleElement) {
1020 styleElement = document.createElement("style");
1021 styleElement.id = styleId;
1022 document.head.appendChild(styleElement);
1023 }
1024
1025 // Store original background for reset
1026 element.dataset.darkifyOriginalBeforeBg = beforeStyle.backgroundImage;
1027
1028 // Add the styles for :before
1029 const cssText = `
1030 .darkify_dark_mode_enabled [data-darkify-style-id="${styleId}"]::before {
1031 background-image: linear-gradient(rgba(0, 0, 0, ${level}), rgba(0, 0, 0, ${level})), ${beforeStyle.backgroundImage} !important;
1032 }
1033 `;
1034 styleElement.textContent = cssText;
1035
1036 // Ensure position relative on parent
1037 if (window.getComputedStyle(element).position === "static") {
1038 element.style.position = "relative";
1039 }
1040 }
1041 // Process :after pseudo-element
1042 if (
1043 afterStyle.backgroundImage !== "none" &&
1044 afterStyle.backgroundImage.includes("url") &&
1045 !afterStyle.backgroundImage.includes("rgba(0, 0, 0, " + level + ")")
1046 ) {
1047 // Create a style element for this specific element
1048 const styleId = `darkify-after-${Math.random()
1049 .toString(36)
1050 .substr(2, 9)}`;
1051 element.setAttribute("data-darkify-style-id", styleId);
1052
1053 let styleElement = document.getElementById(styleId);
1054 if (!styleElement) {
1055 styleElement = document.createElement("style");
1056 styleElement.id = styleId;
1057 document.head.appendChild(styleElement);
1058 }
1059
1060 // Store original background for reset
1061 element.dataset.darkifyOriginalAfterBg = afterStyle.backgroundImage;
1062
1063 // Add the styles for :after
1064 const cssText = `
1065 .darkify_dark_mode_enabled [data-darkify-style-id="${styleId}"]::after {
1066 background-image: linear-gradient(rgba(0, 0, 0, ${level}), rgba(0, 0, 0, ${level})), ${afterStyle.backgroundImage} !important;
1067 }
1068 `;
1069 styleElement.textContent = cssText;
1070
1071 // Ensure position relative on parent
1072 if (window.getComputedStyle(element).position === "static") {
1073 element.style.position = "relative";
1074 }
1075 }
1076 } else if (
1077 window.getComputedStyle(element, null).backgroundImage !== "none" &&
1078 window
1079 .getComputedStyle(element, null)
1080 .backgroundImage.includes("rgba(0, 0, 0, " + level + ")")
1081 ) {
1082 element.style.setProperty(
1083 "background-image",
1084 window
1085 .getComputedStyle(element, null)
1086 .backgroundImage.replace(
1087 "linear-gradient(rgba(0, 0, 0, " +
1088 level +
1089 "), rgba(0, 0, 0, " +
1090 level +
1091 ")), ",
1092 "",
1093 ),
1094 );
1095 }
1096 }
1097
1098 function darkify_img_brightness_and_grayscale(element) {
1099 if (
1100 document
1101 .getElementsByTagName("html")[0]
1102 .classList.contains("darkify_dark_mode_enabled")
1103 ) {
1104 if (
1105 !element.classList.contains("darkify_changed_brightness_and_grayscale")
1106 ) {
1107 element.dataset.darkify_preserved_filter = element.style.filter;
1108 element.classList.add("darkify_changed_brightness_and_grayscale");
1109
1110 if (
1111 darkify_enable_low_image_brightness === "1" &&
1112 darkify_enable_image_grayscale === "1"
1113 ) {
1114 element.style.filter =
1115 "brightness(" +
1116 darkify_image_brightness_to +
1117 "%)" +
1118 " " +
1119 "grayscale(" +
1120 darkify_image_grayscale_to +
1121 "%)";
1122 } else {
1123 if (darkify_enable_low_image_brightness === "1") {
1124 element.style.filter =
1125 "brightness(" + darkify_image_brightness_to + "%)";
1126 } else if (darkify_enable_image_grayscale === "1") {
1127 element.style.filter =
1128 "grayscale(" + darkify_image_grayscale_to + "%)";
1129 }
1130 }
1131 }
1132 } else if (
1133 element.classList.contains("darkify_changed_brightness_and_grayscale")
1134 ) {
1135 element.style.filter = element.dataset.darkify_preserved_filter;
1136 element.classList.remove("darkify_changed_brightness_and_grayscale");
1137 delete element.dataset.darkify_preserved_filter;
1138 }
1139 }
1140
1141 function darkify_invert_inline_svg(element) {
1142 if (document.body.classList.contains("block-editor-page")) return;
1143 if (
1144 document
1145 .getElementsByTagName("html")[0]
1146 .classList.contains("darkify_dark_mode_enabled")
1147 ) {
1148 element.style.filter = "invert(1)";
1149 element.classList.add("darkify_inverted_inline_svg");
1150 } else if (element.classList.contains("darkify_inverted_inline_svg")) {
1151 element.style.filter = element.style.filter.replace("invert(1)", "");
1152 element.classList.remove("darkify_inverted_inline_svg");
1153 }
1154 }
1155
1156 function darkify_video_brightness_and_grayscale(element) {
1157 if (
1158 document
1159 .getElementsByTagName("html")[0]
1160 .classList.contains("darkify_dark_mode_enabled")
1161 ) {
1162 if (
1163 !element.classList.contains(
1164 "darkify_changed_video_brightness_and_grayscale",
1165 )
1166 ) {
1167 element.dataset.darkify_preserved_filter = element.style.filter;
1168 element.classList.add("darkify_changed_video_brightness_and_grayscale");
1169 if (
1170 darkify_enable_low_video_brightness === "1" &&
1171 darkify_enable_video_grayscale === "1"
1172 ) {
1173 element.style.filter =
1174 "brightness(" +
1175 darkify_video_brightness_to +
1176 "%)" +
1177 " " +
1178 "grayscale(" +
1179 darkify_video_grayscale_to +
1180 "%)";
1181 } else {
1182 if (darkify_enable_low_video_brightness === "1") {
1183 element.style.filter =
1184 "brightness(" + darkify_video_brightness_to + "%)";
1185 } else if (darkify_enable_video_grayscale === "1") {
1186 element.style.filter =
1187 "grayscale(" + darkify_video_grayscale_to + "%)";
1188 }
1189 }
1190 }
1191 } else if (
1192 element.classList.contains("darkify_changed_video_brightness_and_grayscale")
1193 ) {
1194 element.style.filter = element.dataset.darkify_preserved_filter;
1195 element.classList.remove("darkify_changed_video_brightness_and_grayscale");
1196 delete element.dataset.darkify_preserved_filter;
1197 }
1198 }
1199
1200 function darkify_replace_video(videoElement, videos) {
1201 if (
1202 document
1203 .getElementsByTagName("html")[0]
1204 .classList.contains("darkify_dark_mode_enabled")
1205 ) {
1206 for (let i = 0; i < videos.length; i++) {
1207 const normalVideo = videos[i].normal_video;
1208 const normalVideoPath = new URL(normalVideo).pathname;
1209 const darkVideo = videos[i].dark_video;
1210 const darkVideoPath = new URL(darkVideo).pathname;
1211
1212 if (
1213 videoElement.getAttribute("src") != null &&
1214 videoElement.getAttribute("src").includes(normalVideoPath)
1215 ) {
1216 videoElement.src = darkVideo;
1217 videoElement.classList.add("darkify_replaced_video");
1218 }
1219
1220 if (videoElement.querySelectorAll("source") != null) {
1221 let sources = videoElement.querySelectorAll("source");
1222 for (let j = 0; j < sources.length; j++) {
1223 if (
1224 sources[j].getAttribute("src") != null &&
1225 sources[j].getAttribute("src").includes(normalVideoPath)
1226 ) {
1227 sources[j].src = darkVideo + "?_=" + Date.now();
1228 videoElement.classList.add("darkify_replaced_video");
1229 videoElement.load();
1230 }
1231 }
1232 }
1233 }
1234 } else {
1235 if (videoElement.classList.contains("darkify_replaced_video")) {
1236 for (let i = 0; i < videos.length; i++) {
1237 const normalVideo = videos[i].normal_video;
1238 const normalVideoPath = new URL(normalVideo).pathname;
1239 const darkVideo = videos[i].dark_video;
1240 const darkVideoPath = new URL(darkVideo).pathname;
1241
1242 if (
1243 videoElement.getAttribute("src") != null &&
1244 videoElement.getAttribute("src").includes(darkVideoPath)
1245 ) {
1246 videoElement.src = normalVideo;
1247 videoElement.classList.remove("darkify_replaced_video");
1248 }
1249
1250 if (videoElement.querySelectorAll("source") != null) {
1251 let sources = videoElement.querySelectorAll("source");
1252 for (let j = 0; j < sources.length; j++) {
1253 if (
1254 sources[j].getAttribute("src") != null &&
1255 sources[j].getAttribute("src").includes(darkVideoPath)
1256 ) {
1257 sources[j].src = normalVideo + "?_=" + Date.now();
1258 videoElement.classList.remove("darkify_replaced_video");
1259 videoElement.load();
1260 }
1261 }
1262 }
1263 }
1264 }
1265 }
1266 }
1267
1268 function darkify_process_pseudo_bg(element) {
1269 const isTransparent = (color) =>
1270 !color ||
1271 color === "transparent" ||
1272 color === "rgba(0, 0, 0, 0)" ||
1273 color === "rgba(255, 255, 255, 0)";
1274
1275 const beforeBg = window.getComputedStyle(element, "::before").backgroundColor;
1276 const afterBg = window.getComputedStyle(element, "::after").backgroundColor;
1277
1278 if (!isTransparent(beforeBg)) {
1279 element.setAttribute("data-darkify-pseudo-before-bg", beforeBg);
1280 }
1281 if (!isTransparent(afterBg)) {
1282 element.setAttribute("data-darkify-pseudo-after-bg", afterBg);
1283 }
1284 }
1285
1286 function darkify_apply_pseudo_bg_styles() {
1287 document
1288 .querySelectorAll(
1289 "[data-darkify-pseudo-before-bg], [data-darkify-pseudo-after-bg]",
1290 )
1291 .forEach((element) => {
1292 const hasBefore = element.hasAttribute("data-darkify-pseudo-before-bg");
1293 const hasAfter = element.hasAttribute("data-darkify-pseudo-after-bg");
1294
1295 let pseudoId = element.getAttribute("data-darkify-pseudo-bg-id");
1296 if (!pseudoId) {
1297 pseudoId = "dkf-" + Math.random().toString(36).substr(2, 9);
1298 element.setAttribute("data-darkify-pseudo-bg-id", pseudoId);
1299 }
1300
1301 let styleEl = document.getElementById("darkify-pseudo-bg-" + pseudoId);
1302 if (!styleEl) {
1303 styleEl = document.createElement("style");
1304 styleEl.id = "darkify-pseudo-bg-" + pseudoId;
1305 document.head.appendChild(styleEl);
1306 }
1307
1308 let css = "";
1309
1310 if (hasBefore) {
1311 const beforeBg = element.getAttribute("data-darkify-pseudo-before-bg");
1312 const isSecondary =
1313 darkify_secondary_bg_color !== "" &&
1314 beforeBg !== darkify_secondary_bg_color;
1315 const bgVar = isSecondary
1316 ? "--darkify_dark_mode_secondary_bg"
1317 : "--darkify_dark_mode_bg";
1318 css += `
1319 .darkify_dark_mode_enabled [data-darkify-pseudo-bg-id="${pseudoId}"]::before {
1320 background: var(${bgVar}) !important;
1321 background-color: var(${bgVar}) !important;
1322 }
1323 `;
1324 }
1325
1326 if (hasAfter) {
1327 const afterBg = element.getAttribute("data-darkify-pseudo-after-bg");
1328 const isSecondary =
1329 darkify_secondary_bg_color !== "" &&
1330 afterBg !== darkify_secondary_bg_color;
1331 const bgVar = isSecondary
1332 ? "--darkify_dark_mode_secondary_bg"
1333 : "--darkify_dark_mode_bg";
1334 css += `
1335 .darkify_dark_mode_enabled [data-darkify-pseudo-bg-id="${pseudoId}"]::after {
1336 background: var(${bgVar}) !important;
1337 background-color: var(${bgVar}) !important;
1338 }
1339 `;
1340 }
1341
1342 styleEl.textContent = css;
1343 });
1344 }
1345
1346 function darkify_fix_background_color_alpha(element) {
1347 if (
1348 document
1349 .getElementsByTagName("html")[0]
1350 .classList.contains("darkify_dark_mode_enabled")
1351 ) {
1352 if (element.hasAttribute("data-darkify_alpha_bg")) {
1353 var alphaValue = element.dataset.darkify_alpha_bg
1354 .replace("rgba(", "")
1355 .replace(")", "")
1356 .split(",")[3]
1357 .trim();
1358 var backgroundColor = window.getComputedStyle(
1359 element,
1360 null,
1361 ).backgroundColor;
1362
1363 if (!backgroundColor.includes("rgba")) {
1364 element.style.setProperty(
1365 "background-color",
1366 backgroundColor
1367 .replace(")", ", " + alphaValue + ")")
1368 .replace("rgb", "rgba"),
1369 "important",
1370 );
1371 }
1372 }
1373 } else if (element.hasAttribute("data-darkify_alpha_bg")) {
1374 element.style.backgroundColor = "";
1375 }
1376 }
1377
1378 function darkify_implement_secondary_bg() {
1379 let maxAreaElement = null;
1380 let maxArea = 0;
1381
1382 const elements = document.querySelectorAll(
1383 "* :not(head, title, link, meta, script, style, defs, filter)",
1384 );
1385
1386 for (let i = 0; i < elements.length; i++) {
1387 const element = elements[i];
1388 if (element.hasAttribute("data-darkify_secondary_bg_finder")) {
1389 const secondaryBgColor = element.dataset.darkify_secondary_bg_finder;
1390 if (
1391 secondaryBgColor !== "transparent" &&
1392 secondaryBgColor !== "rgba(0, 0, 0, 0)"
1393 ) {
1394 const boundingRect = element.getBoundingClientRect();
1395 const area = boundingRect.width * boundingRect.height;
1396 if (area > maxArea) {
1397 maxArea = area;
1398 maxAreaElement = secondaryBgColor;
1399 }
1400 }
1401 }
1402 }
1403
1404 for (let i = 0; i < elements.length; i++) {
1405 const element = elements[i];
1406 if (element.hasAttribute("data-darkify_secondary_bg_finder")) {
1407 if (
1408 element.classList.contains("darkify_style_all") ||
1409 element.classList.contains("darkify_style_bg_txt") ||
1410 element.classList.contains("darkify_style_bg_border") ||
1411 element.classList.contains("darkify_style_bg")
1412 ) {
1413 const isDifferentSecondaryBg =
1414 maxAreaElement !== element.dataset.darkify_secondary_bg_finder;
1415 if (isDifferentSecondaryBg) {
1416 element.classList.add("darkify_style_secondary_bg");
1417 }
1418 }
1419 delete element.dataset.darkify_secondary_bg_finder;
1420 }
1421 }
1422
1423 darkify_secondary_bg_color = maxAreaElement;
1424 }
1425
1426 function darkify_recheck_on_css_loaded_later() {
1427 document
1428 .querySelectorAll(
1429 ".darkify_style_txt_border, .darkify_style_txt, .darkify_style_border",
1430 )
1431 .forEach(function (element) {
1432 const computedStyle = window.getComputedStyle(element, null);
1433 const backgroundColor = computedStyle.backgroundColor;
1434 if (
1435 backgroundColor !== "rgba(0, 0, 0, 0)" &&
1436 backgroundColor !== "rgba(255, 255, 255, 0)"
1437 ) {
1438 darkify_process_element(element);
1439 }
1440 });
1441 }
1442
1443 function darkify_check_preloading() {
1444 let isPreloaded = false;
1445 const lastState = localStorage.darkify_last_state
1446 ? localStorage.darkify_last_state
1447 : "not_set";
1448 const adminPanelLastState = localStorage.darkify_admin_panel_last_state
1449 ? localStorage.darkify_admin_panel_last_state
1450 : "not_set";
1451
1452 if (darkify_is_this_admin_panel === "1") {
1453 if (adminPanelLastState === "1") {
1454 isPreloaded = true;
1455 }
1456 } else {
1457 if (lastState === "1" || lastState === "0") {
1458 if (lastState === "1") {
1459 isPreloaded = true;
1460 }
1461 } else {
1462 if (darkify_enable_default_dark_mode === "1") {
1463 isPreloaded = true;
1464 }
1465 if (darkify_enable_time_based_dark === "1") {
1466 const currentDate = new Date();
1467 const darkStart = new Date();
1468 const darkStop = new Date();
1469 darkStart.setHours(
1470 parseInt(darkify_time_based_dark_start.split(":")[0]),
1471 );
1472 darkStart.setMinutes(
1473 parseInt(darkify_time_based_dark_start.split(":")[1]),
1474 );
1475 darkStop.setHours(parseInt(darkify_time_based_dark_stop.split(":")[0]));
1476 darkStop.setMinutes(
1477 parseInt(darkify_time_based_dark_stop.split(":")[1]),
1478 );
1479
1480 if (
1481 parseInt(darkify_time_based_dark_stop.split(":")[0]) >=
1482 parseInt(darkify_time_based_dark_start.split(":")[0])
1483 ) {
1484 if (
1485 currentDate.getTime() > darkStart.getTime() &&
1486 currentDate.getTime() < darkStop.getTime()
1487 ) {
1488 isPreloaded = true;
1489 }
1490 } else if (currentDate.getHours() > 12) {
1491 if (
1492 currentDate.getTime() > darkStart.getTime() &&
1493 currentDate.getTime() > darkStop.getTime()
1494 ) {
1495 isPreloaded = true;
1496 }
1497 } else if (
1498 currentDate.getTime() < darkStart.getTime() &&
1499 currentDate.getTime() < darkStop.getTime()
1500 ) {
1501 isPreloaded = true;
1502 }
1503 }
1504 }
1505 }
1506
1507 if (
1508 darkify_is_this_admin_panel === "0" &&
1509 darkify_enable_os_aware === "1" &&
1510 window.matchMedia &&
1511 window.matchMedia("(prefers-color-scheme: dark)").matches &&
1512 lastState !== "1" &&
1513 lastState !== "0"
1514 ) {
1515 isPreloaded = true;
1516 }
1517
1518 return isPreloaded;
1519 }
1520
1521 function darkify_process_element(element) {
1522 var computedStyle = window.getComputedStyle(element, null);
1523 var old_transition = "";
1524
1525 // if (computedStyle.transition !== "all 0s ease 0s") {
1526 // old_transition = computedStyle.transition;
1527 // // element.style.setProperty("transition", "none");
1528 // }
1529
1530 if (
1531 element.classList.contains("darkify_style_all") ||
1532 element.classList.contains("darkify_style_bg_txt") ||
1533 element.classList.contains("darkify_style_bg_border") ||
1534 element.classList.contains("darkify_style_txt_border") ||
1535 element.classList.contains("darkify_style_bg") ||
1536 element.classList.contains("darkify_style_txt") ||
1537 element.classList.contains("darkify_style_border") ||
1538 element.classList.contains("darkify_style_secondary_bg")
1539 ) {
1540 element.classList.remove("darkify_style_all");
1541 element.classList.remove("darkify_style_bg_txt");
1542 element.classList.remove("darkify_style_bg_border");
1543 element.classList.remove("darkify_style_txt_border");
1544 element.classList.remove("darkify_style_bg");
1545 element.classList.remove("darkify_style_txt");
1546 element.classList.remove("darkify_style_border");
1547 element.classList.remove("darkify_style_secondary_bg");
1548 }
1549
1550 var nodeName = element.nodeName.toLowerCase();
1551 var backgroundColor = computedStyle.backgroundColor;
1552 var color = computedStyle.color;
1553 var borderColor = computedStyle.borderColor;
1554 var backgroundImage = computedStyle.backgroundImage;
1555
1556 if (
1557 nodeName === "body" &&
1558 (backgroundColor === "rgba(0, 0, 0, 0)" ||
1559 backgroundColor === "rgba(255, 255, 255, 0)")
1560 ) {
1561 element.style.setProperty("background-color", "rgb(255, 255, 255)");
1562 backgroundColor = window.getComputedStyle(element, null).backgroundColor;
1563 }
1564
1565 if (darkify_disallowed_elements.length > 0) {
1566 if (element.matches(darkify_disallowed_elements)) {
1567 // if (old_transition !== "") {
1568 // element.style.setProperty("transition", old_transition);
1569 // }
1570 // element.classList.remove("darkify_processed");
1571 return;
1572 }
1573 }
1574
1575 var has_background_img_url = false;
1576 if (backgroundImage !== "none" && backgroundImage.includes("url")) {
1577 has_background_img_url = true;
1578 if (darkify_enable_bg_image_darken === "1") {
1579 darkify_darken_bg_image(element, darken_level);
1580 }
1581 }
1582 if (
1583 backgroundColor !== "rgba(0, 0, 0, 0)" &&
1584 backgroundColor !== "rgba(255, 255, 255, 0)" &&
1585 !has_background_img_url
1586 ) {
1587 if (!element.hasAttribute("data-darkify_secondary_bg_finder")) {
1588 element.dataset.darkify_secondary_bg_finder = backgroundColor;
1589 }
1590 if (darkify_secondary_bg_color !== "") {
1591 var isSecondaryBgColorDifferent =
1592 darkify_secondary_bg_color !==
1593 element.dataset.darkify_secondary_bg_finder;
1594 if (isSecondaryBgColorDifferent) {
1595 element.classList.add("darkify_style_secondary_bg");
1596 }
1597 delete element.dataset.darkify_secondary_bg_finder;
1598 }
1599 }
1600 if (
1601 backgroundColor !== "rgba(0, 0, 0, 0)" &&
1602 color !== "rgba(0, 0, 0, 0)" &&
1603 borderColor !== "rgba(0, 0, 0, 0)" &&
1604 backgroundColor !== "rgba(255, 255, 255, 0)" &&
1605 color !== "rgba(255, 255, 255, 0)" &&
1606 borderColor !== "rgba(255, 255, 255, 0)" &&
1607 has_background_img_url === false
1608 ) {
1609 element.classList.add("darkify_style_all");
1610 } else {
1611 if (
1612 backgroundColor !== "rgba(0, 0, 0, 0)" &&
1613 color !== "rgba(0, 0, 0, 0)" &&
1614 backgroundColor !== "rgba(255, 255, 255, 0)" &&
1615 color !== "rgba(255, 255, 255, 0)" &&
1616 has_background_img_url === false
1617 ) {
1618 element.classList.add("darkify_style_bg_txt");
1619 } else {
1620 if (
1621 backgroundColor !== "rgba(0, 0, 0, 0)" &&
1622 borderColor !== "rgba(0, 0, 0, 0)" &&
1623 backgroundColor !== "rgba(255, 255, 255, 0)" &&
1624 borderColor !== "rgba(255, 255, 255, 0)" &&
1625 has_background_img_url === false
1626 ) {
1627 element.classList.add("darkify_style_bg_border");
1628 } else {
1629 if (
1630 color !== "rgba(0, 0, 0, 0)" &&
1631 borderColor !== "rgba(0, 0, 0, 0)" &&
1632 color !== "rgba(255, 255, 255, 0)" &&
1633 borderColor !== "rgba(255, 255, 255, 0)"
1634 ) {
1635 element.classList.add("darkify_style_txt_border");
1636 } else {
1637 if (
1638 backgroundColor !== "rgba(0, 0, 0, 0)" &&
1639 backgroundColor !== "rgba(255, 255, 255, 0)" &&
1640 has_background_img_url === false
1641 ) {
1642 element.classList.add("darkify_style_bg");
1643 } else {
1644 if (
1645 color !== "rgba(0, 0, 0, 0)" &&
1646 color !== "rgba(255, 255, 255, 0)"
1647 ) {
1648 element.classList.add("darkify_style_txt");
1649 } else if (
1650 borderColor !== "rgba(0, 0, 0, 0)" &&
1651 borderColor !== "rgba(255, 255, 255, 0)"
1652 ) {
1653 element.classList.add("darkify_style_border");
1654 }
1655 }
1656 }
1657 }
1658 }
1659 }
1660 if (
1661 backgroundImage !== "none" &&
1662 !has_background_img_url &&
1663 !element.classList.contains("darkify_style_all") &&
1664 !element.classList.contains("darkify_style_bg_txt") &&
1665 !element.classList.contains("darkify_style_bg_border") &&
1666 !element.classList.contains("darkify_style_bg")
1667 ) {
1668 element.classList.add("darkify_style_secondary_bg");
1669 }
1670
1671 if (nodeName === "a") {
1672 element.classList.add("darkify_style_link");
1673 }
1674
1675 if (
1676 nodeName === "input" ||
1677 nodeName === "select" ||
1678 nodeName === "textarea"
1679 ) {
1680 element.classList.add("darkify_style_form_element");
1681 }
1682
1683 const hasTargetClass = darkify_allowed_btn_class.some((cls) =>
1684 element.classList.contains(cls),
1685 );
1686
1687 if (nodeName === "button" || hasTargetClass || element.type === "submit") {
1688 element.classList.add("darkify_style_button");
1689 element.classList.remove("darkify_style_secondary_bg");
1690 element.classList.remove("darkify_style_all");
1691 element.classList.remove("darkify_style_link");
1692 }
1693
1694 if (
1695 (darkify_enable_low_image_brightness === "1" ||
1696 darkify_enable_image_grayscale === "1") &&
1697 nodeName === "img"
1698 ) {
1699 darkify_img_brightness_and_grayscale(element);
1700 }
1701
1702 if (darkify_enable_invert_inline_svg === "1" && nodeName === "svg") {
1703 darkify_invert_inline_svg(element);
1704 }
1705
1706 if (
1707 darkify_enable_low_video_brightness === "1" ||
1708 darkify_enable_video_grayscale === "1"
1709 ) {
1710 if (nodeName === "video") {
1711 darkify_video_brightness_and_grayscale(element);
1712 }
1713
1714 if (nodeName === "iframe") {
1715 const srcAttribute = element.getAttribute("src");
1716 if (srcAttribute !== null) {
1717 if (
1718 srcAttribute.includes("youtube") ||
1719 srcAttribute.includes("vimeo") ||
1720 srcAttribute.includes("dailymotion")
1721 ) {
1722 darkify_video_brightness_and_grayscale(element);
1723 }
1724 }
1725 }
1726 }
1727
1728 if (backgroundColor.includes("rgba")) {
1729 element.dataset.darkify_alpha_bg = backgroundColor;
1730 darkify_fix_background_color_alpha(element);
1731 }
1732
1733 darkify_process_pseudo_bg(element);
1734
1735 // if (old_transition !== "") {
1736 // setTimeout(function () {
1737 // element.style.setProperty("transition", old_transition);
1738 // }, 0);
1739 // }
1740
1741 setTimeout(function () {
1742 elements_class_changed.observe(element, {
1743 attributes: true,
1744 attributeFilter: ["class"],
1745 });
1746 }, 0);
1747
1748 element.classList.add("darkify_processed");
1749 }
1750
1751 function darkify_init_processes() {
1752 has_process_run_at_least_once = true;
1753 document
1754 .querySelectorAll(
1755 "* :not(head, title, link, meta, script, style, defs, filter, .darkify_processed)",
1756 )
1757 .forEach(function (element) {
1758 darkify_process_element(element);
1759 });
1760 }
1761
1762 function darkify_init_observer() {
1763 darkify_observer.observe(document, {
1764 attributes: false,
1765 childList: true,
1766 characterData: false,
1767 subtree: true,
1768 });
1769
1770 dark_mode_status_changed.observe(document.getElementsByTagName("html")[0], {
1771 attributes: true,
1772 });
1773
1774 if (document.readyState !== "loading") {
1775 if (!has_process_run_at_least_once) {
1776 darkify_init_processes();
1777 }
1778 darkify_implement_secondary_bg();
1779 darkify_apply_pseudo_bg_styles();
1780 darkify_recheck_on_css_loaded_later();
1781 darkify_restore_selected_theme();
1782 } else {
1783 document.addEventListener("DOMContentLoaded", function () {
1784 if (!has_process_run_at_least_once) {
1785 darkify_init_processes();
1786 }
1787 darkify_implement_secondary_bg();
1788 darkify_apply_pseudo_bg_styles();
1789 darkify_recheck_on_css_loaded_later();
1790 darkify_restore_selected_theme();
1791 });
1792 }
1793 }
1794
1795 if (!_dkf_iframe_disabled && darkify_check_preloading()) {
1796 document
1797 .getElementsByTagName("html")[0]
1798 .classList.add("darkify_dark_mode_enabled");
1799 darkify_init_observer();
1800 darkify_process_iframes(); // �
1801 darkify proceed iframe
1802 }
1803
1804 if (document.readyState !== "loading") {
1805 darkify_restore_selected_theme();
1806 } else {
1807 document.addEventListener("DOMContentLoaded", darkify_restore_selected_theme);
1808 }
1809