PluginProbe
Accessibility by AllAccessible / trunk
Accessibility by AllAccessible vtrunk
2.1.6 2.1.5 2.1.4 2.1.3 2.1.2 2.1.1 2.1.0 2.0.6 trunk 1.0 1.1.0 1.1.1 1.1.2 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.2 1.2.1 1.2.10 1.2.2 1.2.3 1.2.4 1.2.5 All 43 releases
allaccessible / inc / WidgetCustomizer.php

WidgetCustomizer.php in Accessibility by AllAccessible trunk, at inc/WidgetCustomizer.php

706 lines 49.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Widget Customizer Component
4 *
5 * Simple Tailwind form for basic widget customization
6 * Directs to dashboard for advanced settings
7 *
8 * @package AllAccessible
9 * @since 2.0.0
10 */
11
12 if (!defined('ABSPATH')) {
13 die('You are not allowed to call this page directly.');
14 }
15
16 /**
17 * Render widget customizer form for paid users
18 *
19 * @param object $site_options Site options from /validate API
20 * @param string $widget_settings_url URL to advanced widget settings
21 */
22 function aacb_render_widget_customizer($site_options, $widget_settings_url) {
23 if (!$site_options || is_wp_error($site_options)) {
24 return;
25 }
26
27 // Get correct subdomain ID from validate response
28 $subdomain_id = $site_options->subID ?? get_option('aacb_siteID');
29
30 // Extract current values
31 $color = $site_options->triggerBtnBg ?? '#2b446d';
32
33 // Convert RGB color to hex if needed (database might have rgb(r,g,b) format)
34 $color = trim($color);
35 if (strpos($color, 'rgb') !== false) {
36 preg_match('/rgb\((\d+),\s*(\d+),\s*(\d+)\)/', $color, $matches);
37 if (count($matches) === 4) {
38 $color = sprintf("#%02x%02x%02x", $matches[1], $matches[2], $matches[3]);
39 } else {
40 $color = '#2b446d'; // Fallback
41 }
42 }
43
44 $position = $site_options->buttonPosition ?? 'bottom-right';
45 $size = $site_options->triggerBtnSize ?? '55';
46 $icon = $site_options->triggerSVG ?? 'Default';
47 $shape = $site_options->triggerBtnRadius ?? '50';
48 $white_label = $site_options->isWhitelabel ?? false;
49 // Headless mode: hide visible widget bubble while AllAccessible agents continue
50 // running remediation in the background. Prefer the backend-stored flag
51 // (from site_options) so multi-device admins see consistent state; fall back
52 // to local aacb_options for sites that haven't synced yet.
53 $aacb_local_opts = get_option('aacb_options', array());
54 $headless_mode = isset($site_options->headlessMode)
55 ? (bool) $site_options->headlessMode
56 : !empty($aacb_local_opts['headless_mode']);
57 ?>
58
59 <div class="aacx-v2" style="margin-bottom: var(--aacx-space-8);">
60 <style>
61 /* aacx-hidden compatibility — preserved from legacy Tailwind so existing
62 JS toggleClass('aacx-hidden') still hides status panels. */
63 .aacx-v2 .aacx-hidden { display: none !important; }
64 /* Customizer-specific layout helpers (scoped to .aacx-v2 only). */
65 .wc-color-row { display: flex; align-items: center; gap: var(--aacx-space-3); }
66 .wc-color-input { height: 44px; width: 64px; padding: 2px; cursor: pointer;
67 border: 1px solid var(--aacx-border-strong); border-radius: var(--aacx-radius-sm);
68 background: var(--aacx-surface); }
69 .wc-icon-row { display: flex; align-items: center; gap: var(--aacx-space-3); }
70 .wc-icon-preview { width: 44px; height: 44px; padding: 6px; border-radius: var(--aacx-radius);
71 background: var(--aacx-primary-600); display: flex; align-items: center; justify-content: center;
72 flex-shrink: 0; color: #fff; }
73 .wc-toolbar { display: flex; align-items: center; gap: var(--aacx-space-3); flex-wrap: wrap;
74 padding-top: var(--aacx-space-6); margin-top: var(--aacx-space-6);
75 border-top: 1px solid var(--aacx-border); }
76 .wc-status { display: inline-flex; align-items: center; gap: var(--aacx-space-2);
77 font-size: var(--aacx-text-sm); }
78 .wc-status svg { width: 18px; height: 18px; }
79 .wc-preview-frame { position: relative; background: var(--aacx-surface);
80 border: 1px solid var(--aacx-border-strong); border-radius: var(--aacx-radius-md);
81 height: 380px; overflow: hidden; }
82 .wc-headless-toggle { display: flex; align-items: center; gap: var(--aacx-space-3);
83 padding: var(--aacx-space-3) var(--aacx-space-4); cursor: pointer;
84 border: 1px solid var(--aacx-border-strong); border-radius: var(--aacx-radius);
85 background: var(--aacx-surface); transition: border-color var(--aacx-transition); }
86 .wc-headless-toggle:hover { border-color: var(--aacx-ai-700); }
87 .wc-headless-toggle input { width: 18px; height: 18px; cursor: pointer; flex-shrink: 0; }
88 /* Spin animation for legacy SVG spinners */
89 @keyframes wc-spin { to { transform: rotate(360deg); } }
90 .aacx-v2 .aacx-animate-spin { animation: wc-spin 0.8s linear infinite; }
91 </style>
92
93 <div class="aacx-v2__card aacx-v2__card--elevated">
94
95 <!-- Header -->
96 <div class="aacx-v2__card-header">
97 <div style="display: flex; align-items: center; gap: var(--aacx-space-4);">
98 <div style="width: 40px; height: 40px; background: var(--aacx-primary-100); border-radius: var(--aacx-radius); display: flex; align-items: center; justify-content: center; flex-shrink: 0;">
99 <svg style="width: 22px; height: 22px; color: var(--aacx-primary-600);" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
100 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6V4m0 2a2 2 0 100 4m0-4a2 2 0 110 4m-6 8a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4m6 6v10m6-2a2 2 0 100-4m0 4a2 2 0 110-4m0 4v2m0-6V4"/>
101 </svg>
102 </div>
103 <div>
104 <h3 style="margin: 0;"><?php _e('Quick Widget Settings', 'allaccessible'); ?></h3>
105 <p style="font-size: var(--aacx-text-xs); color: var(--aacx-text-muted); margin-top: 2px;"><?php _e('Customize your widget appearance', 'allaccessible'); ?></p>
106 </div>
107 </div>
108 <a href="<?php echo esc_url($widget_settings_url); ?>" target="_blank" class="aacx-v2__btn aacx-v2__btn--primary aacx-v2__btn--sm">
109 <?php _e('Advanced Settings', 'allaccessible'); ?>
110 <svg style="width: 14px; height: 14px;" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
111 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 6H6a2 2 0 00-2 2v10a2 2 0 002 2h10a2 2 0 002-2v-4M14 4h6m0 0v6m0-6L10 14"/>
112 </svg>
113 </a>
114 </div>
115
116 <!-- Body: info banner + 2-column form/preview grid -->
117 <div class="aacx-v2__card-body">
118
119 <!-- Info banner -->
120 <div class="aacx-v2__banner aacx-v2__banner--info" style="margin-bottom: var(--aacx-space-6);">
121 <div>
122 <strong><?php _e('Pro Tip:', 'allaccessible'); ?></strong>
123 <?php _e('For more options including accessibility profiles, custom CSS, and advanced features, visit', 'allaccessible'); ?>
124 <a href="<?php echo esc_url($widget_settings_url); ?>" target="_blank"><?php _e('Advanced Widget Settings', 'allaccessible'); ?></a>.
125 </div>
126 </div>
127
128 <!-- 2-column: form (2/3) + preview (1/3) -->
129 <div style="display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: var(--aacx-space-8);">
130
131 <!-- LEFT: settings form -->
132 <div>
133 <form id="aacb-widget-settings-form">
134 <input type="hidden" name="accountID" value="<?php echo esc_attr(get_option('aacb_accountID')); ?>">
135 <input type="hidden" name="subdomainID" value="<?php echo esc_attr($subdomain_id); ?>">
136
137 <div class="aacx-v2__stack" style="gap: var(--aacx-space-6);">
138
139 <!-- Row 1: Color + Position -->
140 <div class="aacx-v2__grid aacx-v2__grid--2">
141 <div class="aacx-v2__field">
142 <label class="aacx-v2__label" for="widget-color"><?php _e('Widget Color', 'allaccessible'); ?></label>
143 <div class="wc-color-row">
144 <input type="color" name="triggerBtnBg" id="widget-color" value="<?php echo esc_attr(trim($color)); ?>" class="wc-color-input">
145 <input type="hidden" id="widget-color-text" value="<?php echo esc_attr(trim($color)); ?>" readonly>
146 </div>
147 <p class="aacx-v2__help"><?php _e('Choose the color for your accessibility widget button.', 'allaccessible'); ?></p>
148 </div>
149 <div class="aacx-v2__field">
150 <label class="aacx-v2__label" for="wc-position"><?php _e('Widget Position', 'allaccessible'); ?></label>
151 <select name="buttonPosition" id="wc-position" class="aacx-v2__select">
152 <option value="bottom-right" <?php selected($position, 'bottom-right'); ?>><?php _e('Bottom Right', 'allaccessible'); ?></option>
153 <option value="bottom-left" <?php selected($position, 'bottom-left'); ?>><?php _e('Bottom Left', 'allaccessible'); ?></option>
154 <option value="bottom-center" <?php selected($position, 'bottom-center'); ?>><?php _e('Bottom Center', 'allaccessible'); ?></option>
155 <option value="right-center" <?php selected($position, 'right-center'); ?>><?php _e('Middle Right', 'allaccessible'); ?></option>
156 <option value="left-center" <?php selected($position, 'left-center'); ?>><?php _e('Middle Left', 'allaccessible'); ?></option>
157 <option value="top-right" <?php selected($position, 'top-right'); ?>><?php _e('Top Right', 'allaccessible'); ?></option>
158 <option value="top-left" <?php selected($position, 'top-left'); ?>><?php _e('Top Left', 'allaccessible'); ?></option>
159 </select>
160 <p class="aacx-v2__help"><?php _e('Where the widget button appears on your site.', 'allaccessible'); ?></p>
161 </div>
162 </div>
163
164 <!-- Row 2: Size + Icon -->
165 <div class="aacx-v2__grid aacx-v2__grid--2">
166 <div class="aacx-v2__field">
167 <label class="aacx-v2__label" for="wc-size"><?php _e('Widget Size', 'allaccessible'); ?></label>
168 <select name="triggerBtnSize" id="wc-size" class="aacx-v2__select">
169 <option value="40" <?php selected($size, '40'); ?>><?php _e('Small', 'allaccessible'); ?></option>
170 <option value="55" <?php selected($size, '55'); ?>><?php _e('Medium (Default)', 'allaccessible'); ?></option>
171 <option value="80" <?php selected($size, '80'); ?>><?php _e('Large', 'allaccessible'); ?></option>
172 </select>
173 <p class="aacx-v2__help"><?php _e('Size of the widget button in pixels.', 'allaccessible'); ?></p>
174 </div>
175 <div class="aacx-v2__field">
176 <label class="aacx-v2__label" for="icon-selector"><?php _e('Widget Icon', 'allaccessible'); ?></label>
177 <div class="wc-icon-row">
178 <select name="triggerSVG" id="icon-selector" class="aacx-v2__select" style="flex: 1;">
179 <option value="Default" <?php selected($icon, 'Default'); ?>><?php _e('Default', 'allaccessible'); ?></option>
180 <option value="Alt" <?php selected($icon, 'Alt'); ?>><?php _e('Accessibility Person', 'allaccessible'); ?></option>
181 <option value="Alt2" <?php selected($icon, 'Alt2'); ?>><?php _e('Accessibility Settings', 'allaccessible'); ?></option>
182 <option value="Adjust" <?php selected($icon, 'Adjust'); ?>><?php _e('Adjust', 'allaccessible'); ?></option>
183 <option value="Chair2" <?php selected($icon, 'Chair2'); ?>><?php _e('Wheelchair 1', 'allaccessible'); ?></option>
184 <option value="Heart" <?php selected($icon, 'Heart'); ?>><?php _e('Heart', 'allaccessible'); ?></option>
185 <option value="Braille" <?php selected($icon, 'Braille'); ?>><?php _e('Braille', 'allaccessible'); ?></option>
186 <option value="Blind" <?php selected($icon, 'Blind'); ?>><?php _e('Blind/Cane', 'allaccessible'); ?></option>
187 <option value="Eye" <?php selected($icon, 'Eye'); ?>><?php _e('Eye', 'allaccessible'); ?></option>
188 <option value="Globe" <?php selected($icon, 'Globe'); ?>><?php _e('Globe', 'allaccessible'); ?></option>
189 <option value="Access" <?php selected($icon, 'Access'); ?>><?php _e('Universal Access', 'allaccessible'); ?></option>
190 <option value="Cogs" <?php selected($icon, 'Cogs'); ?>><?php _e('Settings', 'allaccessible'); ?></option>
191 <option value="Cane" <?php selected($icon, 'Cane'); ?>><?php _e('Walking Cane', 'allaccessible'); ?></option>
192 </select>
193 <div id="icon-preview" class="wc-icon-preview" aria-hidden="true"></div>
194 </div>
195 <p class="aacx-v2__help"><?php _e('Icon displayed in the widget button.', 'allaccessible'); ?></p>
196 </div>
197 </div>
198
199 <!-- Row 3: Shape + Headless mode -->
200 <div class="aacx-v2__grid aacx-v2__grid--2">
201 <div class="aacx-v2__field">
202 <label class="aacx-v2__label" for="wc-shape"><?php _e('Widget Shape', 'allaccessible'); ?></label>
203 <select name="triggerBtnRadius" id="wc-shape" class="aacx-v2__select">
204 <option value="50" <?php selected($shape, '50'); ?>><?php _e('Circle', 'allaccessible'); ?></option>
205 <option value="0" <?php selected($shape, '0'); ?>><?php _e('Square', 'allaccessible'); ?></option>
206 </select>
207 <p class="aacx-v2__help"><?php _e('Shape of the widget button.', 'allaccessible'); ?></p>
208 </div>
209 <div class="aacx-v2__field">
210 <label class="aacx-v2__label" for="wc-headless" style="display: inline-flex; align-items: center; gap: var(--aacx-space-2);">
211 <span class="aacx-v2__ai-badge"><?php _e('AllAccessible AI', 'allaccessible'); ?></span>
212 <?php _e('Run without the bubble', 'allaccessible'); ?>
213 </label>
214 <label class="wc-headless-toggle" for="wc-headless">
215 <input type="checkbox" name="headlessMode" id="wc-headless" value="1" <?php checked($headless_mode); ?>>
216 <span style="font-size: var(--aacx-text-sm); color: var(--aacx-text-strong);"><?php _e('Hide widget, keep agentic AI remediation running.', 'allaccessible'); ?></span>
217 </label>
218 <p class="aacx-v2__help"><?php _e('Visitors won\'t see the accessibility toolbar. AllAccessible agents continue scanning and applying approved fixes in the background.', 'allaccessible'); ?></p>
219 </div>
220 </div>
221
222 </div>
223
224 <!-- Save toolbar -->
225 <div class="wc-toolbar">
226 <button type="submit" id="save-widget-settings" class="aacx-v2__btn aacx-v2__btn--primary">
227 <svg style="width: 16px; height: 16px;" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
228 <path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
229 </svg>
230 <?php _e('Save Settings', 'allaccessible'); ?>
231 </button>
232
233 <a href="#" id="show-troubleshooting-link" class="aacx-v2__btn aacx-v2__btn--ghost aacx-v2__btn--sm">
234 <?php _e('Having trouble viewing updates?', 'allaccessible'); ?>
235 </a>
236
237 <div id="troubleshooting-buttons" class="aacx-hidden">
238 <button type="button" id="invalidate-all-cache-button" class="aacx-v2__btn aacx-v2__btn--secondary aacx-v2__btn--sm" title="<?php esc_attr_e('Clear all caches: browser, cookies, and server (full reset)', 'allaccessible'); ?>">
239 <?php _e('Clear All Caches', 'allaccessible'); ?>
240 </button>
241 </div>
242
243 <!-- Status indicators (JS toggles aacx-hidden) -->
244 <div id="save-status" class="aacx-hidden wc-status" style="color: var(--aacx-text-muted);">
245 <svg class="aacx-animate-spin" fill="none" viewBox="0 0 24 24" aria-hidden="true">
246 <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" opacity="0.25"></circle>
247 <path fill="currentColor" opacity="0.75" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
248 </svg>
249 <?php _e('Saving...', 'allaccessible'); ?>
250 </div>
251
252 <div id="save-success" class="aacx-hidden wc-status" style="color: var(--aacx-ok-700);">
253 <span aria-hidden="true"></span> <?php _e('Settings saved!', 'allaccessible'); ?>
254 </div>
255
256 <div id="save-error" class="aacx-hidden wc-status" style="color: var(--aacx-danger-700);"></div>
257
258 <div id="reset-cache-success" class="aacx-hidden wc-status" style="color: var(--aacx-ok-700);">
259 <span aria-hidden="true"></span> <?php _e('Cache cleared! Refresh your site to see changes.', 'allaccessible'); ?>
260 </div>
261
262 <div id="invalidate-cache-status" class="aacx-hidden wc-status" style="color: var(--aacx-warn-700);">
263 <svg class="aacx-animate-spin" fill="none" viewBox="0 0 24 24" aria-hidden="true">
264 <circle cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" opacity="0.25"></circle>
265 <path fill="currentColor" opacity="0.75" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"></path>
266 </svg>
267 <?php _e('Clearing all caches...', 'allaccessible'); ?>
268 </div>
269
270 <div id="invalidate-cache-success" class="aacx-hidden wc-status" style="color: var(--aacx-ok-700);">
271 <span aria-hidden="true"></span> <?php _e('All caches cleared! Refresh your site to see changes.', 'allaccessible'); ?>
272 </div>
273
274 <div id="invalidate-cache-error" class="aacx-hidden wc-status" style="color: var(--aacx-danger-700);"></div>
275 </div>
276 </form>
277 </div>
278
279 <!-- RIGHT: Live preview -->
280 <div>
281 <div style="position: sticky; top: 20px;">
282 <div class="aacx-v2__card aacx-v2__card--featured">
283 <div class="aacx-v2__card-header">
284 <h3 style="margin: 0;"><?php _e('Live Preview', 'allaccessible'); ?></h3>
285 </div>
286 <div class="aacx-v2__card-body">
287 <div class="wc-preview-frame">
288 <div id="widget-preview-position" style="position: absolute; bottom: 20px; right: 20px; transition: all 0.3s;">
289 <div id="widget-preview-button"
290 data-color="<?php echo esc_attr(trim($color)); ?>"
291 data-size="<?php echo esc_attr($size); ?>"
292 data-shape="<?php echo esc_attr($shape); ?>">
293 <!-- Icon SVG injected by JavaScript -->
294 </div>
295 </div>
296 </div>
297 <p style="font-size: var(--aacx-text-xs); text-align: center; color: var(--aacx-text-muted); margin-top: var(--aacx-space-3);"><?php _e('See how your widget will look on your site.', 'allaccessible'); ?></p>
298 </div>
299 </div>
300 </div>
301 </div>
302
303 </div>
304 </div>
305 </div>
306 </div>
307
308 <script>
309 /**
310 * Clear browser-side widget caches so option changes show up on the
311 * next page render. Modern v2 widget namespaces all storage under
312 * `aacx_` / `aacx-` prefixes (handled by the widget's storage layer).
313 * The legacy widget used `overrideOptions`.
314 *
315 * Writing `aacx_cache_invalidation` is the cross-tab signal the widget
316 * watches — any other open tab with the widget loaded will drop its
317 * own `aacx_validation` + `aacx_config` keys when the storage event
318 * fires.
319 *
320 * Idempotent. Silent on quota / private-browsing failures.
321 */
322 function aacxClearWidgetCaches() {
323 try {
324 if (typeof localStorage === 'undefined') return;
325
326 // Legacy: pre-2026 widget cache key
327 localStorage.removeItem('overrideOptions');
328
329 // v2 widget: every aacx_* / aacx-* key (user preferences,
330 // license cache, config cache, settings, etc.)
331 var toRemove = [];
332 for (var i = 0; i < localStorage.length; i++) {
333 var key = localStorage.key(i);
334 if (key && (key.indexOf('aacx_') === 0 || key.indexOf('aacx-') === 0)) {
335 toRemove.push(key);
336 }
337 }
338 toRemove.forEach(function (key) {
339 localStorage.removeItem(key);
340 });
341
342 // sessionStorage too — widget caches some short-lived data here
343 try {
344 var sessRemove = [];
345 for (var j = 0; j < sessionStorage.length; j++) {
346 var sk = sessionStorage.key(j);
347 if (sk && (sk.indexOf('aacx_') === 0 || sk.indexOf('aacx-') === 0)) {
348 sessRemove.push(sk);
349 }
350 }
351 sessRemove.forEach(function (sk) {
352 sessionStorage.removeItem(sk);
353 });
354 } catch (e) { /* ignore */ }
355
356 // Cross-tab signal — other open tabs running the widget will
357 // see this and clear their own validation/config caches.
358 localStorage.setItem('aacx_cache_invalidation', JSON.stringify({
359 ts: Date.now(),
360 source: 'wp-plugin-settings-save',
361 keys: ['aacx_validation', 'aacx_config']
362 }));
363
364 // Legacy cookie
365 document.cookie = 'aacxValidated=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
366
367 } catch (e) {
368 console.warn('⚠️ Could not clear browser cache:', e);
369 }
370 }
371
372 jQuery(document).ready(function($) {
373 // Icon SVGs from v1.3.8 (exact same as old plugin)
374 const svgIcons = {
375 Default: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 79.583 79.638" style="width:85%;height:85%;" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1, 0, 0, 1, -10.250157, -10.143444)"><path fill="white" d="M55.6,73.2c0.5,1.1,1.5,1.7,2.6,1.7c0.4,0,0.8-0.1,1.2-0.3c1.5-0.7,2.1-2.4,1.4-3.9c0,0-5.5-12.6-6.5-17.2c-0.4-1.6-0.6-5.9-0.7-7.9c0-0.7,0.4-1.3,1-1.5l12.3-3.7c1.5-0.4,2.4-2.1,2-3.6c-0.4-1.5-2.1-2.4-3.6-2c0,0-11.4,3.7-15.5,3.7c-4,0-15.3-3.6-15.3-3.6c-1.5-0.4-3.2,0.3-3.7,1.8c-0.5,1.6,0.4,3.3,2,3.7l12.3,3.7c0.6,0.2,1.1,0.8,1,1.5c-0.1,2-0.3,6.3-0.7,7.9c-1,4.6-6.5,17.2-6.5,17.2c-0.7,1.5,0,3.2,1.4,3.9c0.4,0.2,0.8,0.3,1.2,0.3c1.1,0,2.2-0.6,2.6-1.7L50,61.2L55.6,73.2z"/><circle fill="white" cx="50" cy="30" r="5.6"/><path fill="white" d="M89.5,50c0-21.8-17.7-39.5-39.5-39.5c-21.8,0-39.5,17.7-39.5,39.5S28.2,89.5,50,89.5C71.8,89.5,89.5,71.8,89.5,50zM17.1,50c0-18.2,14.8-32.9,32.9-32.9S82.9,31.8,82.9,50S68.2,82.9,50,82.9S17.1,68.2,17.1,50z"/></g></svg>',
376 Alt: '<svg version="1.1" x="0px" y="0px" viewBox="0 0 182.608 214.637" style="width:70%;height:70%;" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1, 0, 0, 1, -15.892562, 0.061192)"><path fill="white" d="M127.75,183.153c-10.629,10.63-24.76,16.483-39.793,16.483c-31.024,0-56.265-25.241-56.265-56.268c0-15.034,5.852-29.168,16.479-39.796c2.929-2.929,2.928-7.678-0.001-10.607c-2.929-2.93-7.678-2.929-10.607,0.001c-13.459,13.461-20.871,31.36-20.871,50.401c0,39.297,31.969,71.268,71.265,71.268c19.039,0,36.938-7.414,50.4-20.878c2.929-2.929,2.928-7.678-0.001-10.606C135.427,180.225,130.678,180.224,127.75,183.153z"/><path fill="white" d="M190.444,166.706h-21.335L155.9,130.057c-1.072-2.975-3.894-4.957-7.056-4.957H93.232V91.424h51.648c4.142,0,7.5-3.357,7.5-7.5c0-4.143-3.358-7.5-7.5-7.5H93.232V53.279c0-4.143-3.358-7.5-7.5-7.5c-4.142,0-7.5,3.357-7.5,7.5v79.32c0,4.143,3.358,7.5,7.5,7.5h57.842l13.209,36.649c1.072,2.975,3.894,4.957,7.056,4.957h26.604c4.142,0,7.5-3.357,7.5-7.5S194.587,166.706,190.444,166.706z"/><path fill="white" d="M86.015,36.438c10.063,0,18.221-8.154,18.221-18.224C104.235,8.161,96.078,0,86.015,0C75.952,0,67.796,8.161,67.796,18.215C67.796,28.284,75.952,36.438,86.015,36.438z"/></g></svg>',
377 Alt2: '<svg x="0px" y="0px" viewBox="0.634 0.344 56.406 60.613" style="width:70%;height:70%;" xmlns="http://www.w3.org/2000/svg"><g transform="matrix(1, 0, 0, 1, -21.093462, -19.272371)"><path fill="white" d="M50.1,41.3c-4.9,0-8.8,3.9-8.8,8.8s3.9,8.8,8.8,8.8s8.8-3.9,8.8-8.8S55,41.3,50.1,41.3zM76.4,58l-4.6-3.9c0.3-1.4,0.4-2.9,0.4-4.3c0-1.4-0.1-2.9-0.4-4.3l4.6-3.9c1.5-1.3,2-3.5,1-5.3l-2-3.5c-0.8-1.3-2.1-2-3.6-2c-0.5,0-1,0.1-1.4,0.3l-5.8,2.1c-2.3-2-4.8-3.4-7.4-4.3l-1-5.9c-0.4-2-2.1-3.1-4.1-3.1h-4c-2,0-3.8,1.1-4.1,3.1l-1,5.8c-2.8,0.9-5.3,2.4-7.5,4.3L29.6,31c-0.5-0.1-0.9-0.3-1.4-0.3c-1.5,0-2.9,0.8-3.6,2l-2,3.5c-1,1.8-0.6,4,1,5.3l4.6,3.9c-0.3,1.4-0.4,2.9-0.4,4.3c0,1.5,0.1,2.9,0.4,4.3l-4.6,3.9c-1.5,1.3-2,3.5-1,5.3l2,3.5c0.8,1.3,2.1,2,3.6,2c0.5,0,1-0.1,1.4-0.3l5.8-2.1c2.3,2,4.8,3.4,7.4,4.3l1,6c0.4,2,2,3.4,4.1,3.4h4c2,0,3.8-1.5,4.1-3.5l1-6c2.9-1,5.5-2.5,7.8-4.6l5.4,2.1c0.5,0.1,1,0.3,1.5,0.3c1.5,0,2.9-0.8,3.6-2l1.9-3.3C78.4,61.5,77.9,59.3,76.4,58zM50.1,63.9c-7.7,0-13.8-6.2-13.8-13.8s6.2-13.8,13.8-13.8s13.8,6.2,13.8,13.8S57.7,63.9,50.1,63.9z"/></g></svg>',
378 Adjust: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="width:70%;height:70%;"><path fill="white" d="M8 256c0 136.966 111.033 248 248 248s248-111.034 248-248S392.966 8 256 8 8 119.033 8 256zm248 184V72c101.705 0 184 82.311 184 184 0 101.705-82.311 184-184 184z"></path></svg>',
379 Braille: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512" style="width:70%;height:70%;"><path fill="white" d="M128 256c0 35.346-28.654 64-64 64S0 291.346 0 256s28.654-64 64-64 64 28.654 64 64zM64 384c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0-352C28.654 32 0 60.654 0 96s28.654 64 64 64 64-28.654 64-64-28.654-64-64-64zm160 192c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0 160c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0-352c-35.346 0-64 28.654-64 64s28.654 64 64 64 64-28.654 64-64-28.654-64-64-64zm224 192c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0 160c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0-352c-35.346 0-64 28.654-64 64s28.654 64 64 64 64-28.654 64-64-28.654-64-64-64zm160 192c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0 160c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32zm0-320c-17.673 0-32 14.327-32 32s14.327 32 32 32 32-14.327 32-32-14.327-32-32-32z"></path></svg>',
380 Blind: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512" style="width:70%;height:70%;"><path fill="white" d="M380.15 510.837a8 8 0 0 1-10.989-2.687l-125.33-206.427a31.923 31.923 0 0 0 12.958-9.485l126.048 207.608a8 8 0 0 1-2.687 10.991zM142.803 314.338l-32.54 89.485 36.12 88.285c6.693 16.36 25.377 24.192 41.733 17.501 16.357-6.692 24.193-25.376 17.501-41.734l-62.814-153.537zM96 88c24.301 0 44-19.699 44-44S120.301 0 96 0 52 19.699 52 44s19.699 44 44 44zm154.837 169.128l-120-152c-4.733-5.995-11.75-9.108-18.837-9.112V96H80v.026c-7.146.003-14.217 3.161-18.944 9.24L0 183.766v95.694c0 13.455 11.011 24.791 24.464 24.536C37.505 303.748 48 293.1 48 280v-79.766l16-20.571v140.698L9.927 469.055c-6.04 16.609 2.528 34.969 19.138 41.009 16.602 6.039 34.968-2.524 41.009-19.138L136 309.638V202.441l-31.406-39.816a4 4 0 1 1 6.269-4.971l102.3 129.217c9.145 11.584 24.368 11.339 33.708 3.965 10.41-8.216 12.159-23.334 3.966-33.708z"/></svg>',
381 Eye: '<svg style="width:70%;height:70%;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="white" d="M38.8 5.1C28.4-3.1 13.3-1.2 5.1 9.2S-1.2 34.7 9.2 42.9l592 464c10.4 8.2 25.5 6.3 33.7-4.1s6.3-25.5-4.1-33.7L525.6 386.7c39.6-40.6 66.4-86.1 79.9-118.4c3.3-7.9 3.3-16.7 0-24.6c-14.9-35.7-46.2-87.7-93-131.1C465.5 68.8 400.8 32 320 32c-68.2 0-125 26.3-169.3 60.8L38.8 5.1zM223.1 149.5C248.6 126.2 282.7 112 320 112c79.5 0 144 64.5 144 144c0 24.9-6.3 48.3-17.4 68.7L408 294.5c5.2-11.8 8-24.8 8-38.5c0-53-43-96-96-96c-2.8 0-5.6 .1-8.4 .4c5.3 9.3 8.4 20.1 8.4 31.6c0 10.2-2.4 19.8-6.6 28.3l-90.3-70.8zM160 448c53 0 96-43 96-96s-43-96-96-96s-96 43-96 96s43 96 96 96z"/></svg>',
382 Cogs: '<svg style="width:70%;height:70%;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="white" d="M224 96c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zM152.5 247.2c12.4-4.7 18.7-18.5 14-30.9s-18.5-18.7-30.9-14C75.1 225.1 32 283.5 32 352c0 88.4 71.6 160 160 160c61.2 0 114.3-34.3 141.2-84.7c6.2-11.7 1.8-26.2-9.9-32.5s-26.2-1.8-32.5 9.9C272 440 234.8 464 192 464c-61.9 0-112-50.1-112-112c0-47.9 30.1-88.8 72.5-104.8zM291.8 176l-1.9-9.7c-4.5-22.3-24-38.3-46.8-38.3c-30.1 0-52.7 27.5-46.8 57l23.1 115.5c6 29.9 32.2 51.4 62.8 51.4h5.1c.4 0 .8 0 1.3 0h94.1c6.7 0 12.6 4.1 15 10.4L434 459.2c6 16.1 23.8 24.6 40.1 19.1l48-16c16.8-5.6 25.8-23.7 20.2-40.5s-23.7-25.8-40.5-20.2l-18.7 6.2-25.5-68c-11.7-31.2-41.6-51.9-74.9-51.9H314.2l-9.6-48H368c17.7 0 32-14.3 32-32s-14.3-32-32-32H291.8z"/></svg>',
383 Globe: '<svg style="width:70%;height:70%;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="white" d="M352 256c0 22.2-1.2 43.6-3.3 64H163.3c-2.2-20.4-3.3-41.8-3.3-64s1.2-43.6 3.3-64H348.7c2.2 20.4 3.3 41.8 3.3 64zm28.8-64H503.9c5.3 20.5 8.1 41.9 8.1 64s-2.8 43.5-8.1 64H380.8c2.1-20.6 3.2-42 3.2-64s-1.1-43.4-3.2-64zm112.6-32H376.7c-10-63.9-29.8-117.4-55.3-151.6c78.3 20.7 142 77.5 171.9 151.6zm-149.1 0H167.7c6.1-36.4 15.5-68.6 27-94.7c10.5-23.6 22.2-40.7 33.5-51.5C239.4 3.2 248.7 0 256 0s16.6 3.2 27.8 13.8c11.3 10.8 23 27.9 33.5 51.5c11.6 26 21 58.2 27 94.7zm-209 0H18.6C48.6 85.9 112.2 29.1 190.6 8.4C165.1 42.6 145.3 96.1 135.3 160zM8.1 192H131.2c-2.1 20.6-3.2 42-3.2 64s1.1 43.4 3.2 64H8.1C2.8 299.5 0 278.1 0 256s2.8-43.5 8.1-64zM194.7 446.6c-11.6-26-20.9-58.2-27-94.6H344.3c-6.1 36.4-15.5 68.6-27 94.6c-10.5 23.6-22.2 40.7-33.5 51.5C272.6 508.8 263.3 512 256 512s-16.6-3.2-27.8-13.8c-11.3-10.8-23-27.9-33.5-51.5zM135.3 352c10 63.9 29.8 117.4 55.3 151.6C112.2 482.9 48.6 426.1 18.6 352H135.3zm358.1 0c-30 74.1-93.6 130.9-171.9 151.6c25.5-34.2 45.2-87.7 55.3-151.6H493.4z"/></svg>',
384 Heart: '<svg x="0px" y="0px" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512" style="width:70%;height:70%;"><path fill="white" d="M244 84L255.1 96L267.1 84.02C300.6 51.37 347 36.51 392.6 44.1C461.5 55.58 512 115.2 512 185.1V190.9C512 232.4 494.8 272.1 464.4 300.4L283.7 469.1C276.2 476.1 266.3 480 256 480C245.7 480 235.8 476.1 228.3 469.1L47.59 300.4C17.23 272.1 0 232.4 0 190.9V185.1C0 115.2 50.52 55.58 119.4 44.1C164.1 36.51 211.4 51.37 244 84C243.1 84 244 84.01 244 84L244 84zM255.1 163.9L210.1 117.1C188.4 96.28 157.6 86.4 127.3 91.44C81.55 99.07 48 138.7 48 185.1V190.9C48 219.1 59.71 246.1 80.34 265.3L256 429.3L431.7 265.3C452.3 246.1 464 219.1 464 190.9V185.1C464 138.7 430.4 99.07 384.7 91.44C354.4 86.4 323.6 96.28 301.9 117.1L255.1 163.9z"/></svg>',
385 Chair2: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" style="width:70%;height:70%;"><path fill="white" d="M416 48c0 26.5-21.5 48-48 48s-48-21.5-48-48s21.5-48 48-48s48 21.5 48 48zM204.5 121.3c-5.4-2.5-11.7-1.9-16.4 1.7l-40.9 30.7c-14.1 10.6-34.2 7.7-44.8-6.4s-7.7-34.2 6.4-44.8l40.9-30.7c23.7-17.8 55.3-21 82.1-8.4l90.4 42.5c29.1 13.7 36.8 51.6 15.2 75.5L299.1 224h97.4c30.3 0 53 27.7 47.1 57.4L415.4 422.3c-3.5 17.3-20.3 28.6-37.7 25.1s-28.6-20.3-25.1-37.7L377 288H306.7c8.6 19.6 13.3 41.2 13.3 64c0 88.4-71.6 160-160 160S0 440.4 0 352s71.6-160 160-160c11.1 0 22 1.1 32.4 3.3l54.2-54.2-42.1-19.8zM160 448c53 0 96-43 96-96s-43-96-96-96s-96 43-96 96s43 96 96 96z"/></svg>',
386 Chair3: '<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512" style="width:70%;height:70%;"><path fill="white" d="M224 96c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm-45.7 48h91.4c11.8 0 23.4 1.2 34.5 3.3c-2.1 18.5 7.4 35.6 21.8 44.8c-16.6 10.6-26.7 31.6-20 53.3c4 12.9 9.4 25.5 16.4 37.6s15.2 23.1 24.4 33c15.7 16.9 39.6 18.4 57.2 8.7v.9c0 9.2 2.7 18.5 7.9 26.3H29.7C13.3 512 0 498.7 0 482.3C0 383.8 79.8 304 178.3 304z"/></svg>',
387 Access: '<svg style="width:70%;height:70%;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="white" d="M512 256c0 141.4-114.6 256-256 256S0 397.4 0 256S114.6 0 256 0S512 114.6 512 256zM161.5 169.9c-12.2-5.2-26.3 .4-31.5 12.6s.4 26.3 12.6 31.5l11.9 5.1c17.3 7.4 35.2 12.9 53.6 16.3v50.1c0 4.3-.7 8.6-2.1 12.6l-28.7 86.1c-4.2 12.6 2.6 26.2 15.2 30.4s26.2-2.6 30.4-15.2l24.4-73.2c1.3-3.8 4.8-6.4 8.8-6.4s7.6 2.6 8.8 6.4l24.4 73.2c4.2 12.6 17.8 19.4 30.4 15.2s19.4-17.8 15.2-30.4l-28.7-86.1c-1.4-4.1-2.1-8.3-2.1-12.6V235.5c18.4-3.5 36.3-8.9 53.6-16.3l11.9-5.1c12.2-5.2 17.8-19.3 12.6-31.5s-19.3-17.8-31.5-12.6L338.7 175c-26.1 11.2-54.2 17-82.7 17s-56.5-5.8-82.7-17l-11.9-5.1zM256 160c22.1 0 40-17.9 40-40s-17.9-40-40-40s-40 17.9-40 40s17.9 40 40 40z"/></svg>',
388 Cane: '<svg style="width:70%;height:70%;" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path fill="white" d="M176 96c26.5 0 48-21.5 48-48s-21.5-48-48-48s-48 21.5-48 48s21.5 48 48 48zm-8.4 32c-36.4 0-69.6 20.5-85.9 53.1L35.4 273.7c-7.9 15.8-1.5 35 14.3 42.9s35 1.5 42.9-14.3L128 231.6v43.2c0 17 6.7 33.3 18.7 45.3L224 397.3V480c0 17.7 14.3 32 32 32s32-14.3 32-32V390.6c0-12.7-5.1-24.9-14.1-33.9L224 306.7V213.3l70.4 93.9c10.6 14.1 30.7 17 44.8 6.4s17-30.7 6.4-44.8L268.8 166.4C250.7 142.2 222.2 128 192 128H167.6zM128.3 346.8L97 472.2c-4.3 17.1 6.1 34.5 23.3 38.8s34.5-6.1 38.8-23.3l22-88.2-52.8-52.8zM450.8 505.1c5 7.3 15 9.1 22.3 4s9.1-15 4-22.3L358.9 316.1c-2.8 3.8-6.1 7.3-10.1 10.3c-5 3.8-10.5 6.4-16.2 7.9L450.8 505.1z"/></svg>'
389 };
390
391 // Update icon preview
392 function updateIconPreview() {
393 const selectedIcon = $('#icon-selector').val();
394 const iconSVG = svgIcons[selectedIcon] || svgIcons['Default'];
395 $('#icon-preview').html(iconSVG);
396 }
397
398 // Position mappings (MUST be defined FIRST before any function calls)
399 const positions = {
400 'bottom-right': { bottom: '20px', right: '20px', top: 'auto', left: 'auto' },
401 'bottom-left': { bottom: '20px', left: '20px', top: 'auto', right: 'auto' },
402 'bottom-center': { bottom: '20px', left: '50%', transform: 'translateX(-50%)', top: 'auto', right: 'auto' },
403 'top-right': { top: '20px', right: '20px', bottom: 'auto', left: 'auto' },
404 'top-left': { top: '20px', left: '20px', bottom: 'auto', right: 'auto' },
405 'right-center': { top: '50%', right: '20px', transform: 'translateY(-50%)', bottom: 'auto', left: 'auto' },
406 'left-center': { top: '50%', left: '20px', transform: 'translateY(-50%)', bottom: 'auto', right: 'auto' },
407 };
408
409 // Helper: Convert RGB to Hex if needed
410 function rgbToHex(color) {
411 // If already hex, return it
412 if (color.startsWith('#')) return color;
413
414 // Convert rgb(r,g,b) to hex
415 const match = color.match(/rgb\((\d+),\s*(\d+),\s*(\d+)\)/);
416 if (match) {
417 const r = parseInt(match[1]).toString(16).padStart(2, '0');
418 const g = parseInt(match[2]).toString(16).padStart(2, '0');
419 const b = parseInt(match[3]).toString(16).padStart(2, '0');
420 return '#' + r + g + b;
421 }
422 return color;
423 }
424
425 // Update preview when settings change
426 function updatePreview() {
427 let color = $('#widget-color').val() || '#2b446d';
428 const position = $('select[name="buttonPosition"]').val();
429 const size = $('select[name="triggerBtnSize"]').val();
430 const shape = $('select[name="triggerBtnRadius"]').val();
431 const selectedIcon = $('#icon-selector').val();
432
433 // Convert RGB to hex if needed
434 color = rgbToHex(color);
435
436
437 // Update icon SVG
438 const iconSVG = svgIcons[selectedIcon] || svgIcons['Default'];
439 const $button = $('#widget-preview-button');
440
441 $button.html(iconSVG);
442
443 // Set all styles as one attribute to override any inline styles
444 const previewSize = Math.max(parseInt(size), 60);
445 const borderRadius = shape === '50' ? '50%' : '12px';
446
447 $button.attr('style',
448 'background-color: ' + color + '; ' +
449 'width: ' + previewSize + 'px; ' +
450 'height: ' + previewSize + 'px; ' +
451 'border-radius: ' + borderRadius + '; ' +
452 'display: flex; ' +
453 'align-items: center; ' +
454 'justify-content: center; ' +
455 'box-shadow: 0 20px 25px -5px rgba(0, 0, 0, 0.1); ' +
456 'cursor: pointer; ' +
457 'transition: all 0.3s; ' +
458 'color: white; ' +
459 'font-weight: bold;'
460 );
461
462 // Update position
463 const pos = positions[position] || positions['bottom-right'];
464 $('#widget-preview-position').css({
465 top: pos.top || 'auto',
466 right: pos.right || 'auto',
467 bottom: pos.bottom || 'auto',
468 left: pos.left || 'auto',
469 transform: pos.transform || 'none'
470 });
471 }
472
473 // Update color text input when color picker changes
474 $('#widget-color').on('input', function() {
475 updatePreview();
476 });
477
478 // Update position when dropdown changes
479 $('select[name="buttonPosition"]').on('change', function() {
480 updatePreview();
481 });
482
483 // Update size when dropdown changes
484 $('select[name="triggerBtnSize"]').on('change', function() {
485 updatePreview();
486 });
487
488 // Update shape when dropdown changes
489 $('select[name="triggerBtnRadius"]').on('change', function() {
490 updatePreview();
491 });
492
493 // Icon selector change
494 $('#icon-selector').on('change', function() {
495 updateIconPreview(); // Update the small preview box
496 updatePreview(); // Update the live preview widget
497 });
498
499 // Initialize both previews with current settings
500 updateIconPreview();
501 updatePreview();
502
503 // Form submission
504 $('#aacb-widget-settings-form').on('submit', function(e) {
505 e.preventDefault();
506
507 const subdomainID = $('input[name="subdomainID"]').val();
508
509 if (!subdomainID) {
510 console.error('❌ Cannot save - no subdomain ID found');
511 $('#save-status').addClass('aacx-hidden');
512 $('#save-error').removeClass('aacx-hidden').text('Error: Missing subdomain ID. Please refresh and try again.');
513 $('#save-widget-settings').prop('disabled', false);
514 return;
515 }
516
517 const formData = {
518 accountID: $('input[name="accountID"]').val(),
519 subdomainID: subdomainID,
520 triggerBtnBg: $('input[name="triggerBtnBg"]').val(),
521 buttonPosition: $('select[name="buttonPosition"]').val(),
522 triggerBtnSize: $('select[name="triggerBtnSize"]').val(),
523 triggerSVG: $('select[name="triggerSVG"]').val(),
524 triggerBtnRadius: $('select[name="triggerBtnRadius"]').val(),
525 headlessMode: $('#wc-headless').is(':checked')
526 };
527
528
529 // Show loading
530 $('#save-widget-settings').prop('disabled', true);
531 $('#save-status').removeClass('aacx-hidden');
532 $('#save-success').addClass('aacx-hidden');
533 $('#save-error').addClass('aacx-hidden');
534
535 // Save endpoint stays on the app service (kept warm) instead of
536 // the edge handler so customers don't eat the ~3-5s cold-start
537 // when hitting Save. App service busts its own cache AND
538 // triggers the edge cache invalidation so widget visitors see
539 // fresh options immediately.
540 $.ajax({
541 url: 'https://app.allaccessible.org/api/save-site-options/' + subdomainID,
542 method: 'POST',
543 contentType: 'application/json',
544 data: JSON.stringify(formData),
545 success: function(response) {
546
547 // Clear server-side cache so next page load gets fresh data
548 $.post(ajaxurl, {
549 action: 'aacb_clear_cache',
550 _wpnonce: '<?php echo wp_create_nonce('aacb_clear_cache'); ?>'
551 });
552
553 // Clear browser-side widget caches so the admin sees their
554 // settings change immediately on the next page render.
555 // Modern widget (v2) namespaces its storage under aacx_ /
556 // aacx- prefixes; pre-2026 widget used `overrideOptions`.
557 // Cross-tab clearing: writing to `aacx_cache_invalidation`
558 // signals other open tabs to drop their own widget caches
559 // (handled by the widget's storage layer).
560 aacxClearWidgetCaches();
561
562 // Show success
563 $('#save-status').addClass('aacx-hidden');
564 $('#save-success').removeClass('aacx-hidden');
565 $('#save-widget-settings').prop('disabled', false);
566
567 // Hide success message after 3 seconds
568 setTimeout(function() {
569 $('#save-success').addClass('aacx-hidden');
570 }, 3000);
571 },
572 error: function(xhr, status, error) {
573 console.error('❌ Save failed:', {xhr, status, error});
574
575 $('#save-status').addClass('aacx-hidden');
576 $('#save-error').text('<?php esc_js(_e('Failed to save settings. Please try again.', 'allaccessible')); ?>').removeClass('aacx-hidden');
577 $('#save-widget-settings').prop('disabled', false);
578
579 setTimeout(function() {
580 $('#save-error').addClass('aacx-hidden');
581 }, 5000);
582 }
583 });
584 });
585
586 // Show troubleshooting link handler
587 $('#show-troubleshooting-link').on('click', function(e) {
588 e.preventDefault();
589
590 // Hide the link
591 $(this).addClass('aacx-hidden');
592
593 // Show the cache clearing button
594 $('#troubleshooting-buttons').removeClass('aacx-hidden');
595 });
596
597 // Reset cache button handler
598 $('#reset-cache-button').on('click', function(e) {
599 e.preventDefault();
600
601
602 // Clear browser-side caches (localStorage and cookies)
603 try {
604 aacxClearWidgetCaches();
605
606 // Clear server-side cache too
607 $.post(ajaxurl, {
608 action: 'aacb_clear_cache',
609 _wpnonce: '<?php echo wp_create_nonce('aacb_clear_cache'); ?>'
610 }, function() {
611 });
612
613 // Show success message
614 $('#reset-cache-success').removeClass('aacx-hidden');
615
616 // Hide success message after 5 seconds
617 setTimeout(function() {
618 $('#reset-cache-success').addClass('aacx-hidden');
619 }, 5000);
620
621 } catch (e) {
622 console.error('❌ Could not clear cache:', e);
623 alert('<?php esc_js(_e('Error clearing cache. Please check browser console.', 'allaccessible')); ?>');
624 }
625 });
626
627 // Clear all caches button handler (browser + server)
628 $('#invalidate-all-cache-button').on('click', function(e) {
629 e.preventDefault();
630
631
632 // Show loading status
633 $('#invalidate-cache-status').removeClass('aacx-hidden');
634 $('#invalidate-cache-success').addClass('aacx-hidden');
635 $('#invalidate-cache-error').addClass('aacx-hidden');
636
637 try {
638 // Step 1: Clear browser-side caches (localStorage and cookies)
639 if (typeof localStorage !== 'undefined') {
640 localStorage.removeItem('overrideOptions');
641 }
642
643 document.cookie = 'aacxValidated=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
644
645 // Step 2: Clear WordPress transient cache
646 $.post(ajaxurl, {
647 action: 'aacb_clear_cache',
648 _wpnonce: '<?php echo wp_create_nonce('aacb_clear_cache'); ?>'
649 }, function() {
650 });
651
652 // Step 3: Call AllAccessible API to invalidate server cache
653 const accountID = $('input[name="accountID"]').val();
654
655 if (accountID) {
656 $.ajax({
657 url: 'https://api.allaccessible.org/cache/invalidate-license',
658 method: 'POST',
659 contentType: 'application/json',
660 data: JSON.stringify({ accountId: accountID }),
661 success: function(response) {
662
663 // Hide loading, show success
664 $('#invalidate-cache-status').addClass('aacx-hidden');
665 $('#invalidate-cache-success').removeClass('aacx-hidden');
666
667 // Hide success message after 5 seconds
668 setTimeout(function() {
669 $('#invalidate-cache-success').addClass('aacx-hidden');
670 }, 5000);
671 },
672 error: function(xhr, status, error) {
673 console.error('❌ Server cache invalidation failed:', error);
674
675 // Hide loading, show error
676 $('#invalidate-cache-status').addClass('aacx-hidden');
677 $('#invalidate-cache-error').removeClass('aacx-hidden').text('<?php esc_js(_e('Server cache invalidation failed. Browser cache was cleared.', 'allaccessible')); ?>');
678
679 setTimeout(function() {
680 $('#invalidate-cache-error').addClass('aacx-hidden');
681 }, 5000);
682 }
683 });
684 } else {
685 console.warn('⚠️ No account ID found, skipping server cache invalidation');
686
687 // Hide loading, show partial success
688 $('#invalidate-cache-status').addClass('aacx-hidden');
689 $('#invalidate-cache-success').removeClass('aacx-hidden');
690
691 setTimeout(function() {
692 $('#invalidate-cache-success').addClass('aacx-hidden');
693 }, 5000);
694 }
695
696 } catch (e) {
697 console.error('❌ Could not clear caches:', e);
698 $('#invalidate-cache-status').addClass('aacx-hidden');
699 $('#invalidate-cache-error').removeClass('aacx-hidden').text('<?php esc_js(_e('Error clearing caches. Please check browser console.', 'allaccessible')); ?>');
700 }
701 });
702 });
703 </script>
704 <?php
705 }
706