PluginProbe
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) / 1.3.2
Darkify – Dark Mode & Night Mode for Website & Admin (Dark Theme Included) v1.3.2
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
← All changes | src/Frontend/Frontend.php +83 -384 2.0.41.3.2 View file →
@@ -1,15 +1,12 @@
1 1 <?php
2 2
3 3 namespace ThemeAtelier\Darkify\Frontend;
4 4
5 -use ThemeAtelier\Darkify\Includes\DarkifyExternalSupport;
6 -use ThemeAtelier\Darkify\Includes\DarkifyUtils;
5 +use ThemeAtelier\Darkify\Helpers\DarkifyExternalSupport;
6 +use ThemeAtelier\Darkify\Helpers\DarkifyUtils;
7 +use ThemeAtelier\Darkify\Helpers\Helpers;
7 8
8 -if (!defined('ABSPATH')) {
9 - die;
10 -} // Cannot access directly.
11 -
12 9 /**
13 10 * The public-facing functionality of the plugin.
14 11 *
15 12 * @link https://themeatelier.net
@@ -75,18 +72,17 @@
75 72 if (function_exists('wp_rand')) {
76 73 $this->unique_id = wp_rand();
77 74 }
78 75
79 - // Register unconditionally and gate inside the callback. The constructor
80 - // runs at plugin boot (before `init`), so the option read here is always
81 - // the SAVED value — deciding registration here means the admin Live
82 - // Preview, which swaps in unsaved values only on `init` (see
83 - // PreviewRest::maybe_enable_preview), could never show a newly enabled
84 - // menu switch until saved. The filter runs at render time, after `init`,
85 - // so re-reading the option in the callback sees the preview values.
86 - add_filter('wp_nav_menu_items', array($this, 'darkify_switch_in_menu'), 10, 2);
76 + $options = get_option('darkify');
77 + $show_in_menu = isset($options['show_in_menu']) ? $options['show_in_menu'] : "";
78 + $enable_switcher_in_menu = isset($show_in_menu["enable_switch_in_menu"]) ? $show_in_menu["enable_switch_in_menu"] : false;
87 79
88 - add_action('wp_enqueue_scripts', array($this, 'darkify_client_enqueue'), 100);
80 + if ($enable_switcher_in_menu) {
81 + add_filter('wp_nav_menu_items', array($this, 'darkify_switch_in_menu'), 10, 2);
82 + }
83 +
84 + add_action('wp_enqueue_scripts', array($this, 'darkify_client_enqueue'));
89 85 add_action('login_enqueue_scripts', array($this, 'darkify_client_enqueue'));
90 86 add_action('register_enqueue_scripts', array($this, 'darkify_client_enqueue'));
91 87 add_action('wp_head', array($this, 'darkify_client_header_script'), 1);
92 88 add_action('login_head', array($this, 'darkify_client_header_script'), 1);
@@ -93,367 +89,87 @@
93 89 add_action('register_head', array($this, 'darkify_client_header_script'), 1);
94 90 add_action('wp_footer', array($this, 'darkify_client_footer_script'));
95 91 add_action('login_footer', array($this, 'darkify_client_footer_script'));
96 92 add_action('register_footer', array($this, 'darkify_client_footer_script'));
97 -
98 - add_action('init', array($this, 'darkify_register_switcher_styles'), 5);
99 - add_filter('autoptimize_filter_js_exclude', array($this, 'darkify_exclude_js_from_cache_plugins'));
100 93 }
101 94
102 - public function darkify_exclude_js_from_cache_plugins($excludes)
103 - {
104 - $excludes .= ',client_main.js,client_main.min.js';
105 - return $excludes;
106 - }
107 95
108 - public function darkify_register_switcher_styles()
96 + public function darkify_client_enqueue()
109 97 {
110 - $min = (apply_filters('darkify_dev_mode', false) || WP_DEBUG) ? '' : '.min';
111 - wp_register_style('darkify-client-main', DARKIFY_DIR_URL . 'src/assets/css/client_main' . $min . '.css', array(), DARKIFY_VERSION);
112 - wp_register_style('theme-classic', DARKIFY_DIR_URL . 'src/assets/css/switcher/classic' . $min . '.css', array('darkify-client-main'), DARKIFY_VERSION, 'all');
113 - wp_register_style('theme-expand', DARKIFY_DIR_URL . 'src/assets/css/switcher/expand' . $min . '.css', array('darkify-client-main'), DARKIFY_VERSION, 'all');
114 - wp_register_style('theme-inner-moon', DARKIFY_DIR_URL . 'src/assets/css/switcher/inner-moon' . $min . '.css', array('darkify-client-main'), DARKIFY_VERSION, 'all');
115 - wp_register_style('theme-within', DARKIFY_DIR_URL . 'src/assets/css/switcher/within' . $min . '.css', array('darkify-client-main'), DARKIFY_VERSION, 'all');
116 - wp_register_style('theme-orbit', DARKIFY_DIR_URL . 'src/assets/css/switcher/orbit' . $min . '.css', array('darkify-client-main'), DARKIFY_VERSION, 'all');
117 - }
118 -
119 - function darkify_client_enqueue()
120 - {
121 98 if ($this->darkify_is_dark_mode_allowed()) {
122 99 $options = get_option('darkify');
123 - $min = (apply_filters('darkify_dev_mode', false) || WP_DEBUG) ? '' : '.min';
100 + $enable_dark_switcher = $options['enable_dark_switcher'];
101 + $enable_time = $options['enable_time'];
124 102
125 - wp_enqueue_style('darkify-client-main', DARKIFY_DIR_URL . 'src/assets/css/client_main' . $min . '.css', array(), DARKIFY_VERSION);
126 - wp_enqueue_script('darkify-client-main', DARKIFY_DIR_URL . 'src/assets/js/client_main' . $min . '.js', array(), DARKIFY_VERSION);
103 + $enable_default_dark_mode = $options['enable_default_dark_mode'];
104 + $enable_time = $options['enable_time'];
105 + $enable_time_based_dark = isset($enable_time['enable_time_based_dark']) ? $enable_time['enable_time_based_dark'] : "";
106 + $time_based_dark_start = isset($enable_time['time_based_dark_start']['from']) ? $enable_time['time_based_dark_start']['from'] : "";
107 + $time_based_dark_stop = isset($enable_time['time_based_dark_start']['to']) ? $enable_time['time_based_dark_start']['to'] : "";
108 + $grayscale = $options['brightness'];
109 + $enable_low_image_brightness = isset($grayscale['enable_low_image_brightness']) ? $grayscale['enable_low_image_brightness'] : "";
110 + $low_image_brightness_label = isset($grayscale['low_image_brightness_label']) ? $grayscale['low_image_brightness_label'] : "";
111 + $grayscale = isset($options['grayscale']);
112 + $enable_image_grayscale = isset($grayscale['enable_image_grayscale']) ? $grayscale['enable_image_grayscale'] : "";
113 + $image_grayscale_label = isset($grayscale['image_grayscale_label']) ? $grayscale['image_grayscale_label'] : "";
114 + $disallowed_low_grayscale_images = isset($grayscale['disallowed_low_grayscale_images']) ? $grayscale['disallowed_low_grayscale_images'] : "";
115 + $darken_background = isset($options['darken_background']);
116 + $enable_low_image_darken = isset($darken_background['enable_low_image_darken']) ? $darken_background['enable_low_image_darken'] : "";
117 + $low_image_darken_label = isset($darken_background['low_image_darken_label']) ? $darken_background['low_image_darken_label'] : "";
118 + $video_brightness = isset($options['video_brightness']);
119 + $enable_low_video_brightness = isset($video_brightness['enable_low_video_brightness']) ? $video_brightness['enable_low_video_brightness'] : "";
120 + $low_video_brightness_label = isset($video_brightness['low_video_brightness_label']) ? $video_brightness['low_video_brightness_label'] : "";
121 + $video_grayscale = isset($options['video_grayscale']);
122 + $enable_video_grayscale = isset($video_grayscale['enable_video_grayscale']) ? $video_grayscale['enable_video_grayscale'] : "";
127 123
128 - // When "Frontend Iframe Dark Mode" is OFF, use a two-stage inline script
129 - // strategy that covers all scenarios — source JS, minified JS,
130 - // cross-origin iframes, and same-origin WP pages embedded as iframes.
131 - //
132 - // ROOT CAUSE: a same-origin WP page loaded inside an <iframe> has its
133 - // OWN copy of client_main[.min].js running. That copy calls
134 - // darkify_check_preloading() → localStorage.darkify_last_state="1" →
135 - // adds darkify_dark_mode_enabled to the iframe's <html> → goes dark.
136 - // The parent's darkify_process_iframes guard is irrelevant here.
137 - //
138 - // Stage 1 — 'before' (runs BEFORE client_main[.min].js on EVERY page):
139 - // If this page is rendering inside a frontend iframe, intercept the
140 - // localStorage.darkify_last_state property via Object.defineProperty so
141 - // that darkify_check_preloading() always reads "0" and returns false.
142 - // This prevents the iframe page's own Darkify from initialising dark
143 - // mode at all — works with both source and minified JS because
144 - // darkify_check_preloading() always reads the property directly.
145 - // The source JS also has a _dkf_iframe_disabled guard for belt-and-
146 - // suspenders coverage.
147 - //
148 - // Stage 2 — 'after' (runs AFTER client_main[.min].js on EVERY page):
149 - // • Cleans up the localStorage property intercept from Stage 1.
150 - // • Overrides the five injection helpers so the PARENT page's
151 - // darkify_apply_dark_to_iframe() applyDirect() closures (which look
152 - // up these functions by name in the global scope at call time) become
153 - // no-ops for cross-origin and not-yet-loaded iframes.
154 - $enable_frontend_iframe = isset($options['enable_frontend_iframe_dark_mode']) ? $options['enable_frontend_iframe_dark_mode'] : true;
155 - if (!$enable_frontend_iframe) {
156 124
157 - // Stage 1 — intercept localStorage.darkify_last_state BEFORE client_main.
158 - wp_add_inline_script(
159 - 'darkify-client-main',
160 - '(function(){' .
161 - // Only apply when this window is a child frame, the setting
162 - // exists and is OFF, and it is not the admin panel context.
163 - 'if(window===window.top)return;' .
164 - 'if(typeof darkify_enable_frontend_iframe_dark_mode==="undefined"' .
165 - '||darkify_enable_frontend_iframe_dark_mode==="1")return;' .
166 - 'if(typeof darkify_is_this_admin_panel!=="undefined"' .
167 - '&&darkify_is_this_admin_panel==="1")return;' .
168 - // Define an own property on localStorage that intercepts reads
169 - // of darkify_last_state. darkify_check_preloading() uses
170 - // localStorage.darkify_last_state (property access, not getItem),
171 - // so this getter is hit every time the function runs.
172 - // Returning "0" makes the function take the "was explicitly set
173 - // to 0 → not preloaded" branch and return false.
174 - // The setter is silently ignored so state writes in the iframe
175 - // don't pollute the shared localStorage for other tabs/pages.
176 - 'try{' .
177 - 'Object.defineProperty(localStorage,"darkify_last_state",{' .
178 - 'get:function(){return"0";},set:function(){},configurable:true' .
179 - '});' .
180 - '}catch(e){' .
181 - // Fallback for environments where Object.defineProperty on
182 - // Storage objects is not supported: temporarily set the value.
183 - 'try{window._dkf_ls_bk=localStorage.darkify_last_state;' .
184 - 'localStorage.darkify_last_state="0";}catch(e2){}' .
185 - '}' .
186 - '})();',
187 - 'before'
188 - );
189 125
190 - // Stage 2 — cleanup + injection helper overrides AFTER client_main.
191 - wp_add_inline_script(
192 - 'darkify-client-main',
193 - '(function(){' .
194 - // Remove the own-property intercept so subsequent reads of
195 - // localStorage.darkify_last_state return the real stored value.
196 - // "delete" removes the own property, restoring prototype lookup.
197 - 'if(window!==window.top){' .
198 - 'try{delete localStorage.darkify_last_state;}catch(e){' .
199 - // Fallback: restore saved value
200 - 'try{if(typeof window._dkf_ls_bk!=="undefined"){' .
201 - 'if(window._dkf_ls_bk)localStorage.darkify_last_state=window._dkf_ls_bk;' .
202 - 'else localStorage.removeItem("darkify_last_state");' .
203 - '}delete window._dkf_ls_bk;}catch(e2){}' .
204 - '}' .
205 - '}' .
206 - // Override injection helpers so the PARENT page's applyDirect()
207 - // closures (which already hold references to these global
208 - // functions) become no-ops for frontend iframes.
209 - 'function _dkfAdmin(){' .
210 - 'return typeof darkify_is_this_admin_panel!=="undefined"&&darkify_is_this_admin_panel==="1";' .
211 - '}' .
212 - 'if(typeof darkify_process_iframes==="function"){' .
213 - 'var _op=darkify_process_iframes;' .
214 - 'darkify_process_iframes=function(){if(_dkfAdmin())return _op();};' .
215 - '}' .
216 - 'if(typeof darkify_inject_css_into_iframe==="function"){' .
217 - 'var _oi=darkify_inject_css_into_iframe;' .
218 - 'darkify_inject_css_into_iframe=function(d){if(_dkfAdmin())return _oi(d);};' .
219 - '}' .
220 - 'if(typeof darkify_sync_iframe_vars==="function"){' .
221 - 'var _ov=darkify_sync_iframe_vars;' .
222 - 'darkify_sync_iframe_vars=function(d){if(_dkfAdmin())return _ov(d);};' .
223 - '}' .
224 - 'if(typeof darkify_run_engine_on_iframe==="function"){' .
225 - 'var _oe=darkify_run_engine_on_iframe;' .
226 - 'darkify_run_engine_on_iframe=function(d){if(_dkfAdmin())return _oe(d);};' .
227 - '}' .
228 - 'if(typeof darkify_inject_block_editor_css_into_iframe==="function"){' .
229 - 'var _ob=darkify_inject_block_editor_css_into_iframe;' .
230 - 'darkify_inject_block_editor_css_into_iframe=function(d){if(_dkfAdmin())return _ob(d);};' .
231 - '}' .
232 - '})();',
233 - 'after'
234 - );
235 - }
126 + wp_enqueue_style("theme-{$enable_dark_switcher}", DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/$enable_dark_switcher.css", array(), DARKIFY_VERSION, 'all');
127 + wp_register_style('theme-around', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/around.css", array(), DARKIFY_VERSION, 'all');
128 + wp_register_style('theme-classic', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/classic.css", array(), DARKIFY_VERSION, 'all');
129 + wp_register_style('theme-dark-inner', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/dark-inner.css", array(), DARKIFY_VERSION, 'all');
130 + wp_register_style('theme-dark-side', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/dark-side.css", array(), DARKIFY_VERSION, 'all');
131 + wp_register_style('theme-eclipse', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/eclipse.css", array(), DARKIFY_VERSION, 'all');
132 + wp_register_style('theme-expand', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/expand.css", array(), DARKIFY_VERSION, 'all');
133 + wp_register_style('theme-half-sun', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/half-sun.css", array(), DARKIFY_VERSION, 'all');
134 + wp_register_style('theme-horizon', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/horizon.css", array(), DARKIFY_VERSION, 'all');
135 + wp_register_style('theme-inner-moon', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/inner-moon.css", array(), DARKIFY_VERSION, 'all');
136 + wp_register_style('theme-lightbulb', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/lightbulb.css", array(), DARKIFY_VERSION, 'all');
137 + wp_register_style('theme-simple', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/simple.css", array(), DARKIFY_VERSION, 'all');
138 + wp_register_style('theme-within', DARKIFY_DIR_URL . "src/Frontend/assets/css/switcher/within.css", array(), DARKIFY_VERSION, 'all');
139 + wp_enqueue_style('darkify-client-main', DARKIFY_DIR_URL . 'src/assets/css/client_main.css', array(), DARKIFY_VERSION);
140 + wp_enqueue_style('ico-font', DARKIFY_DIR_URL . 'src/assets/css/icofont.css', array(), DARKIFY_VERSION);
141 + wp_enqueue_script('darkify-client-main', DARKIFY_DIR_URL . 'src/assets/js/client_main.js', array('jquery'), DARKIFY_VERSION);
236 142
237 - // Enqueue the global floating switcher style from settings
238 - $enable_dark_mode_switch = isset($options['enable_dark_mode_switch']) ? $options['enable_dark_mode_switch'] : true;
239 - if ($enable_dark_mode_switch) {
240 - $global_variant = isset($options['enable_dark_switcher']) ? $options['enable_dark_switcher'] : 'classic';
241 - wp_enqueue_style('theme-' . $global_variant);
242 -
243 - // Separate mobile floating switcher (Different Switch in Mobile) — its toggler
244 - // style may differ from the default, so enqueue that variant's CSS as well.
245 - if (!empty($options['dark_switcher_switch_in_mobile'])) {
246 - $mobile_variant = isset($options['enable_dark_switcher_in_mobile']) ? $options['enable_dark_switcher_in_mobile'] : 'classic';
247 - wp_enqueue_style('theme-' . $this->darkify_switch_to_variant($mobile_variant));
248 - }
249 - }
250 -
251 - // Detect [darkify] shortcode usage in current post and enqueue its variant
252 - $post = get_post();
253 - if ($post && !empty($post->post_content)) {
254 - if (has_shortcode($post->post_content, 'darkify')) {
255 - preg_match_all('/\[darkify\b[^\]]*\bswitch=["\']([^"\']+)["\'][^\]]*\]/i', $post->post_content, $matches);
256 - $used_switches = !empty($matches[1]) ? $matches[1] : [];
257 - foreach ($used_switches as $switch_val) {
258 - wp_enqueue_style('theme-' . $this->darkify_switch_to_variant($switch_val));
259 - }
260 - // Shortcode without switch= attribute defaults to classic
261 - preg_match_all('/\[darkify\b/i', $post->post_content, $all_sc);
262 - if (count($all_sc[0]) > count($used_switches)) {
263 - wp_enqueue_style('theme-classic');
264 - }
265 - }
266 -
267 - // Detect darkify/switcher Gutenberg block usage and enqueue its variant
268 - if (function_exists('has_block') && has_block('darkify/switcher', $post)) {
269 - foreach ($this->darkify_get_block_variants($post->post_content) as $variant) {
270 - wp_enqueue_style('theme-' . $variant);
271 - }
272 - }
273 - }
274 -
275 - // Detect switcher used in nav menus and enqueue its variant
276 - $show_in_menu = isset($options['show_in_menu']) ? $options['show_in_menu'] : [];
277 - $enable_in_menu = isset($show_in_menu['enable_switch_in_menu']) ? $show_in_menu['enable_switch_in_menu'] : false;
278 - if ($enable_in_menu) {
279 - $select_menus = isset($show_in_menu['select_menus']) ? $show_in_menu['select_menus'] : [];
280 - foreach ($select_menus as $select_menu) {
281 - $shortcode = $select_menu['darkify_menu_shortcode_helper'] ?? '';
282 - if ($shortcode) {
283 - preg_match('/\bswitch=["\']([^"\']+)["\']/', $shortcode, $m);
284 - $switch_val = $m[1] ?? '1';
285 - wp_enqueue_style('theme-' . $this->darkify_switch_to_variant($switch_val));
286 - }
287 - }
288 - }
143 + $darkify_data = array(
144 + 'darkify_switch_unique_id' => $this->unique_id,
145 + 'darkify_is_this_admin_panel' => is_admin() ? "1" : "0",
146 + 'darkify_enable_default_dark_mode' => $enable_default_dark_mode,
147 + 'darkify_enable_os_aware' => $options["enable_os_aware"],
148 + 'darkify_enable_keyboard_shortcut' => $options["enable_keyboard_shortcut"],
149 + 'darkify_enable_time_based_dark' => $enable_time_based_dark,
150 + 'darkify_time_based_dark_start' => $time_based_dark_start ? $time_based_dark_start : "19:00",
151 + 'darkify_time_based_dark_stop' => $time_based_dark_stop ? $time_based_dark_stop : "07:00",
152 + 'darkify_alternative_dark_mode_switch' => $options["alternative_dark_mode_switcher"],
153 + 'darkify_enable_low_image_brightness' => $enable_low_image_brightness,
154 + 'darkify_image_brightness_to' => $low_image_brightness_label,
155 + 'darkify_enable_image_grayscale' => $enable_image_grayscale,
156 + 'darkify_image_grayscale_to' => $image_grayscale_label,
157 + 'darkify_disallowed_grayscale_images' => $disallowed_low_grayscale_images,
158 + 'darkify_enable_bg_image_darken' => $enable_low_image_darken,
159 + 'darkify_bg_image_darken_to' => $low_image_darken_label,
160 + 'darkify_enable_invert_inline_svg' => $options["enable_invert_inline_svg"],
161 + 'darkify_enable_low_video_brightness' => $enable_low_video_brightness,
162 + 'darkify_video_brightness_to' => $low_video_brightness_label,
163 + 'darkify_enable_video_grayscale' => $enable_video_grayscale,
164 + // Add more variables as needed...
165 + );
166 + wp_localize_script('darkify-client-main', 'darkify_data', $darkify_data);
289 167 }
290 168 }
291 -
292 - private function darkify_switch_to_variant($switch_value)
293 - {
294 - $map = [
295 - '1' => 'classic', '2' => 'expand', '3' => 'inner-moon',
296 - '4' => 'within', '5' => 'orbit',
297 - ];
298 - if (is_numeric($switch_value)) {
299 - return $map[(string) $switch_value] ?? 'classic';
300 - }
301 - return $switch_value ?: 'classic';
302 - }
303 -
304 - private function darkify_get_block_variants($content)
305 - {
306 - $variants = [];
307 - $this->darkify_collect_block_variants(parse_blocks($content), $variants);
308 - return array_unique($variants);
309 - }
310 -
311 - private function darkify_collect_block_variants($blocks, &$variants)
312 - {
313 - foreach ($blocks as $block) {
314 - if ('darkify/switcher' === $block['blockName']) {
315 - $variants[] = $block['attrs']['style'] ?? 'classic';
316 - }
317 - if (!empty($block['innerBlocks'])) {
318 - $this->darkify_collect_block_variants($block['innerBlocks'], $variants);
319 - }
320 - }
321 -
322 - $options = get_option('darkify');
323 - $min = (apply_filters('darkify_dev_mode', false) || WP_DEBUG) ? '' : '.min';
324 -
325 - // Always register all variant styles so page builders (Elementor, etc.) can reference them.
326 - wp_register_style('darkify-client-main', DARKIFY_DIR_URL . 'src/assets/css/client_main' . $min . '.css', array(), DARKIFY_VERSION);
327 - wp_register_style('theme-classic', DARKIFY_DIR_URL . 'src/assets/css/switcher/classic' . $min . '.css', array('darkify-client-main'), DARKIFY_VERSION, 'all');
328 - wp_register_style('theme-expand', DARKIFY_DIR_URL . 'src/assets/css/switcher/expand' . $min . '.css', array('darkify-client-main'), DARKIFY_VERSION, 'all');
329 - wp_register_style('theme-inner-moon', DARKIFY_DIR_URL . 'src/assets/css/switcher/inner-moon' . $min . '.css', array('darkify-client-main'), DARKIFY_VERSION, 'all');
330 - wp_register_style('theme-orbit', DARKIFY_DIR_URL . 'src/assets/css/switcher/orbit' . $min . '.css', array('darkify-client-main'), DARKIFY_VERSION, 'all');
331 - wp_register_style('theme-within', DARKIFY_DIR_URL . 'src/assets/css/switcher/within' . $min . '.css', array('darkify-client-main'), DARKIFY_VERSION, 'all');
332 -
333 - // Determine which variants are actually needed on this page.
334 - $variants = $this->darkify_collect_page_variants($options);
335 -
336 - if (empty($variants)) {
337 - return;
338 - }
339 -
340 - // Enqueue main CSS + only the required variant CSS — all resolved before wp_head fires.
341 - wp_enqueue_style('darkify-client-main');
342 - foreach ($variants as $variant) {
343 - wp_enqueue_style("theme-{$variant}");
344 - }
345 - wp_enqueue_script('darkify-client-main', DARKIFY_DIR_URL . 'src/assets/js/client_main' . $min . '.js', array(), DARKIFY_VERSION);
346 - }
347 -
348 - /**
349 - * Collects all switcher variants needed on the current page so they can be
350 - * enqueued during wp_enqueue_scripts (before wp_head), guaranteeing CSS loads
351 - * in the <head> regardless of page builder or theme.
352 - */
353 - private function darkify_collect_page_variants($options)
354 - {
355 - $all_variants = array('classic', 'expand', 'inner-moon', 'orbit', 'within');
356 - $num_map = array('1' => 'classic', '2' => 'expand', '3' => 'inner-moon', '4' => 'within', '5' => 'orbit');
357 - $variants = array();
358 -
359 - // 1. Global / floating switcher variant (always present when dark mode is active).
360 - $global_variant = isset($options['enable_dark_switcher']) ? $options['enable_dark_switcher'] : 'classic';
361 - if (in_array($global_variant, $all_variants, true)) {
362 - $variants[] = $global_variant;
363 - }
364 -
365 - // 2. Menu switcher — parse variant from each saved shortcode helper string.
366 - $show_in_menu = isset($options['show_in_menu']) ? $options['show_in_menu'] : array();
367 - if (!empty($show_in_menu['enable_switch_in_menu']) && !empty($show_in_menu['select_menus'])) {
368 - foreach ($show_in_menu['select_menus'] as $menu_item) {
369 - $helper = isset($menu_item['darkify_menu_shortcode_helper']) ? $menu_item['darkify_menu_shortcode_helper'] : '';
370 - if ($helper && preg_match('/switch=["\']?([^"\'>\s\]]+)["\']?/', $helper, $m)) {
371 - $v = isset($num_map[$m[1]]) ? $num_map[$m[1]] : $m[1];
372 - if (in_array($v, $all_variants, true)) {
373 - $variants[] = $v;
374 - }
375 - }
376 - }
377 - }
378 -
379 - // 3. Page-level detection — shortcodes, Gutenberg blocks, Elementor widget.
380 - global $post;
381 - if ($post) {
382 - $content = $post->post_content;
383 -
384 - // Shortcodes: parse switch attribute from every [darkify] tag.
385 - if (has_shortcode($content, 'darkify')) {
386 - preg_match_all('/\[darkify[^\]]*switch=["\']?([^"\'>\s\]]+)/', $content, $sc_matches);
387 - foreach ($sc_matches[1] as $switch_val) {
388 - $v = isset($num_map[$switch_val]) ? $num_map[$switch_val] : $switch_val;
389 - if (in_array($v, $all_variants, true)) {
390 - $variants[] = $v;
391 - } else {
392 - $variants[] = 'classic'; // default when no valid variant found
393 - }
394 - }
395 - // If [darkify] exists but has no switch attribute, default to classic.
396 - if (empty($sc_matches[1]) && has_shortcode($content, 'darkify')) {
397 - $variants[] = 'classic';
398 - }
399 - }
400 -
401 - // Gutenberg blocks: walk the block tree and collect style attributes.
402 - if (function_exists('has_block') && has_block('darkify/switcher', $post)) {
403 - $blocks = parse_blocks($content);
404 - $variants = array_merge($variants, $this->darkify_extract_block_variants($blocks, $all_variants));
405 - }
406 -
407 - // Elementor widget: if Darkify widget appears in the page's Elementor data,
408 - // load all variants — the specific style is stored as a control value that
409 - // would require deep JSON parsing to extract reliably.
410 - if (class_exists('\Elementor\Plugin')) {
411 - $elementor_data = get_post_meta($post->ID, '_elementor_data', true);
412 - if ($elementor_data && strpos($elementor_data, '"widgetType":"darkify"') !== false) {
413 - return $all_variants;
414 - }
415 - }
416 - }
417 -
418 - return array_values(array_unique(array_filter($variants)));
419 - }
420 -
421 - /**
422 - * Recursively walks a parsed block tree and collects darkify/darkify style values.
423 - */
424 - private function darkify_extract_block_variants($blocks, $all_variants)
425 - {
426 - $variants = array();
427 - foreach ($blocks as $block) {
428 - if ('darkify/switcher' === $block['blockName']) {
429 - $style = isset($block['attrs']['style']) ? $block['attrs']['style'] : 'classic';
430 - if (in_array($style, $all_variants, true)) {
431 - $variants[] = $style;
432 - }
433 - }
434 - if (!empty($block['innerBlocks'])) {
435 - $variants = array_merge($variants, $this->darkify_extract_block_variants($block['innerBlocks'], $all_variants));
436 - }
437 - }
438 - return $variants;
439 - }
440 169 public function darkify_is_dark_mode_allowed()
441 170 {
442 171 $options = get_option('darkify');
443 -
444 - // "Frontend Dark Mode" (enable_dark_mode_switch) is the MASTER control
445 - // for the whole frontend feature. When it's off, load NOTHING on the
446 - // front end — no CSS, no JS, no inline output — since every asset and
447 - // inline script path is gated through this method. (Admin dark mode is a
448 - // separate option handled elsewhere and is unaffected.)
449 - $enable_dark_mode_switch = isset($options['enable_dark_mode_switch'])
450 - ? $options['enable_dark_mode_switch']
451 - : true;
452 - if (! $enable_dark_mode_switch) {
453 - return False;
454 - }
455 -
456 172 /* Disable if Oxygen Builder is Opened */
457 173 if (isset($_GET['ct_builder'])) {
458 174 if ($_GET['ct_builder'] == "true") {
459 175 if (!isset($_GET['oxygen_iframe'])) {
@@ -468,9 +184,9 @@
468 184
469 185 public function darkify_client_header_script()
470 186 {
471 187 if ($this->darkify_is_dark_mode_allowed()) {
472 - include_once DarkifyUtils::darkify_locate_template('header_script.php');
188 + include_once Helpers::darkify_locate_template('header_script.php');
473 189 }
474 190 }
475 191
476 192 public function darkify_client_footer_script()
@@ -475,42 +191,25 @@
475 191
476 192 public function darkify_client_footer_script()
477 193 {
478 194 if ($this->darkify_is_dark_mode_allowed()) {
479 - include_once DarkifyUtils::darkify_locate_template('footer_script.php');
195 + include_once Helpers::darkify_locate_template('footer_script.php');
480 196 }
481 197 }
482 198
483 - function darkify_switch_in_menu($items, $args)
199 + public function darkify_switch_in_menu($items, $args)
484 200 {
485 201 $options = get_option('darkify');
486 - $show_in_menu = isset($options['show_in_menu']) ? $options['show_in_menu'] : '';
202 + $show_in_menu = $options['show_in_menu'];
203 + $switch_in_menu_location = $show_in_menu['switch_in_menu_location'];
204 + $darkify_menu_shortcode_helper = $show_in_menu['darkify_menu_shortcode_helper'];
487 205
488 - // Gate here (not at filter registration) so the live preview's unsaved
489 - // "Enable Menu Switch" toggle takes effect without saving.
490 - $enable_switcher_in_menu = isset($show_in_menu["enable_switch_in_menu"]) ? $show_in_menu["enable_switch_in_menu"] : false;
491 - if (! $enable_switcher_in_menu) {
492 - return $items;
493 - }
494 -
495 - $select_menus = isset($show_in_menu['select_menus']) ? $show_in_menu['select_menus'] : [];
496 -
497 - $last_shortcode = '';
498 -
499 - if ($this->darkify_is_dark_mode_allowed() && isset($args->menu->term_id)) {
500 - foreach ($select_menus as $select_menu) {
501 -
502 - $switch_in_menu_location = $select_menu['switch_in_menu_location'] ?? '';
503 - $darkify_menu_shortcode_helper = $select_menu['darkify_menu_shortcode_helper'] ?? '';
504 - $menu_position = isset($select_menu['select_menu_position']) ? $select_menu['select_menu_position'] : 'after';
206 + if ($this->darkify_is_dark_mode_allowed()) {
207 + if (isset($args->menu->term_id)) {
505 208 if ($args->menu->term_id == $switch_in_menu_location) {
506 - $last_shortcode = $darkify_menu_shortcode_helper;
209 + $items .= '<li class="menu-item">' . do_shortcode($darkify_menu_shortcode_helper) . '</li>';
507 210 }
508 211 }
509 - if ($last_shortcode) {
510 - $items = ($menu_position == 'before') ? '<li class="menu-item">' . do_shortcode($last_shortcode) . '</li>' . $items : $items . '<li class="menu-item">' . do_shortcode($last_shortcode) . '</li>';
511 - }
512 212 }
513 -
514 213 return $items;
515 214 }
516 215 }