PluginProbe
Booking Calendar / 11.8
Booking Calendar v11.8
11.9 11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 All 205 releases
booking / includes / page-settings-themes / _src / settings_themes_page.js

settings_themes_page.js in Booking Calendar 11.8, at includes/page-settings-themes/_src/settings_themes_page.js

975 lines 36.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * Appearance / Theme settings page UI.
3 */
4 ( function ( $, w ) {
5 'use strict';
6
7 var cfg = w.wpbc_settings_themes_page || {};
8 var localized_default_form_accent_color = String( cfg.form_accent_defaults && cfg.form_accent_defaults.booking_form_accent_color || '' ).trim();
9
10 /** @type {string} Default accent supplied by the PHP configuration constant. */
11 var default_form_accent_color = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test( localized_default_form_accent_color )
12 ? localized_default_form_accent_color
13 : '';
14 var preview_ajax = null;
15 var preview_timer = 0;
16 var preview_notice_timer = 0;
17 var preview_notice_message_timer = 0;
18
19 function trim_text( value ) {
20 return String( value || '' ).trim();
21 }
22
23 function make_asset_url( path ) {
24 path = String( path || '' );
25 if ( /^https?:\/\//i.test( path ) || /^\/\//.test( path ) ) {
26 return path;
27 }
28 return String( cfg.plugin_url || '' ).replace( /\/$/, '' ) + path;
29 }
30
31 function show_message( message, type, delay ) {
32 if ( typeof w.wpbc_admin_show_message === 'function' ) {
33 w.wpbc_admin_show_message( message, type || 'info', delay || 4000, false );
34 }
35 }
36
37 function pulse_element( $element, duration ) {
38 if ( ! $element || ! $element.length ) {
39 return;
40 }
41
42 $element
43 .removeClass( 'wpbc_theme_attention_pulse' )
44 .each( function () {
45 void this.offsetWidth;
46 } )
47 .addClass( 'wpbc_theme_attention_pulse' );
48
49 setTimeout( function () {
50 $element.removeClass( 'wpbc_theme_attention_pulse' );
51 }, duration || 2100 );
52 }
53
54 function pulse_latest_warning_notice() {
55 clearTimeout( preview_notice_message_timer );
56 preview_notice_message_timer = setTimeout( function () {
57 pulse_element( $( '#ajax_working .wpbc_inner_message.notice-warning' ).last() );
58 }, 50 );
59 }
60
61 function show_highlighted_notice( message, type, delay, $control ) {
62 if ( $control && $control.length ) {
63 pulse_element( $control );
64 }
65
66 show_message( message, type || 'warning', delay || 9000 );
67 pulse_latest_warning_notice();
68 }
69
70 function switch_panel( $tab ) {
71 var panel_id = $tab.attr( 'aria-controls' );
72 var $tabs = $tab.closest( '.wpbc_theme_rightbar_tabs' ).find( '[role="tab"]' );
73 var $panels = $( '.wpbc_theme_rightbar_panels [role="tabpanel"]' );
74
75 $tabs.attr( 'aria-selected', 'false' );
76 $tab.attr( 'aria-selected', 'true' );
77
78 $panels.attr( 'hidden', 'hidden' ).attr( 'aria-hidden', 'true' );
79 $( '#' + panel_id ).removeAttr( 'hidden' ).attr( 'aria-hidden', 'false' );
80 }
81
82 function toggle_group( $button ) {
83 var $group = $button.closest( '.wpbc_ui__collapsible_group' );
84 var $fields = $group.find( '> .group__fields' );
85 var is_open = $group.hasClass( 'is-open' );
86
87 $group.toggleClass( 'is-open', ! is_open );
88 $button.attr( 'aria-expanded', is_open ? 'false' : 'true' );
89 $fields.prop( 'hidden', is_open ).attr( 'aria-hidden', is_open ? 'true' : 'false' );
90 }
91
92 function get_form() {
93 return $( '[data-wpbc-theme-settings-form="1"]' ).first();
94 }
95
96 function collect_payload() {
97 var $form = get_form();
98 var data = {};
99
100 sync_form_style_choice();
101
102 $.each( $form.serializeArray(), function ( index, item ) {
103 if ( 0 === String( item.name || '' ).indexOf( 'wpbc_setup' ) ) {
104 return;
105 }
106 data[ item.name ] = item.value;
107 } );
108
109 data.booking_timeslot_picker = $form.find( '[name="booking_timeslot_picker"]' ).prop( 'checked' ) ? 'On' : 'Off';
110 data.booking_form_accent_enabled = $form.find( '[name="booking_form_accent_enabled"]' ).prop( 'checked' ) ? 'On' : 'Off';
111 data.resource_id = $( '#wpbc_theme_resource_id' ).val() || '';
112 data.months_count = $( '#wpbc_theme_months_count' ).val() || '';
113 data.preview_mode = $( '#wpbc_theme_preview_mode' ).val() || 'form';
114 data.custom_booking_form = $( '#wpbc_theme_custom_form' ).val() || 'standard';
115
116 return data;
117 }
118
119 function map_form_style_choice( value ) {
120 var choice = String( value || 'light_bordered' );
121 var current_theme = $( '#booking_form_theme' ).val() || '';
122 var parts;
123 var preset;
124
125 if ( 'custom' === choice ) {
126 return {
127 theme : current_theme,
128 preset: 'custom'
129 };
130 }
131
132 parts = choice.split( '_' );
133 preset = parts[1] || 'bordered';
134 if ( [ 'bordered', 'none', 'soft' ].indexOf( preset ) === -1 ) {
135 preset = 'bordered';
136 }
137
138 return {
139 theme : ( 'dark' === parts[0] ) ? 'wpbc_theme_dark_1' : '',
140 preset: preset
141 };
142 }
143
144 function get_form_style_choice_from_values() {
145 var theme = $( '#booking_form_theme' ).val() || '';
146 var preset = $( '#booking_form_appearance_preset' ).val() || 'bordered';
147 var prefix = theme ? 'dark' : 'light';
148
149 if ( 'custom' === preset ) {
150 return 'custom';
151 }
152 if ( [ 'bordered', 'none', 'soft' ].indexOf( preset ) === -1 ) {
153 preset = 'bordered';
154 }
155
156 return prefix + '_' + preset;
157 }
158
159 function sync_form_style_choice() {
160 var $checked = get_form().find( '[name="booking_form_style"]:checked' );
161 var mapped;
162
163 if ( ! $checked.length ) {
164 return;
165 }
166
167 mapped = map_form_style_choice( $checked.val() );
168 $( '#booking_form_theme' ).val( mapped.theme );
169 $( '#booking_form_appearance_preset' ).val( mapped.preset );
170 }
171
172 function sync_form_style_choice_selection() {
173 var choice = get_form_style_choice_from_values();
174 var $choices = get_form().find( '[name="booking_form_style"]' );
175
176 $choices.prop( 'checked', false );
177 $choices.filter( '[value="' + choice + '"]' ).prop( 'checked', true );
178
179 $( '.wpbc_theme_choice' ).removeClass( 'is-selected' );
180 $choices.filter( ':checked' ).closest( '.wpbc_theme_choice' ).addClass( 'is-selected' );
181 }
182
183 function apply_form_theme() {
184 var theme = $( '#booking_form_theme' ).val() || '';
185 var $preview = $( '[data-wpbc-theme-preview="1"]' );
186 var $theme_targets = $preview.add( $preview.find( '.wpbc_container.wpbc_form, .wpbc_container_booking_form' ) );
187
188 $theme_targets.each( function () {
189 var $target = $( this );
190 var classes = String( this.className || '' ).split( /\s+/ );
191
192 $.each( classes, function ( index, class_name ) {
193 if ( /^wpbc_theme_/.test( class_name ) && ! /^wpbc_theme_preview/.test( class_name ) ) {
194 $target.removeClass( class_name );
195 }
196 } );
197 } );
198 if ( theme ) {
199 $theme_targets.addClass( theme );
200 }
201
202 sync_form_style_choice_selection();
203 }
204
205 function get_form_appearance_presets() {
206 return {
207 bordered: {
208 background : '#ffffff',
209 borderColor: '#cccccc',
210 borderWidth: '1px',
211 radius : '2px',
212 padding : '10px 30px',
213 shadow : 'rgba(0, 0, 0, 0.05) 0px 2px 6px 0px'
214 },
215 none : {
216 background : 'transparent',
217 borderColor: 'transparent',
218 borderWidth: '0px',
219 radius : '0px',
220 padding : '0px',
221 shadow : 'none'
222 },
223 soft : {
224 background : '#f9f9fa',
225 borderColor: '#fff',
226 borderWidth: '3px',
227 radius : '8px',
228 padding : '20px',
229 shadow : 'rgba(15, 23, 42, 0.06) 0px 4px 16px 0px'
230 }
231 };
232 }
233
234 function is_dark_form_theme() {
235 return 'wpbc_theme_dark_1' === String( $( '#booking_form_theme' ).val() || '' );
236 }
237
238 function get_form_appearance_preset_for_theme( preset ) {
239 var presets = get_form_appearance_presets();
240
241 if ( ! is_dark_form_theme() ) {
242 return presets[preset] || presets.bordered;
243 }
244
245 if ( 'soft' === preset ) {
246 return {
247 background : '#1f2937',
248 borderColor: '#334155',
249 borderWidth: '3px',
250 radius : '8px',
251 padding : '20px',
252 shadow : 'rgba(0, 0, 0, 0.24) 0px 4px 16px 0px'
253 };
254 }
255
256 return presets[preset] || presets.bordered;
257 }
258
259 function sanitize_theme_color( value, fallback ) {
260 var v = String( value || '' ).trim();
261 return /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test( v ) || 'transparent' === v ? v : fallback;
262 }
263
264 function sanitize_theme_length( value, fallback ) {
265 var v = String( value || '' ).trim();
266 return /^\d+(?:\.\d+)?(?:px|rem|em|%)$/i.test( v ) ? v : fallback;
267 }
268
269 function sanitize_theme_spacing( value, fallback ) {
270 var v = String( value || '' ).trim().replace( /\s+/g, ' ' );
271 var parts = v ? v.split( ' ' ) : [];
272 var i;
273
274 if ( parts.length < 1 || parts.length > 4 ) {
275 return fallback;
276 }
277 for ( i = 0; i < parts.length; i++ ) {
278 if ( ! /^\d+(?:\.\d+)?(?:px|rem|em|%)$/i.test( parts[i] ) ) {
279 return fallback;
280 }
281 }
282 return parts.join( ' ' );
283 }
284
285 function mix_theme_colors( color, target, amount ) {
286 var source = sanitize_theme_color( color, default_form_accent_color ).replace( '#', '' );
287 var destination = sanitize_theme_color( target, '#000000' ).replace( '#', '' );
288 var channels = [];
289 var index;
290
291 if ( 3 === source.length ) {
292 source = source.replace( /./g, function ( value ) { return value + value; } );
293 }
294 for ( index = 0; index < 3; index++ ) {
295 channels.push( Math.round( parseInt( source.substr( index * 2, 2 ), 16 ) + ( ( parseInt( destination.substr( index * 2, 2 ), 16 ) - parseInt( source.substr( index * 2, 2 ), 16 ) ) * amount ) ) );
296 }
297
298 return '#' + channels.map( function ( channel ) { return ( '0' + channel.toString( 16 ) ).slice( -2 ); } ).join( '' );
299 }
300
301 function get_theme_color_luminance( color ) {
302 var hex = sanitize_theme_color( color, default_form_accent_color ).replace( '#', '' );
303 var channels;
304 if ( 3 === hex.length ) {
305 hex = hex.replace( /./g, function ( value ) { return value + value; } );
306 }
307 channels = [ 0, 1, 2 ].map( function ( index ) {
308 var channel = parseInt( hex.substr( index * 2, 2 ), 16 ) / 255;
309 return channel <= 0.03928 ? channel / 12.92 : Math.pow( ( channel + 0.055 ) / 1.055, 2.4 );
310 } );
311 return ( 0.2126 * channels[0] ) + ( 0.7152 * channels[1] ) + ( 0.0722 * channels[2] );
312 }
313
314 /**
315 * Apply the accent to preview variables while respecting Custom button controls.
316 *
317 * @param {Object} css_vars Resolved preview variables.
318 * @param {boolean} preserve_custom_button_colors Whether Custom button variables remain authoritative.
319 * @return {Object} Preview variables with the optional accent overlay.
320 */
321 function apply_form_accent_css_vars( css_vars, preserve_custom_button_colors ) {
322 var enabled = get_form().find( '[name="booking_form_accent_enabled"]' ).prop( 'checked' );
323 var accent_raw = String( get_form().find( '[name="booking_form_accent_color"]' ).val() || '' ).trim();
324 var accent = /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test( accent_raw ) ? accent_raw : default_form_accent_color;
325 var luminance;
326 var hover;
327 var contrast;
328 var hover_contrast;
329
330 if ( ! enabled ) {
331 return css_vars;
332 }
333 if ( ! /^#(?:[0-9a-f]{3}|[0-9a-f]{6})$/i.test( accent ) ) {
334 return css_vars;
335 }
336 luminance = get_theme_color_luminance( accent );
337 contrast = luminance > 0.18 ? '#000000' : '#ffffff';
338 hover = mix_theme_colors( accent, '#ffffff' === contrast ? '#000000' : '#ffffff', 0.10 );
339 hover_contrast = contrast;
340
341 var accent_overlay = {
342 '--wpbc_form-accent-color': accent,
343 '--wpbc_form-accent-hover-color': hover,
344 '--wpbc_form-accent-contrast-color': contrast,
345 '--wpbc_form-field-focus-border-color': accent,
346 '--wpbc_form-field-focus-shadow-color': accent,
347 '--wpbc_form-choice-checked-border-color': accent,
348 '--wpbc_form-choice-checked-color': accent,
349 '--wpbc_form-choice-focus-color': accent,
350 '--wpbc_form-button-background-color': accent,
351 '--wpbc_form-button-background-color-alt': accent,
352 '--wpbc_form-button-border-color': accent,
353 '--wpbc_form-button-text-color': contrast,
354 '--wpbc_form-button-text-color-alt': contrast,
355 '--wpbc_form-button-hover-background-color': hover,
356 '--wpbc_form-button-hover-border-color': hover,
357 '--wpbc_form-button-hover-text-color': hover_contrast,
358 '--wpbc_form-button-light-hover-border-color': accent,
359 '--wpbc_form-button-primary-hover-border-color': hover,
360 '--wpbc_form-page-break-color': accent
361 };
362
363 if ( preserve_custom_button_colors ) {
364 [
365 '--wpbc_form-button-background-color',
366 '--wpbc_form-button-background-color-alt',
367 '--wpbc_form-button-border-color',
368 '--wpbc_form-button-text-color',
369 '--wpbc_form-button-text-color-alt',
370 '--wpbc_form-button-hover-background-color',
371 '--wpbc_form-button-hover-border-color',
372 '--wpbc_form-button-hover-text-color',
373 '--wpbc_form-button-light-hover-border-color',
374 '--wpbc_form-button-primary-hover-border-color'
375 ].forEach( function ( css_var_name ) {
376 delete accent_overlay[ css_var_name ];
377 } );
378 }
379
380 return $.extend( {}, css_vars, accent_overlay );
381 }
382
383 function get_form_style_presets() {
384 return cfg.form_style_presets && 'object' === typeof cfg.form_style_presets ? cfg.form_style_presets : {};
385 }
386
387 function get_current_form_style() {
388 var $checked = get_form().find( '[name="booking_form_style"]:checked' );
389 return $checked.length ? String( $checked.val() || 'light_bordered' ) : 'light_bordered';
390 }
391
392 function get_custom_form_style_defaults() {
393 return $.extend( {
394 booking_form_custom_background_color : '#ffffff',
395 booking_form_custom_border_color : '#cccccc',
396 booking_form_custom_border_width : '1px',
397 booking_form_custom_border_radius : '2px',
398 booking_form_custom_padding_vertical : '10px',
399 booking_form_custom_padding_horizontal : '30px',
400 booking_form_custom_text_color : '#1d2327',
401 booking_form_custom_field_background_color : '#ffffff',
402 booking_form_custom_field_text_color : '#3c434a',
403 booking_form_custom_field_border_color : '#cccccc',
404 booking_form_custom_button_background_color: '#066aab',
405 booking_form_custom_button_text_color : '#ffffff',
406 booking_form_custom_button_border_color : '#066aab',
407 booking_form_custom_button_hover_background_color: '#055589',
408 booking_form_custom_button_hover_text_color: '#ffffff',
409 booking_form_custom_button_hover_border_color: '#055589',
410 booking_form_custom_secondary_button_background_color: '#fdfdfd',
411 booking_form_custom_secondary_button_text_color: '#444444',
412 booking_form_custom_secondary_button_border_color: '#eeeeee',
413 booking_form_custom_secondary_button_hover_background_color: '#fdfdfd',
414 booking_form_custom_secondary_button_hover_text_color: '#444444',
415 booking_form_custom_secondary_button_hover_border_color: '#4d91cd',
416 booking_form_custom_button_border_width: '1px',
417 booking_form_custom_button_border_radius: '3px'
418 }, cfg.custom_form_style_defaults && 'object' === typeof cfg.custom_form_style_defaults ? cfg.custom_form_style_defaults : {} );
419 }
420
421 function get_custom_form_style_css_vars() {
422 var defaults = get_custom_form_style_defaults();
423 var values = $.extend( {}, defaults, cfg.settings && 'object' === typeof cfg.settings ? cfg.settings : {} );
424
425 return {
426 '--wpbc-bfb-form-background' : sanitize_theme_color( values.booking_form_custom_background_color, defaults.booking_form_custom_background_color ),
427 '--wpbc-bfb-form-border-color' : sanitize_theme_color( values.booking_form_custom_border_color, defaults.booking_form_custom_border_color ),
428 '--wpbc-bfb-form-border-width' : sanitize_theme_length( values.booking_form_custom_border_width, defaults.booking_form_custom_border_width ),
429 '--wpbc-bfb-form-border-radius' : sanitize_theme_length( values.booking_form_custom_border_radius, defaults.booking_form_custom_border_radius ),
430 '--wpbc-bfb-form-padding' : sanitize_theme_length( values.booking_form_custom_padding_vertical, defaults.booking_form_custom_padding_vertical ) + ' ' + sanitize_theme_length( values.booking_form_custom_padding_horizontal, defaults.booking_form_custom_padding_horizontal ),
431 '--wpbc-bfb-form-box-shadow' : 'rgba(0, 0, 0, 0.05) 0px 2px 6px 0px',
432 '--wpbc_form-label-color' : sanitize_theme_color( values.booking_form_custom_text_color, defaults.booking_form_custom_text_color ),
433 '--wpbc_form-label-sublabel-color' : sanitize_theme_color( values.booking_form_custom_text_color, defaults.booking_form_custom_text_color ),
434 '--wpbc_form-label-error-color' : '#d63637',
435 '--wpbc_form-field-background-color' : sanitize_theme_color( values.booking_form_custom_field_background_color, defaults.booking_form_custom_field_background_color ),
436 '--wpbc_form-field-menu-color' : sanitize_theme_color( values.booking_form_custom_field_background_color, defaults.booking_form_custom_field_background_color ),
437 '--wpbc_form-field-text-color' : sanitize_theme_color( values.booking_form_custom_field_text_color, defaults.booking_form_custom_field_text_color ),
438 '--wpbc_form-field-border-color' : sanitize_theme_color( values.booking_form_custom_field_border_color, defaults.booking_form_custom_field_border_color ),
439 '--wpbc_form-field-border-color-spare': sanitize_theme_color( values.booking_form_custom_field_border_color, defaults.booking_form_custom_field_border_color ),
440 '--wpbc_form-field-focus-border-color': '#066aab',
441 '--wpbc_form-field-focus-shadow-color': '#066aab',
442 '--wpbc_form-field-disabled-color' : 'rgba(0, 0, 0, 0.2)',
443 '--wpbc_form-button-border-radius' : sanitize_theme_length( values.booking_form_custom_button_border_radius, defaults.booking_form_custom_button_border_radius ),
444 '--wpbc_form-button-border-style' : 'solid',
445 '--wpbc_form-button-border-size' : sanitize_theme_length( values.booking_form_custom_button_border_width, defaults.booking_form_custom_button_border_width ),
446 '--wpbc_form-button-background-color' : sanitize_theme_color( values.booking_form_custom_button_background_color, defaults.booking_form_custom_button_background_color ),
447 '--wpbc_form-button-background-color-alt': sanitize_theme_color( values.booking_form_custom_button_background_color, defaults.booking_form_custom_button_background_color ),
448 '--wpbc_form-button-border-color' : sanitize_theme_color( values.booking_form_custom_button_border_color, defaults.booking_form_custom_button_border_color ),
449 '--wpbc_form-button-text-color' : sanitize_theme_color( values.booking_form_custom_button_text_color, defaults.booking_form_custom_button_text_color ),
450 '--wpbc_form-button-text-color-alt' : sanitize_theme_color( values.booking_form_custom_button_text_color, defaults.booking_form_custom_button_text_color ),
451 '--wpbc_form-button-hover-background-color': sanitize_theme_color( values.booking_form_custom_button_hover_background_color, defaults.booking_form_custom_button_hover_background_color ),
452 '--wpbc_form-button-hover-border-color': sanitize_theme_color( values.booking_form_custom_button_hover_border_color, defaults.booking_form_custom_button_hover_border_color ),
453 '--wpbc_form-button-hover-text-color' : sanitize_theme_color( values.booking_form_custom_button_hover_text_color, defaults.booking_form_custom_button_hover_text_color ),
454 '--wpbc_form-choice-checked-border-color': '#066aab',
455 '--wpbc_form-choice-checked-color' : '#066aab',
456 '--wpbc_form-choice-focus-color' : '#066aab',
457 '--wpbc_form-button-light-background-color': sanitize_theme_color( values.booking_form_custom_secondary_button_background_color, defaults.booking_form_custom_secondary_button_background_color ),
458 '--wpbc_form-button-light-border-color': sanitize_theme_color( values.booking_form_custom_secondary_button_border_color, defaults.booking_form_custom_secondary_button_border_color ),
459 '--wpbc_form-button-light-border-size': sanitize_theme_length( values.booking_form_custom_button_border_width, defaults.booking_form_custom_button_border_width ),
460 '--wpbc_form-button-light-text-color' : sanitize_theme_color( values.booking_form_custom_secondary_button_text_color, defaults.booking_form_custom_secondary_button_text_color ),
461 '--wpbc_form-button-light-box-shadow' : '0 2px 10px 2px #ffffff54',
462 '--wpbc_form-button-light-hover-background-color': sanitize_theme_color( values.booking_form_custom_secondary_button_hover_background_color, defaults.booking_form_custom_secondary_button_hover_background_color ),
463 '--wpbc_form-button-light-hover-border-color': sanitize_theme_color( values.booking_form_custom_secondary_button_hover_border_color, defaults.booking_form_custom_secondary_button_hover_border_color ),
464 '--wpbc_form-button-light-hover-text-color': sanitize_theme_color( values.booking_form_custom_secondary_button_hover_text_color, defaults.booking_form_custom_secondary_button_hover_text_color ),
465 '--wpbc_form-button-light-hover-box-shadow': '0 2px 10px 2px #ffffff54',
466 '--wpbc_form-button-primary-hover-border-color': sanitize_theme_color( values.booking_form_custom_button_hover_border_color, defaults.booking_form_custom_button_hover_border_color ),
467 '--wpbc_form-page-break-color' : '#066aab'
468 };
469 }
470
471 function get_form_style_css_var_names() {
472 var keys = [];
473 var presets;
474
475 if ( Array.isArray( cfg.form_style_css_var_names ) && cfg.form_style_css_var_names.length ) {
476 return cfg.form_style_css_var_names;
477 }
478
479 presets = get_form_style_presets();
480 $.each( presets, function ( preset_key, preset ) {
481 if ( preset && preset.css_vars && 'object' === typeof preset.css_vars ) {
482 $.each( preset.css_vars, function ( var_name ) {
483 if ( -1 === keys.indexOf( var_name ) ) {
484 keys.push( var_name );
485 }
486 } );
487 }
488 } );
489
490 $.each( get_custom_form_style_css_vars(), function ( var_name ) {
491 if ( -1 === keys.indexOf( var_name ) ) {
492 keys.push( var_name );
493 }
494 } );
495
496 return keys;
497 }
498
499 function resolve_form_style_css_vars( style ) {
500 var presets = get_form_style_presets();
501 var preset = presets[ style ] || presets.light_bordered || {};
502
503 if ( 'custom' === style || preset.custom ) {
504 return apply_form_accent_css_vars( get_custom_form_style_css_vars(), true );
505 }
506
507 return apply_form_accent_css_vars( preset.css_vars && 'object' === typeof preset.css_vars ? preset.css_vars : {} );
508 }
509
510 function apply_form_style_to_preview() {
511 var style = get_current_form_style();
512 var presets = get_form_style_presets();
513 var preset = presets[ style ] || presets.light_bordered || {};
514 var css_vars = resolve_form_style_css_vars( style );
515 var css_var_names = get_form_style_css_var_names();
516 var is_custom = ( 'custom' === style || preset.custom );
517 var $preview = $( '[data-wpbc-theme-preview="1"]' );
518 var $targets = $preview.find( '.wpbc_container.wpbc_form, .wpbc_bfb_form, .wpbc_bfb__form_preview_section_container' );
519
520 $( '[data-wpbc-theme-custom-appearance-notice="1"]' ).toggle( is_custom );
521
522 if ( ! $targets.length ) {
523 return;
524 }
525
526 $targets
527 .removeClass( 'wpbc_bfb_form_appearance_custom' )
528 .each( function () {
529 var style_obj = this.style;
530
531 $.each( css_var_names, function ( index, var_name ) {
532 style_obj.removeProperty( var_name );
533 } );
534
535 $.each( css_vars, function ( var_name, value ) {
536 if ( '' !== String( value || '' ) ) {
537 style_obj.setProperty( var_name, value );
538 }
539 } );
540 } );
541
542 if ( is_custom ) {
543 $targets.filter( '.wpbc_container.wpbc_form, .wpbc_bfb_form' ).addClass( 'wpbc_bfb_form_appearance_custom' );
544 }
545 }
546
547 function resolve_form_appearance() {
548 var preset = $( '#booking_form_appearance_preset' ).val() || 'bordered';
549
550 if ( 'custom' === preset ) {
551 return get_form_appearance_presets().bordered;
552 }
553
554 return get_form_appearance_preset_for_theme( preset );
555 }
556
557 function apply_form_appearance() {
558 apply_form_style_to_preview();
559 }
560
561 function apply_calendar_skin() {
562 var $select = $( '[data-wpbc-theme-calendar-skin="1"]' );
563 var value = $select.find( 'option:selected' ).attr( 'data-wpbc-calendar-skin-url' ) || $select.val() || '';
564 var skin_url = value ? make_asset_url( value ) : '';
565
566 if ( skin_url && typeof w.wpbc__calendar__change_skin === 'function' && $( '#wpbc-calendar-skin-css' ).length ) {
567 w.wpbc__calendar__change_skin( skin_url );
568 }
569 }
570
571 function apply_time_skin() {
572 var value = $( '[data-wpbc-theme-time-skin="1"]' ).val() || '';
573 var skin_url = value ? make_asset_url( value ) : '';
574
575 if ( skin_url && typeof w.wpbc__css__change_skin === 'function' && $( '#wpbc-time_picker-skin-css' ).length ) {
576 w.wpbc__css__change_skin( skin_url, 'wpbc-time_picker-skin-css' );
577 }
578 }
579
580 function select_if_option_exists( $select, value ) {
581 var $option;
582
583 if ( ! $select.length || ! value ) {
584 return false;
585 }
586
587 $option = $select.find( 'option[value="' + value + '"]' );
588 if ( ! $option.length ) {
589 return false;
590 }
591
592 if ( $select.val() === value ) {
593 return false;
594 }
595
596 $select.val( value ).trigger( 'change' );
597 return true;
598 }
599
600 function parse_number_list( value ) {
601 if ( Array.isArray( value ) ) {
602 return $.map( value, function ( item ) {
603 var parsed = parseInt( item, 10 );
604 return isNaN( parsed ) ? null : parsed;
605 } );
606 }
607
608 return $.map( String( value || '' ).split( /\s*,\s*/ ), function ( item ) {
609 var parsed = parseInt( item, 10 );
610 return ( '' === item || isNaN( parsed ) ) ? null : parsed;
611 } );
612 }
613
614 function set_calendar_param( resource_id, key, value ) {
615 if ( w._wpbc && typeof w._wpbc.calendar__set_param_value === 'function' ) {
616 w._wpbc.calendar__set_param_value( resource_id, key, value );
617 }
618 }
619
620 function apply_days_selection_to_calendar( resource_id, days_selection, should_reinit ) {
621 var ds = days_selection || {};
622 var fixed_week_days;
623 var dynamic_specific;
624 var dynamic_week_days;
625
626 if ( ! resource_id || ! w._wpbc || typeof w._wpbc.calendar__set_param_value !== 'function' ) {
627 return;
628 }
629
630 fixed_week_days = parse_number_list( ds.fixed__week_days__start );
631 dynamic_specific = parse_number_list( ds.dynamic__days_specific );
632 dynamic_week_days = parse_number_list( ds.dynamic__week_days__start );
633
634 set_calendar_param( resource_id, 'days_select_mode', String( ds.days_select_mode || 'multiple' ) );
635 set_calendar_param( resource_id, 'fixed__days_num', parseInt( ds.fixed__days_num || 0, 10 ) );
636 set_calendar_param( resource_id, 'fixed__week_days__start', fixed_week_days.length ? fixed_week_days : [ -1 ] );
637 set_calendar_param( resource_id, 'dynamic__days_min', parseInt( ds.dynamic__days_min || 0, 10 ) );
638 set_calendar_param( resource_id, 'dynamic__days_max', parseInt( ds.dynamic__days_max || 0, 10 ) );
639 set_calendar_param( resource_id, 'dynamic__days_specific', dynamic_specific );
640 set_calendar_param( resource_id, 'dynamic__week_days__start', dynamic_week_days.length ? dynamic_week_days : [ -1 ] );
641
642 if ( typeof w.wpbc__conditions__SAVE_INITIAL__days_selection_params__bm === 'function' ) {
643 w.wpbc__conditions__SAVE_INITIAL__days_selection_params__bm( resource_id );
644 }
645
646 if ( should_reinit && typeof w.wpbc_cal__re_init === 'function' ) {
647 w.wpbc_cal__re_init( resource_id );
648 }
649 }
650
651 function ensure_calendar_only_days_selection() {
652 var $preview = $( '[data-wpbc-theme-preview="1"]' ).first();
653 var preview_mode = $preview.attr( 'data-preview-mode' ) || $( '#wpbc_theme_preview_mode' ).val() || 'form';
654 var resource_id = parseInt( $preview.attr( 'data-resource-id' ) || 0, 10 );
655 var expected = cfg.days_selection || {};
656 var expected_mode = String( expected.days_select_mode || 'multiple' );
657 var current_mode = null;
658 var $calendar;
659 var should_reinit = false;
660
661 if ( 'calendar' !== preview_mode || ! resource_id || ! expected_mode ) {
662 return;
663 }
664
665 if ( ! w._wpbc || typeof w._wpbc.calendar__get_param_value !== 'function' ) {
666 return;
667 }
668
669 current_mode = w._wpbc.calendar__get_param_value( resource_id, 'days_select_mode' );
670 if ( String( current_mode || '' ) === expected_mode ) {
671 return;
672 }
673
674 $calendar = $( '#calendar_booking' + resource_id );
675 should_reinit = $calendar.length && $calendar.hasClass( 'hasDatepick' );
676
677 apply_days_selection_to_calendar( resource_id, expected, should_reinit );
678 }
679
680 function apply_related_skins_for_theme( theme ) {
681 var calendar_skin = theme ? '/css/skins/24_9__dark_1.css' : '/css/skins/25_5__square_1.css';
682 var time_skin = '/css/time_picker_skins/form_style.css';
683
684 select_if_option_exists( $( '[data-wpbc-theme-calendar-skin="1"]' ), calendar_skin );
685 select_if_option_exists( $( '[data-wpbc-theme-time-skin="1"]' ), time_skin );
686 }
687
688 function pulse_preview_mode_control() {
689 var $control = $( '.wpbc_theme_control_preview_mode' ).first();
690
691 pulse_element( $control );
692
693 clearTimeout( preview_notice_timer );
694 preview_notice_timer = setTimeout( function () {
695 $control.removeClass( 'wpbc_theme_attention_pulse' );
696 }, 2100 );
697 }
698
699 function get_preview_notice_message( notice_type ) {
700 var i18n = cfg.i18n || {};
701
702 if ( 'form' === notice_type ) {
703 return i18n.form_preview_option_notice || 'This option is visible in the Booking form preview. Switch Preview to Booking form to inspect it.';
704 }
705
706 return '';
707 }
708
709 function maybe_show_preview_notice( $source ) {
710 var notice_type = $source.attr( 'data-wpbc-theme-preview-notice' ) || '';
711 var preview_mode = $( '#wpbc_theme_preview_mode' ).val() || 'form';
712 var message = get_preview_notice_message( notice_type );
713 var $control = $( '.wpbc_theme_control_preview_mode' ).first();
714
715 if ( ! message ) {
716 return;
717 }
718
719 if ( 'form' === notice_type && 'calendar' !== preview_mode ) {
720 return;
721 }
722
723 if ( 'form' === notice_type ) {
724 pulse_preview_mode_control();
725 show_highlighted_notice( message, 'warning', 9000 );
726 return;
727 }
728
729 show_highlighted_notice( message, 'warning', 9000, $control );
730 }
731
732 function show_calendar_only_theme_notice() {
733 var preview_mode = $( '#wpbc_theme_preview_mode' ).val() || 'form';
734
735 if ( 'calendar' !== preview_mode ) {
736 return;
737 }
738
739 pulse_preview_mode_control();
740 show_highlighted_notice(
741 cfg.i18n && cfg.i18n.calendar_only_theme_notice ? cfg.i18n.calendar_only_theme_notice : 'Preview is set to Calendar only. Switch Preview to Booking form to inspect the form theme.',
742 'warning',
743 9000
744 );
745 }
746
747 function sync_time_picker_preview() {
748 var is_enabled = get_form().find( '[name="booking_timeslot_picker"]' ).prop( 'checked' );
749 var $preview = $( '[data-wpbc-theme-preview="1"]' );
750 var time_selectors = 'select[name^="rangetime"], select[name^="starttime"], select[name^="endtime"], select[name^="durationtime"]';
751
752 if ( w._wpbc && typeof w._wpbc.set_other_param === 'function' ) {
753 w._wpbc.set_other_param( 'is_enabled_booking_timeslot_picker', !! is_enabled );
754 }
755
756 if ( is_enabled ) {
757 if ( w._wpbc && typeof w.wpbc_hook__init_timeselector === 'function' ) {
758 w.wpbc_hook__init_timeselector();
759 }
760 return;
761 }
762
763 $preview.find( '.wpbc_times_selector' ).remove();
764 $preview.find( time_selectors ).show();
765 }
766
767 function refresh_preview_mode_controls() {
768 var preview_mode = $( '#wpbc_theme_preview_mode' ).val() || 'form';
769 $( '[data-wpbc-theme-form-control="1"]' ).toggleClass( 'is-visible', 'form' === preview_mode );
770 }
771
772 function set_calendar_loading( is_loading ) {
773 var $panel = $( '[data-wpbc-theme-calendar-panel="1"]' );
774
775 $panel.toggleClass( 'is-loading', !! is_loading );
776 $panel.find( '.wpbc_theme_calendar_loading' ).remove();
777
778 if ( is_loading ) {
779 $panel.append(
780 '<div class="wpbc_calendar_loading wpbc_theme_calendar_loading">' +
781 '<span class="wpbc_icn_autorenew wpbc_animation_spin"></span>&nbsp;' +
782 trim_text( cfg.i18n && cfg.i18n.loading ? cfg.i18n.loading : 'Loading' ) +
783 '</div>'
784 );
785 }
786 }
787
788 function refresh_preview() {
789 var data = collect_payload();
790
791 if ( preview_ajax && preview_ajax.readyState !== 4 ) {
792 preview_ajax.abort();
793 }
794
795 data.action = cfg.preview_action;
796 data.nonce = cfg.nonce;
797
798 set_calendar_loading( true );
799 preview_ajax = $.post( cfg.ajax_url, data )
800 .done( function ( response ) {
801 if ( response && response.success && response.data && response.data.html ) {
802 $( '[data-wpbc-theme-preview="1"]' ).replaceWith( response.data.html );
803 if ( response.data.days_selection ) {
804 cfg.days_selection = response.data.days_selection;
805 }
806 apply_form_theme();
807 apply_form_appearance();
808 apply_calendar_skin();
809 apply_time_skin();
810 ensure_calendar_only_days_selection();
811 sync_time_picker_preview();
812 return;
813 }
814
815 show_message(
816 response && response.data && response.data.message ? response.data.message : ( cfg.i18n && cfg.i18n.preview_failed ? cfg.i18n.preview_failed : 'Unable to refresh calendar preview.' ),
817 'error',
818 10000
819 );
820 } )
821 .fail( function ( xhr, text_status ) {
822 if ( 'abort' === text_status ) {
823 return;
824 }
825 show_message( cfg.i18n && cfg.i18n.preview_failed ? cfg.i18n.preview_failed : 'Unable to refresh calendar preview.', 'error', 10000 );
826 } )
827 .always( function () {
828 set_calendar_loading( false );
829 } );
830 }
831
832 function schedule_preview_refresh() {
833 clearTimeout( preview_timer );
834 preview_timer = setTimeout( refresh_preview, 180 );
835 }
836
837 function save_settings() {
838 var $button = $( '[data-wpbc-theme-save="1"]' );
839 var original_text = $button.data( 'wpbc-original-text' );
840 var data = collect_payload();
841
842 if ( ! original_text ) {
843 original_text = $button.html();
844 $button.data( 'wpbc-original-text', original_text );
845 }
846
847 data.action = cfg.action;
848 data.nonce = cfg.nonce;
849
850 $button.addClass( 'disabled' ).attr( 'aria-disabled', 'true' );
851 $button.find( '.in-button-text' ).html( '&nbsp;&nbsp;' + trim_text( cfg.i18n && cfg.i18n.saving ? cfg.i18n.saving : 'Saving' ) + '...' );
852
853 $.post( cfg.ajax_url, data )
854 .done( function ( response ) {
855 if ( response && response.success ) {
856 show_message( response.data && response.data.message ? response.data.message : ( cfg.i18n && cfg.i18n.saved ? cfg.i18n.saved : 'Saved' ), 'success', 3000 );
857 cfg.settings = response.data && response.data.settings ? response.data.settings : cfg.settings;
858 return;
859 }
860
861 show_message(
862 response && response.data && response.data.message ? response.data.message : ( cfg.i18n && cfg.i18n.save_failed ? cfg.i18n.save_failed : 'Unable to save appearance settings.' ),
863 'error',
864 10000
865 );
866 } )
867 .fail( function () {
868 show_message( cfg.i18n && cfg.i18n.save_failed ? cfg.i18n.save_failed : 'Unable to save appearance settings.', 'error', 10000 );
869 } )
870 .always( function () {
871 $button.removeClass( 'disabled' ).removeAttr( 'aria-disabled' ).html( original_text );
872 } );
873 }
874
875 function bind_events() {
876 $( document ).on( 'click', '.wpbc_theme_rightbar_tabs [role="tab"]', function ( event ) {
877 event.preventDefault();
878 switch_panel( $( this ) );
879 } );
880
881 $( document ).on( 'click', '.wpbc_theme_premium_dismiss a', function ( event ) {
882 event.stopPropagation();
883 } );
884
885 $( document ).on( 'click', '.wpbc_theme_rightbar_panels .wpbc_ui__collapsible_group > .group__header', function ( event ) {
886 event.preventDefault();
887 toggle_group( $( this ) );
888 } );
889
890 $( document ).on( 'click', '[data-wpbc-theme-save="1"]', function ( event ) {
891 event.preventDefault();
892 if ( ! $( this ).hasClass( 'disabled' ) ) {
893 save_settings();
894 }
895 } );
896
897 $( document ).on( 'submit', '[data-wpbc-theme-settings-form="1"]', function () {
898 return true;
899 } );
900
901 $( document ).on( 'change', '[name="booking_form_theme"]', function () {
902 apply_form_theme();
903 apply_related_skins_for_theme( $( this ).val() || '' );
904 show_calendar_only_theme_notice();
905 } );
906
907 $( document ).on( 'change', '[name="booking_form_style"]', function () {
908 sync_form_style_choice();
909 apply_form_theme();
910 apply_form_appearance();
911 apply_related_skins_for_theme( $( '#booking_form_theme' ).val() || '' );
912 show_calendar_only_theme_notice();
913 schedule_preview_refresh();
914 } );
915
916 $( document ).on( 'input change', '[data-wpbc-theme-appearance-control]', function () {
917 // Coloris emits continuous input events. Accent and appearance values are
918 // CSS-only, so update the existing preview without rebuilding it by AJAX.
919 apply_form_appearance();
920 } );
921
922 $( document ).on( 'change', '[data-wpbc-theme-accent-toggle="1"]', function () {
923 $( '[data-wpbc-theme-accent-dependent="1"]' ).toggle( $( this ).prop( 'checked' ) );
924 apply_form_appearance();
925 } );
926
927 $( document ).on( 'change', '[data-wpbc-theme-calendar-skin="1"]', function () {
928 apply_calendar_skin();
929 } );
930
931 $( document ).on( 'change', '[data-wpbc-theme-time-skin="1"]', function () {
932 apply_time_skin();
933 } );
934
935 $( document ).on( 'change', '[name="booking_timeslot_picker"]', function () {
936 sync_time_picker_preview();
937 schedule_preview_refresh();
938 } );
939
940 $( document ).on( 'change', '[data-wpbc-theme-preview-notice]', function () {
941 maybe_show_preview_notice( $( this ) );
942 } );
943
944 $( document ).on( 'change', '#wpbc_theme_resource_id, #wpbc_theme_months_count, #wpbc_theme_custom_form', function () {
945 schedule_preview_refresh();
946 } );
947
948 $( document ).on( 'change', '#wpbc_theme_preview_mode', function () {
949 refresh_preview_mode_controls();
950 schedule_preview_refresh();
951 } );
952 }
953
954 $( function () {
955 if ( ! $( '[data-wpbc-theme-page="1"]' ).length ) {
956 return;
957 }
958
959 bind_events();
960 if ( w.Coloris ) {
961 w.Coloris( {
962 el: '.wpbc_theme_coloris',
963 alpha: false,
964 format: 'hex',
965 themeMode: 'auto'
966 } );
967 }
968 refresh_preview_mode_controls();
969 apply_form_theme();
970 apply_form_appearance();
971 ensure_calendar_only_days_selection();
972 sync_time_picker_preview();
973 } );
974 }( jQuery, window ) );
975