PluginProbe
Booking Calendar / 11.1
Booking Calendar v11.1
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-form-builder / ajax / _src / bfb-ajax.js

bfb-ajax.js in Booking Calendar 11.1, at includes/page-form-builder/ajax/_src/bfb-ajax.js

1,125 lines 35.5 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 // ---------------------------------------------------------------------------------------------------------------------
2 // == File ../includes/page-form-builder/ajax/_out/bfb-ajax.js - after refactor 2026-02-28 15:14 rolback to this paoint, if something will go wrong.
3 // ---------------------------------------------------------------------------------------------------------------------
4 (function (w, d, $) {
5 'use strict';
6
7 var AjaxCfg = w.WPBC_BFB_Ajax || {};
8 if ( ! AjaxCfg.url ) {
9 // Not on builder page or config not injected.
10 return;
11 }
12
13 if ( ! $ || ! $.ajax ) {
14 // In WP admin jQuery should exist; if not, stop to avoid silent corruption.
15 wpbc_admin_show_message( 'WPBC BFB: jQuery is not available.', 'error', 10000 );
16 console.error( 'WPBC BFB: jQuery is not available.' );
17 return;
18 }
19
20 function get_save_source( cfg, btn ) {
21
22 // priority: button attr -> global cfg -> default
23 var v = '';
24 try {
25 if ( btn && btn.getAttribute ) {
26 v = btn.getAttribute( 'data-wpbc-bfb-save-source' ) || '';
27 }
28 } catch ( e ) {}
29
30 if ( ! v && cfg && cfg.save_source ) {
31 v = cfg.save_source;
32 }
33
34 v = String( v || 'auto' ).toLowerCase();
35 if ( [ 'builder', 'advanced', 'auto' ].indexOf( v ) === -1 ) {
36 v = 'builder';
37 }
38 return v;
39 }
40
41 function read_advanced_mode_payload() {
42
43 // best: API (syncs CodeMirror -> textarea)
44 if ( w.wpbc_bfb_advanced_editor_api && typeof w.wpbc_bfb_advanced_editor_api.get_values === 'function' ) {
45 try {
46 return w.wpbc_bfb_advanced_editor_api.get_values();
47 } catch ( e ) {}
48 }
49
50 // fallback: read textareas directly (may be stale if CM not saved)
51 var ta_form = d.getElementById( 'wpbc_bfb__advanced_form_editor' );
52 var ta_content = d.getElementById( 'wpbc_bfb__content_form_editor' );
53
54 return {
55 advanced_form: ta_form ? String( ta_form.value || '' ) : '',
56 content_form : ta_content ? String( ta_content.value || '' ) : '',
57 is_dirty : false
58 };
59 }
60
61 function has_text( v ) {
62 return !! ( v && String( v ).trim() );
63 }
64
65 /**
66 * Serialize values so payload matches previous XMLHttpRequest behavior:
67 * - null/undefined => ''
68 * - objects/arrays => JSON.stringify(...)
69 * - everything else => String(...)
70 *
71 * @param {*} value
72 * @return {string}
73 */
74 function wpbc_bfb__serialize_post_value( value ) {
75
76 if ( value === null || typeof value === 'undefined' ) {
77 return '';
78 }
79
80 if ( typeof value === 'object' ) {
81 try {
82 return JSON.stringify( value );
83 } catch ( e ) {
84 return '';
85 }
86 }
87
88 return String( value );
89 }
90
91 /**
92 * Normalize payload to scalar strings so jQuery does not produce "[object Object]".
93 *
94 * @param {Object} raw_data
95 * @return {Object}
96 */
97 function wpbc_bfb__normalize_post_data( raw_data ) {
98
99 var out = {};
100 raw_data = raw_data || {};
101
102 for ( var k in raw_data ) {
103 if ( ! Object.prototype.hasOwnProperty.call( raw_data, k ) ) {
104 continue;
105 }
106 out[ k ] = wpbc_bfb__serialize_post_value( raw_data[ k ] );
107 }
108
109 return out;
110 }
111
112 /**
113 * POST helper (jQuery only) returning jqXHR promise.
114 * IMPORTANT: dataType:'text' so callers keep manual JSON.parse exactly as before.
115 *
116 * @param {string} url
117 * @param {Object} payload
118 * @return {jqXHR}
119 */
120 function wpbc_bfb__ajax_post( url, payload ) {
121
122 return $.ajax( {
123 url : url,
124 type : 'POST',
125 data : wpbc_bfb__normalize_post_data( payload ),
126 dataType : 'text',
127 contentType : 'application/x-www-form-urlencoded; charset=UTF-8'
128 } );
129 }
130
131 /**
132 * Safe JSON parse helper.
133 *
134 * @param {string} text
135 * @return {Object|null}
136 */
137 function wpbc_bfb__safe_json_parse( text ) {
138 try {
139 return JSON.parse( text );
140 } catch ( e ) {
141 return null;
142 }
143 }
144
145 /**
146 * Parse combined length string like "100%", "320px", "12.5rem".
147 *
148 * @param {string} value
149 * @return {{num:string, unit:string}}
150 */
151 function parse_length_value( value ) {
152 var raw = String( value == null ? '' : value ).trim();
153 var m = raw.match( /^\s*(-?\d+(?:\.\d+)?)\s*([a-z%]*)\s*$/i );
154 if ( ! m ) {
155 return { num: raw, unit: '' };
156 }
157 return { num: ( m[1] || '' ), unit: ( m[2] || '' ) };
158 }
159
160 /**
161 * SAVE: send current Builder structure (+ exported shortcodes) to PHP.
162 *
163 * Uses:
164 * - WPBC_BFB_Ajax.url / nonce_save / form_name / engine / engine_version
165 * - Optional WPBC_BFB_Exporter.export_all()
166 * - Busy helpers wpbc_bfb__button_busy_start / _end if available.
167 *
168 * IMPORTANT:
169 * - Local (form) settings are collected ONLY here (Save form),
170 * via event "wpbc:bfb:form_settings:collect".
171 */
172 function wpbc_bfb__ajax_save_current_form( btn, done_cb ) {
173
174 var cfg = w.WPBC_BFB_Ajax || {};
175 var builder = w.wpbc_bfb || null;
176
177 var $btn = btn ? $( btn ) : null;
178
179 // --- Busy START ----------------------------------------------------------------------------------------------
180 if ( $btn && $btn.length ) {
181 // Will read data-wpbc-u-busy-text="Saving..." from the <a> and add spinner.
182 if ( typeof w.wpbc_bfb__button_busy_start === 'function' ) { // FIX: guard
183 w.wpbc_bfb__button_busy_start( $btn );
184 } else {
185 $btn.prop( 'disabled', true );
186 }
187 } else if ( btn ) {
188 btn.disabled = true;
189 }
190
191 // --- Gather structure ----------------------------------------------------------------------------------------
192 var structure = ( builder && typeof builder.get_structure === 'function' ) ? builder.get_structure() : [];
193
194 // --- Gather current form settings (event-based; modules can contribute) -------------------------------------
195 // ONLY supported settings format:
196 // {
197 // options : { key: value, ... },
198 // css_vars : [],
199 // bfb_options : { advanced_mode_source: 'builder'|'advanced'|'auto' }
200 // }
201 var form_settings = {
202 options : {},
203 css_vars : [],
204 bfb_options : { advanced_mode_source: 'builder' }
205 };
206
207 // Let Settings Options (and any other module) contribute local form settings.
208 wpbc_bfb__dispatch_event_safe( 'wpbc:bfb:form_settings:collect', {
209 settings : form_settings,
210 form_name: cfg.form_name || 'standard'
211 } );
212
213 // Normalize css_vars to array (schema enforcement).
214 if ( form_settings && form_settings.css_vars && ! Array.isArray( form_settings.css_vars ) ) {
215 var out = [];
216 try {
217 for ( var k in form_settings.css_vars ) {
218 if ( Object.prototype.hasOwnProperty.call( form_settings.css_vars, k ) ) {
219 out.push( { name: String( k ), value: String( form_settings.css_vars[ k ] ) } );
220 }
221 }
222 } catch ( _e0 ) {}
223 form_settings.css_vars = out;
224 }
225 if ( ! form_settings.bfb_options || typeof form_settings.bfb_options !== 'object' ) {
226 form_settings.bfb_options = { advanced_mode_source: 'builder' };
227 }
228 if ( ! form_settings.bfb_options.advanced_mode_source ) {
229 form_settings.bfb_options.advanced_mode_source = 'builder';
230 }
231
232 var payload = {
233 action : 'WPBC_AJX_BFB_SAVE_FORM_CONFIG',
234 nonce : cfg.nonce_save || '',
235 form_name : cfg.form_name || 'standard',
236 engine : cfg.engine || 'bfb',
237 engine_version: cfg.engine_version || '1.0',
238 structure : JSON.stringify( structure ),
239 settings : JSON.stringify( form_settings )
240 };
241
242 // -----------------------------------------------------------------------------
243 // Choose where advanced_form + content_form are taken from.
244 // -----------------------------------------------------------------------------
245 var save_source = get_save_source( cfg, btn );
246
247 // 1) Try Advanced Mode text (if selected / auto+dirty).
248 var adv = null;
249
250 if ( save_source === 'advanced' || save_source === 'auto' ) {
251 adv = read_advanced_mode_payload();
252
253 var can_use_advanced =
254 ( save_source === 'advanced' ) ||
255 ( save_source === 'auto' && adv && adv.is_dirty );
256
257 if ( can_use_advanced ) {
258
259 // If user forced "advanced" but both are empty -> fallback to exporter.
260 if ( ! has_text( adv.advanced_form ) && ! has_text( adv.content_form ) ) {
261 wpbc_admin_show_message( 'Advanced Mode is selected, but editors are empty. Using Builder export.', 'warning', 6000 );
262 } else {
263 if ( has_text( adv.advanced_form ) ) {
264 payload.advanced_form = adv.advanced_form;
265 }
266 if ( has_text( adv.content_form ) ) {
267 payload.content_form = adv.content_form;
268 }
269 form_settings.bfb_options.advanced_mode_source = 'advanced';
270 payload.settings = JSON.stringify( form_settings ); // keep payload in sync.
271 }
272 }
273 }
274
275 // 2) If not taken from Advanced Mode -> export from Builder structure (current behavior).
276 if ( ! payload.advanced_form || ! payload.content_form ) {
277
278 if ( w.WPBC_BFB_Exporter && typeof w.WPBC_BFB_Exporter.export_all === 'function' ) {
279 try {
280
281 // Prefer Option A pack: settings.options.*
282 var opt_pack = form_settings.options;
283 var width_combined = opt_pack.booking_form_layout_width || '';
284 var parsed_width = parse_length_value( width_combined );
285
286 var export_options = {
287 gapPercent : 3,
288 form_slug : payload.form_name,
289 form_width_value: parsed_width.num,
290 form_width_unit : parsed_width.unit
291 };
292
293 var export_result = w.WPBC_BFB_Exporter.export_all( structure || [], export_options );
294
295 if ( export_result ) {
296 if ( ! payload.advanced_form && export_result.advanced_form ) {
297 payload.advanced_form = export_result.advanced_form;
298 }
299 if ( ! payload.content_form && export_result.fields_data ) {
300 payload.content_form = export_result.fields_data;
301 }
302 }
303
304 form_settings.bfb_options.advanced_mode_source = 'builder';
305 payload.settings = JSON.stringify( form_settings ); // keep payload in sync.
306
307 } catch ( e ) {
308 wpbc_admin_show_message( 'WPBC BFB: export_all error', 'error', 10000 );
309 console.error( 'WPBC BFB: export_all error', e );
310 }
311 }
312 }
313
314 // Update payload before send via AJAX, if needed.
315 var hook_detail = { payload: payload };
316 wpbc_bfb__dispatch_event_safe( 'wpbc:bfb:form:before_save_payload', hook_detail );
317 payload = hook_detail.payload;
318
319 var ajax_request = wpbc_bfb__ajax_post( cfg.url, payload );
320
321 ajax_request
322 .done(
323 function ( response_text, _text_status, jqxhr ) {
324
325 // Keep old behavior: only treat exact 200 as success.
326 if ( ! jqxhr || jqxhr.status !== 200 ) {
327 if ( typeof done_cb === 'function' ) {
328 try { done_cb( false, null ); } catch ( _e1 ) {}
329 }
330 wpbc_admin_show_message( 'WPBC BFB: save HTTP error', 'error', 10000 );
331 console.error( 'WPBC BFB: save HTTP error', jqxhr ? jqxhr.status : 0 );
332 return;
333 }
334
335 var resp = wpbc_bfb__safe_json_parse( response_text );
336 if ( ! resp ) {
337 wpbc_admin_show_message( 'WPBC BFB: save JSON parse error', 'error', 10000 );
338 console.error( 'WPBC BFB: save JSON parse error', response_text );
339 return;
340 }
341
342 if ( ! resp || ! resp.success ) {
343 const error_message = ( resp.data && resp.data.message ) ? resp.data.message : '';
344 wpbc_admin_show_message( 'WPBC BFB: save failed.' + ' | ' + error_message, 'error', 10000 );
345 console.error( 'WPBC BFB: save failed', resp );
346 return;
347 }
348
349 if ( typeof done_cb === 'function' ) {
350 try { done_cb( true, resp ); } catch ( _e2 ) {}
351 }
352
353 wpbc_bfb__dispatch_event_safe( 'wpbc:bfb:form:ajax_saved', {
354 loaded_data: resp.data,
355 form_name : cfg.form_name || 'standard'
356 } );
357
358 // Optional: visual feedback.
359 wpbc_admin_show_message( 'Form saved', 'success', 1000, false );
360 }
361 )
362 .fail( function ( jqxhr ) {
363
364 // Old behavior: status != 200 triggers done_cb(false, null).
365 if ( typeof done_cb === 'function' ) {
366 try { done_cb( false, null ); } catch ( _e3 ) {}
367 }
368
369 wpbc_admin_show_message( 'WPBC BFB: save HTTP error', 'error', 10000 );
370 console.error( 'WPBC BFB: save HTTP error', jqxhr && jqxhr.status, jqxhr && jqxhr.statusText );
371 } )
372 .always( function () {
373
374 // --- Busy END (always) ----------------------------------------------------------------------------------
375 if ( $btn && $btn.length && typeof w.wpbc_bfb__button_busy_end === 'function' ) {
376 w.wpbc_bfb__button_busy_end( $btn );
377 } else if ( $btn && $btn.length ) {
378 $btn.prop( 'disabled', false ).removeClass( 'wpbc-is-busy' );
379 } else if ( btn ) {
380 btn.disabled = false;
381 }
382 } );
383 }
384
385 function apply_advanced_mode_texts( advanced_form, content_form, advanced_mode_source ) {
386
387 var af = ( advanced_form == null ) ? '' : String( advanced_form );
388 var cf = ( content_form == null ) ? '' : String( content_form );
389
390 // Always update underlying <textarea> so it works even if CodeMirror isn't inited yet.
391 var ta_form = d.getElementById( 'wpbc_bfb__advanced_form_editor' );
392 var ta_content = d.getElementById( 'wpbc_bfb__content_form_editor' );
393
394 if ( ta_form ) {
395 ta_form.value = af;
396 }
397 if ( ta_content ) {
398 ta_content.value = cf;
399 }
400
401 // Notify Advanced Mode module (if loaded) to sync CodeMirror + flags.
402 wpbc_bfb__dispatch_event_safe( 'wpbc:bfb:advanced_text:apply', {
403 advanced_form : af,
404 content_form : cf,
405 advanced_mode_source: advanced_mode_source
406 } );
407 }
408
409 /**
410 * LOAD: fetch FormConfig from PHP and hand structure to Builder.
411 *
412 * Also uses busy helpers if available.
413 */
414 function wpbc_bfb__ajax_load_current_form( btn, done_cb ) {
415
416 var cfg = w.WPBC_BFB_Ajax || {};
417 var builder = w.wpbc_bfb || null;
418
419 var $btn = btn ? $( btn ) : null;
420
421 // --- Busy START ----------------------------------------------------------------------------------------------
422 if ( $btn && $btn.length ) {
423 if ( typeof w.wpbc_bfb__button_busy_start === 'function' ) { // FIX: guard
424 w.wpbc_bfb__button_busy_start( $btn );
425 } else {
426 $btn.prop( 'disabled', true );
427 }
428 } else if ( btn ) {
429 btn.disabled = true;
430 }
431
432 var payload = {
433 action : 'WPBC_AJX_BFB_LOAD_FORM_CONFIG',
434 nonce : cfg.nonce_load || '',
435 form_name: cfg.form_name || 'standard'
436 };
437
438 wpbc_admin_show_message_processing( '' );
439 // wpbc_admin_show_message( 'Processing', 'info', 1000, false ); // Optional: visual feedback.
440
441 var ajax_request = wpbc_bfb__ajax_post( cfg.url, payload );
442
443 ajax_request
444 .done( function ( response_text, _text_status, jqxhr ) {
445
446 if ( ! jqxhr || jqxhr.status !== 200 ) {
447
448 if ( typeof done_cb === 'function' ) {
449 try { done_cb( false, null ); } catch ( _e0 ) {}
450 }
451
452 wpbc_admin_show_message( 'WPBC BFB: load HTTP error', 'error', 10000 );
453 console.error( 'WPBC BFB: load HTTP error', jqxhr ? jqxhr.status : 0 );
454 return;
455 }
456
457 var resp = wpbc_bfb__safe_json_parse( response_text );
458 if ( ! resp ) {
459 wpbc_admin_show_message( 'WPBC BFB: load JSON parse error', 'error', 10000 );
460 console.error( 'WPBC BFB: load JSON parse error', response_text );
461 return;
462 }
463
464 if ( ! resp || ! resp.success || ! resp.data ) {
465 wpbc_admin_show_message( 'WPBC BFB: load failed', 'error', 10000 );
466 console.error( 'WPBC BFB: load failed', resp );
467 return;
468 }
469
470 var data = resp.data;
471 var structure = data.structure || [];
472
473 // Apply Advanced Mode saved texts (if provided by PHP).
474 if ( data && ( typeof data.advanced_form !== 'undefined' || typeof data.content_form !== 'undefined' ) ) {
475 var ams = '';
476 try {
477 var s1 = ( typeof data.settings === 'string' ) ? JSON.parse( data.settings ) : data.settings;
478 ams = ( s1 && s1.bfb_options && s1.bfb_options.advanced_mode_source ) ? s1.bfb_options.advanced_mode_source : '';
479 } catch ( _e1 ) {}
480
481 apply_advanced_mode_texts(
482 data.advanced_form || '',
483 data.content_form || '',
484 ams
485 );
486 }
487
488 // Apply settings to UI (local form settings) if provided.
489 if ( data.settings ) {
490 var parsed_settings = null;
491 try {
492 parsed_settings = ( typeof data.settings === 'string' ) ? JSON.parse( data.settings ) : data.settings;
493 } catch ( _e2 ) {
494 parsed_settings = null;
495 }
496 if ( parsed_settings ) {
497 wpbc_bfb__dispatch_event_safe( 'wpbc:bfb:form_settings:apply', {
498 settings : parsed_settings,
499 form_name: cfg.form_name || 'standard'
500 } );
501 }
502 }
503
504 wpbc_bfb__dispatch_event_safe( 'wpbc:bfb:form:ajax_loaded', {
505 loaded_data: data,
506 form_name : cfg.form_name || 'standard'
507 } );
508
509 if ( typeof done_cb === 'function' ) {
510 try { done_cb( true, data ); } catch ( _e3 ) {}
511 }
512
513 // Prefer your existing callback if it already knows how to init Builder.
514 if ( typeof w.wpbc_bfb__on_structure_loaded === 'function' ) {
515 w.wpbc_bfb__on_structure_loaded( structure );
516 wpbc_admin_show_message( 'Done', 'info', 1000, false );
517 } else if ( builder && typeof builder.load_structure === 'function' ) {
518 builder.load_structure( structure );
519 wpbc_admin_show_message( 'Done', 'info', 1000, false );
520 } else {
521 console.warn( 'WPBC BFB: no loader for structure', structure );
522 }
523 } )
524 .fail( function ( jqxhr ) {
525
526 if ( typeof done_cb === 'function' ) {
527 try { done_cb( false, null ); } catch ( _e4 ) {}
528 }
529
530 wpbc_admin_show_message( 'WPBC BFB: load HTTP error', 'error', 10000 );
531 console.error( 'WPBC BFB: load HTTP error', jqxhr && jqxhr.status, jqxhr && jqxhr.statusText );
532 } )
533 .always( function () {
534
535 // --- Busy END (always) ----------------------------------------------------------------------------------
536 if ( $btn && $btn.length && typeof w.wpbc_bfb__button_busy_end === 'function' ) {
537 w.wpbc_bfb__button_busy_end( $btn );
538 } else if ( $btn && $btn.length ) {
539 $btn.prop( 'disabled', false ).removeClass( 'wpbc-is-busy' );
540 } else if ( btn ) {
541 btn.disabled = false;
542 }
543 } );
544 }
545
546 function wpbc_bfb__ajax_create_form( btn, create_payload, template_form_key, done_cb ) {
547
548 var cfg = w.WPBC_BFB_Ajax || {};
549
550 if ( ! cfg.url || ! cfg.nonce_create ) {
551 wpbc_admin_show_message( 'WPBC BFB: create config is missing (url/nonce).', 'error', 10000 );
552 if ( typeof done_cb === 'function' ) {
553 done_cb( false, null );
554 }
555 return;
556 }
557
558 template_form_key = String( template_form_key || '' ).trim();
559 if ( template_form_key === '__blank__' ) {
560 template_form_key = '';
561 }
562
563 var payload = {
564 action : 'WPBC_AJX_BFB_CREATE_FORM_CONFIG',
565 nonce : cfg.nonce_create,
566 form_name : String( create_payload.form_slug || '' ),
567 template_form_name: template_form_key || '',
568 title : String( create_payload.form_title || '' ),
569 description : String( create_payload.form_description || '' ),
570 image_url : String( create_payload.form_image_url || '' )
571 };
572
573 var ajax_request = wpbc_bfb__ajax_post( cfg.url, payload );
574
575 ajax_request
576 .done( function ( response_text, _text_status, jqxhr ) {
577
578 if ( ! jqxhr || jqxhr.status !== 200 ) {
579 wpbc_admin_show_message( 'WPBC BFB: create HTTP error', 'error', 10000 );
580 if ( typeof done_cb === 'function' ) {
581 done_cb( false, null );
582 }
583 return;
584 }
585
586 var resp = wpbc_bfb__safe_json_parse( response_text );
587 if ( ! resp ) {
588 wpbc_admin_show_message( 'WPBC BFB: create JSON parse error', 'error', 10000 );
589 if ( typeof done_cb === 'function' ) {
590 done_cb( false, null );
591 }
592 return;
593 }
594
595 if ( ! resp || ! resp.success || ! resp.data ) {
596 var msg = ( resp && resp.data && resp.data.message ) ? resp.data.message : 'WPBC BFB: create failed';
597 wpbc_admin_show_message( msg, 'error', 10000 );
598 if ( typeof done_cb === 'function' ) {
599 done_cb( false, resp );
600 }
601 return;
602 }
603
604 // Switch current form to the newly created key.
605 cfg.form_name = resp.data.form_name || create_payload.form_slug;
606
607 if ( typeof done_cb === 'function' ) {
608 done_cb( true, resp );
609 }
610 } )
611 .fail( function ( jqxhr ) {
612 wpbc_admin_show_message( 'WPBC BFB: create HTTP error', 'error', 10000 );
613 if ( typeof done_cb === 'function' ) {
614 done_cb( false, null );
615 }
616 console.error( 'WPBC BFB: create HTTP error', jqxhr && jqxhr.status, jqxhr && jqxhr.statusText );
617 } );
618 }
619
620 // Load ALL Forms.
621 function wpbc_bfb__ajax_list_user_forms( btn, args, done_cb ) {
622
623 var cfg = w.WPBC_BFB_Ajax || {};
624
625 if ( ! cfg.url || ! cfg.nonce_list ) {
626 wpbc_admin_show_message( 'WPBC BFB: list config is missing (url/nonce_list).', 'error', 10000 );
627 if ( typeof done_cb === 'function' ) {
628 done_cb( false, null );
629 }
630 return;
631 }
632
633 var page = ( args && args.page ) ? parseInt( args.page, 10 ) : 1;
634 var limit = ( args && ( args.limit || args.per_page ) ) ? parseInt( ( args.limit || args.per_page ), 10 ) : 20;
635
636 if ( ! page || page < 1 ) {
637 page = 1;
638 }
639 if ( ! limit || limit < 1 ) {
640 limit = 20;
641 }
642
643 var payload = {
644 action : 'WPBC_AJX_BFB_LIST_FORMS',
645 nonce : cfg.nonce_list,
646 include_global: ( args && args.include_global ) ? 1 : 0,
647 status : ( args && args.status ) ? String( args.status ) : 'published',
648 search : ( args && args.search ) ? String( args.search ) : '',
649 page : page,
650 limit : limit
651 };
652
653 var ajax_request = wpbc_bfb__ajax_post( cfg.url, payload );
654
655 ajax_request
656 .done( function ( response_text, _text_status, jqxhr ) {
657
658 if ( ! jqxhr || jqxhr.status !== 200 ) {
659 wpbc_admin_show_message( 'WPBC BFB: list HTTP error', 'error', 10000 );
660 if ( typeof done_cb === 'function' ) {
661 done_cb( false, null );
662 }
663 return;
664 }
665
666 var resp = wpbc_bfb__safe_json_parse( response_text );
667 if ( ! resp ) {
668 wpbc_admin_show_message( 'WPBC BFB: list JSON parse error', 'error', 10000 );
669 if ( typeof done_cb === 'function' ) {
670 done_cb( false, null );
671 }
672 return;
673 }
674
675 if ( ! resp || ! resp.success || ! resp.data ) {
676 wpbc_admin_show_message( 'WPBC BFB: list failed', 'error', 10000 );
677 if ( typeof done_cb === 'function' ) {
678 done_cb( false, resp );
679 }
680 return;
681 }
682
683 if ( typeof done_cb === 'function' ) {
684 done_cb( true, resp.data );
685 }
686 } )
687 .fail( function ( jqxhr ) {
688 wpbc_admin_show_message( 'WPBC BFB: list HTTP error', 'error', 10000 );
689 if ( typeof done_cb === 'function' ) {
690 done_cb( false, null );
691 }
692 console.error( 'WPBC BFB: list HTTP error', jqxhr && jqxhr.status, jqxhr && jqxhr.statusText );
693 } );
694 }
695
696 function wpbc_bfb__ajax_load_form_by_slug( form_slug, btn, done_cb ) {
697
698 var cfg = w.WPBC_BFB_Ajax || {};
699 var builder = w.wpbc_bfb || null;
700
701 form_slug = String( form_slug || '' ).trim();
702 if ( ! form_slug ) {
703 if ( typeof done_cb === 'function' ) {
704 done_cb( false, null );
705 }
706 return;
707 }
708
709 var $btn = btn ? $( btn ) : null;
710
711 // Busy START
712 if ( $btn && $btn.length ) {
713 if ( typeof w.wpbc_bfb__button_busy_start === 'function' ) {
714 w.wpbc_bfb__button_busy_start( $btn );
715 } else {
716 $btn.prop( 'disabled', true );
717 }
718 } else if ( btn ) {
719 btn.disabled = true;
720 }
721
722 var payload = {
723 action : 'WPBC_AJX_BFB_LOAD_FORM_CONFIG',
724 nonce : cfg.nonce_load || '',
725 form_name: form_slug
726 };
727
728 wpbc_admin_show_message_processing( '' );
729
730 var ajax_request = wpbc_bfb__ajax_post( cfg.url, payload );
731
732 ajax_request
733 .done( function ( response_text, _text_status, jqxhr ) {
734
735 if ( ! jqxhr || jqxhr.status !== 200 ) {
736 wpbc_admin_show_message( 'WPBC BFB: load HTTP error', 'error', 10000 );
737 if ( typeof done_cb === 'function' ) {
738 done_cb( false, null );
739 }
740 return;
741 }
742
743 var resp = wpbc_bfb__safe_json_parse( response_text );
744 if ( ! resp ) {
745 wpbc_admin_show_message( 'WPBC BFB: load JSON parse error', 'error', 10000 );
746 if ( typeof done_cb === 'function' ) {
747 done_cb( false, null );
748 }
749 return;
750 }
751
752 if ( ! resp || ! resp.success || ! resp.data ) {
753
754 if ( resp && resp.data && resp.data.message ) {
755 wpbc_admin_show_message( 'WPBC BFB: ' + resp.data.message, 'error', 10000 );
756 } else {
757 wpbc_admin_show_message( 'WPBC BFB: load failed', 'error', 10000 );
758 }
759
760 if ( typeof done_cb === 'function' ) {
761 done_cb( false, resp );
762 }
763 return;
764 }
765
766 var data = resp.data;
767 var structure = data.structure || [];
768
769 // IMPORTANT: switch "current form" after successful load
770 cfg.form_name = form_slug;
771
772 // Apply Advanced Mode texts if present.
773 if ( data && ( typeof data.advanced_form !== 'undefined' || typeof data.content_form !== 'undefined' ) ) {
774
775 var ams2 = '';
776 try {
777 var s2 = ( typeof data.settings === 'string' ) ? JSON.parse( data.settings ) : data.settings;
778 ams2 = ( s2 && s2.bfb_options && s2.bfb_options.advanced_mode_source ) ? s2.bfb_options.advanced_mode_source : '';
779 } catch ( _e2 ) {}
780
781 apply_advanced_mode_texts(
782 data.advanced_form || '',
783 data.content_form || '',
784 ams2
785 );
786 }
787
788 // Apply settings if present.
789 if ( data.settings ) {
790 var parsed_settings = null;
791 try {
792 parsed_settings = ( typeof data.settings === 'string' ) ? JSON.parse( data.settings ) : data.settings;
793 } catch ( _e3 ) {
794 parsed_settings = null;
795 }
796 if ( parsed_settings ) {
797 wpbc_bfb__dispatch_event_safe( 'wpbc:bfb:form_settings:apply', {
798 settings : parsed_settings,
799 form_name: cfg.form_name || 'standard'
800 } );
801 }
802 }
803
804 wpbc_bfb__dispatch_event_safe( 'wpbc:bfb:form:ajax_loaded', {
805 loaded_data: data,
806 form_name : cfg.form_name || 'standard'
807 } );
808
809 if ( typeof done_cb === 'function' ) {
810 try { done_cb( true, data ); } catch ( _e4 ) {}
811 }
812
813 if ( typeof w.wpbc_bfb__on_structure_loaded === 'function' ) {
814 w.wpbc_bfb__on_structure_loaded( structure );
815 wpbc_admin_show_message( 'Done', 'info', 1000, false );
816 } else if ( builder && typeof builder.load_structure === 'function' ) {
817 builder.load_structure( structure );
818 wpbc_admin_show_message( 'Done', 'info', 1000, false );
819 } else {
820 console.warn( 'WPBC BFB: no loader for structure', structure );
821 }
822 } )
823 .fail( function ( jqxhr ) {
824 wpbc_admin_show_message( 'WPBC BFB: load HTTP error', 'error', 10000 );
825 if ( typeof done_cb === 'function' ) {
826 done_cb( false, null );
827 }
828 console.error( 'WPBC BFB: load HTTP error', jqxhr && jqxhr.status, jqxhr && jqxhr.statusText );
829 } )
830 .always( function () {
831
832 // Busy END
833 if ( $btn && $btn.length && typeof w.wpbc_bfb__button_busy_end === 'function' ) {
834 w.wpbc_bfb__button_busy_end( $btn );
835 } else if ( $btn && $btn.length ) {
836 $btn.prop( 'disabled', false ).removeClass( 'wpbc-is-busy' );
837 } else if ( btn ) {
838 btn.disabled = false;
839 }
840 } );
841 }
842
843 function wpbc_bfb__ajax_delete_template_by_slug( form_slug, done_cb ) {
844
845 var cfg = w.WPBC_BFB_Ajax || {};
846 var delete_nonce = cfg.nonce_delete || cfg.nonce_list || '';
847
848 form_slug = String( form_slug || '' ).trim();
849
850 if ( ! form_slug ) {
851 if ( typeof done_cb === 'function' ) {
852 done_cb( false, { data: { message: 'Template key is required.' } } );
853 }
854 return;
855 }
856
857 if ( ! cfg.url || ! delete_nonce ) {
858 console.error( 'WPBC BFB: delete template config is missing (url/nonce).' );
859 if ( typeof done_cb === 'function' ) {
860 done_cb( false, { data: { message: 'WPBC BFB: delete template config is missing (url/nonce).' } } );
861 }
862 return;
863 }
864
865 var payload = {
866 action : 'WPBC_AJX_BFB_DELETE_TEMPLATE_CONFIG',
867 nonce : delete_nonce,
868 form_name: form_slug
869 };
870
871 var ajax_request = wpbc_bfb__ajax_post( cfg.url, payload );
872
873 ajax_request
874 .done( function ( response_text, _text_status, jqxhr ) {
875
876 if ( ! jqxhr || jqxhr.status !== 200 ) {
877 if ( typeof done_cb === 'function' ) {
878 done_cb( false, null );
879 }
880 return;
881 }
882
883 var resp = wpbc_bfb__safe_json_parse( response_text );
884 if ( ! resp ) {
885 if ( typeof done_cb === 'function' ) {
886 done_cb( false, null );
887 }
888 return;
889 }
890
891 if ( ! resp.success ) {
892 if ( typeof done_cb === 'function' ) {
893 done_cb( false, resp );
894 }
895 return;
896 }
897
898 if ( typeof done_cb === 'function' ) {
899 done_cb( true, resp );
900 }
901 } )
902 .fail( function ( jqxhr ) {
903 if ( typeof done_cb === 'function' ) {
904 done_cb( false, null );
905 }
906 console.error( 'WPBC BFB: delete template HTTP error', jqxhr && jqxhr.status, jqxhr && jqxhr.statusText );
907 } );
908 }
909
910
911 // Expose globals for buttons (onclick attributes).
912 w.wpbc_bfb__ajax_delete_template_by_slug = wpbc_bfb__ajax_delete_template_by_slug;
913 w.wpbc_bfb__ajax_save_current_form = wpbc_bfb__ajax_save_current_form;
914 w.wpbc_bfb__ajax_load_current_form = wpbc_bfb__ajax_load_current_form;
915 w.wpbc_bfb__ajax_create_form = wpbc_bfb__ajax_create_form;
916 w.wpbc_bfb__ajax_list_user_forms = wpbc_bfb__ajax_list_user_forms;
917 w.wpbc_bfb__ajax_load_form_by_slug = wpbc_bfb__ajax_load_form_by_slug;
918
919 })( window, document, window.jQuery );
920
921
922 // -- Ajax Helpers: ----------------------------------------------------------------------------------------------------
923 /**
924 * Common callback used by loaders that return structure JSON.
925 *
926 * @param val
927 */
928 function wpbc_bfb__on_structure_loaded( val ) {
929 try {
930 if ( typeof val === 'string' ) {
931 val = JSON.parse( val );
932 }
933 var builder = window.wpbc_bfb || null; // FIX: use window.*
934 if ( builder && typeof builder.load_saved_structure === 'function' ) {
935 builder.load_saved_structure( val || [] );
936 }
937 } catch ( e ) {
938 wpbc_admin_show_message( 'wpbc_bfb__on_structure_loaded error', 'error', 10000 );
939 console.error( 'wpbc_bfb__on_structure_loaded error', e );
940 }
941 }
942
943 function wpbc_bfb__get_ajax_url() {
944 if ( typeof ajaxurl !== 'undefined' && ajaxurl ) {
945 return ajaxurl;
946 }
947
948 var AjaxCfg = window.WPBC_BFB_Ajax || {}; // FIX: guard via window.*
949 if ( AjaxCfg.url ) {
950 return AjaxCfg.url;
951 }
952
953 wpbc_admin_show_message( 'WPBC BFB: ajax URL is missing.', 'error', 10000 );
954 console.error( 'WPBC BFB: ajax URL is missing.' );
955 return '';
956 }
957
958
959 // -- Shared helpers: button busy state --------------------------------------------------------------------------------
960 function wpbc_bfb__button_busy_start( $btn ) {
961
962 if ( ! $btn || ! $btn.length ) return;
963
964 var original_html = $btn.html();
965 var busy_text = $btn.data( 'wpbc-u-busy-text' ) || '';
966 var spinner_html = '<span class="wpbc_icn_rotate_right wpbc_spin wpbc_ajax_icon wpbc_processing wpbc_icn_autorenew" aria-hidden="true"></span>';
967
968 $btn
969 .data( 'wpbc-original-html', original_html )
970 .prop( 'disabled', true )
971 .addClass( 'wpbc-is-busy' );
972
973 if ( busy_text ) {
974 $btn.html( busy_text + ' ' + spinner_html );
975 } else {
976 $btn.append( spinner_html );
977 }
978 }
979
980 function wpbc_bfb__button_busy_end( $btn ) {
981 if ( ! $btn || ! $btn.length ) return;
982
983 var original = $btn.data( 'wpbc-original-html' );
984 if ( typeof original === 'string' ) {
985 $btn.html( original );
986 }
987 $btn
988 .prop( 'disabled', false )
989 .removeClass( 'wpbc-is-busy' )
990 .removeData( 'wpbc-original-html' );
991 }
992
993
994 // -- Import Helpers: --------------------------------------------------------------------------------------------------
995 /**
996 * Import from Simple Form — with busy state
997 *
998 * @param btn
999 */
1000 function wpbc_bfb__import_from_simple_form( btn ) {
1001 try {
1002 var $btn = jQuery( btn );
1003 var nonce = $btn.data( 'wpbc-bfb-import-nonce' );
1004 var action = $btn.data( 'wpbc-bfb-import-action' ) || 'wpbc_bfb_import_simple_form';
1005
1006 if ( ! nonce ) {
1007 wpbc_admin_show_message( 'WPBC BFB: missing import nonce.', 'error', 10000 );
1008 console.error( 'WPBC BFB: missing import nonce.' );
1009 return;
1010 }
1011
1012 var ajax_url = wpbc_bfb__get_ajax_url();
1013 if ( ! ajax_url ) {
1014 return;
1015 }
1016
1017 wpbc_bfb__button_busy_start( $btn );
1018
1019 wpbc_admin_show_message_processing( '' );
1020
1021 jQuery.post( ajax_url, {
1022 action: action,
1023 nonce : nonce
1024 } )
1025 .done( function ( resp ) {
1026 if ( resp && resp.success && resp.data && resp.data.structure ) {
1027 var builder = window.wpbc_bfb || null; // FIX: use window.*
1028 if ( builder && typeof builder.load_saved_structure === 'function' ) {
1029 builder.load_saved_structure( resp.data.structure || [] );
1030 wpbc_admin_show_message( 'Done', 'info', 1000, false );
1031 }
1032 } else {
1033 wpbc_admin_show_message( 'WPBC BFB: import error', 'error', 10000 );
1034 console.error( 'WPBC BFB: import error', resp );
1035 }
1036 } )
1037 .fail( function ( xhr ) {
1038 wpbc_admin_show_message( 'WPBC BFB: AJAX error', 'error', 10000 );
1039 console.error( 'WPBC BFB: AJAX error', xhr.status, xhr.statusText );
1040 } )
1041 .always( function () {
1042 wpbc_bfb__button_busy_end( $btn );
1043 } );
1044
1045 } catch ( e ) {
1046 wpbc_admin_show_message( 'WPBC BFB: import exception', 'error', 10000 );
1047 console.error( 'WPBC BFB: import exception', e );
1048 }
1049 }
1050
1051 /**
1052 * Import legacy booking forms into BFB storage.
1053 *
1054 * Supported modes:
1055 * - current_context
1056 * - all_global
1057 * - all_users
1058 *
1059 * @param btn
1060 */
1061 function wpbc_bfb__import_legacy_forms( btn ) {
1062 try {
1063 var $btn = jQuery( btn );
1064 var nonce = $btn.data( 'wpbc-bfb-import-nonce' );
1065 var action = $btn.data( 'wpbc-bfb-import-action' ) || 'WPBC_AJX_BFB_IMPORT_LEGACY_FORMS';
1066 var mode = $btn.data( 'wpbc-bfb-import-mode' ) || 'current_context';
1067
1068 var skip_if_exists = $btn.data( 'wpbc-bfb-skip-if-exists' ) || 'skip';
1069 var set_bfb_form_not_defined = $btn.data( 'wpbc-bfb-set-form-not-defined' ) || 'not_defined';
1070
1071 if ( ! nonce ) {
1072 wpbc_admin_show_message( 'WPBC BFB: missing legacy import nonce.', 'error', 10000 );
1073 console.error( 'WPBC BFB: missing legacy import nonce.' );
1074 return;
1075 }
1076
1077 var ajax_url = wpbc_bfb__get_ajax_url();
1078 if ( ! ajax_url ) {
1079 return;
1080 }
1081
1082 wpbc_bfb__button_busy_start( $btn );
1083 wpbc_admin_show_message_processing( '' );
1084
1085 jQuery.post( ajax_url, {
1086 action : action,
1087 nonce : nonce,
1088 mode : mode,
1089 skip_if_exists : skip_if_exists,
1090 set_bfb_form_not_defined: set_bfb_form_not_defined,
1091 } )
1092 .done( function ( resp ) {
1093
1094 if ( resp && resp.success && resp.data ) {
1095
1096 var msg = resp.data.message || 'Legacy forms import finished.';
1097 wpbc_admin_show_message( msg, 'success', 6000, false );
1098
1099 if ( resp.data.imported ) {
1100 // Reload current form, if imported some forms.
1101 wpbc_bfb__ajax_load_current_form( null );
1102 }
1103
1104 try {
1105 jQuery( document ).trigger( 'wpbc_bfb_legacy_forms_imported', [ resp.data ] );
1106 } catch ( _e ) {}
1107
1108 } else {
1109 wpbc_admin_show_message( 'WPBC BFB: legacy forms import error', 'error', 10000 );
1110 console.error( 'WPBC BFB: legacy forms import error', resp );
1111 }
1112 } )
1113 .fail( function ( xhr ) {
1114 wpbc_admin_show_message( 'WPBC BFB: legacy forms AJAX error', 'error', 10000 );
1115 console.error( 'WPBC BFB: legacy forms AJAX error', xhr.status, xhr.statusText );
1116 } )
1117 .always( function () {
1118 wpbc_bfb__button_busy_end( $btn );
1119 } );
1120
1121 } catch ( e ) {
1122 wpbc_admin_show_message( 'WPBC BFB: legacy forms import exception', 'error', 10000 );
1123 console.error( 'WPBC BFB: legacy forms import exception', e );
1124 }
1125 }