PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
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 / __js / client / front_end_messages / wpbc_fe_messages.js

wpbc_fe_messages.js in Booking Calendar 11.9, at includes/__js/client/front_end_messages/wpbc_fe_messages.js

557 lines 21.8 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * =====================================================================================================================
3 * includes/__js/front_end_messages/wpbc_fe_messages.js
4 * =====================================================================================================================
5 */
6
7 // ---------------------------------------------------------------------------------------------------------------------
8 // Show Messages at Front-Edn side
9 // ---------------------------------------------------------------------------------------------------------------------
10
11 /**
12 * Extract a safe plain-text message from a recognized JSON AJAX error response.
13 *
14 * Only documented message properties are accepted. Callers must still render the returned value in text mode so an
15 * upstream response cannot inject markup into the public booking page.
16 *
17 * @param {jqXHR|null} jq_xhr jQuery XHR object, when available.
18 * @return {string} Bounded server message, or an empty string when the response is not recognized.
19 */
20 function wpbc_front_end__get_safe_ajax_error_message( jq_xhr ) {
21 if (
22 ! jq_xhr
23 || ! jq_xhr.responseJSON
24 || 'object' !== typeof jq_xhr.responseJSON
25 || Array.isArray( jq_xhr.responseJSON )
26 ) {
27 return '';
28 }
29
30 var response_json = jq_xhr.responseJSON;
31 var message_candidates = [];
32
33 if ( 'string' === typeof response_json.message ) {
34 message_candidates.push( response_json.message );
35 }
36 if (
37 response_json.data
38 && 'object' === typeof response_json.data
39 && ! Array.isArray( response_json.data )
40 && 'string' === typeof response_json.data.message
41 ) {
42 message_candidates.push( response_json.data.message );
43 }
44
45 for ( var candidate_index = 0; candidate_index < message_candidates.length; candidate_index++ ) {
46 var server_message = message_candidates[ candidate_index ].trim();
47 if ( server_message ) {
48 return server_message.slice( 0, 1000 );
49 }
50 }
51
52 return '';
53 }
54
55 /**
56 * Build a public-safe AJAX failure message with optional HTTP 409 cache troubleshooting guidance.
57 *
58 * @param {jqXHR|null} jq_xhr jQuery XHR object, when available.
59 * @param {string} fallback_message Localized fallback shown when no safe server message exists.
60 * @param {string} http_conflict_cache_hint Optional localized cache guidance for HTTP 409 responses.
61 * @return {string} Plain-text message including the HTTP status when available.
62 */
63 function wpbc_front_end__get_ajax_error_message( jq_xhr, fallback_message, http_conflict_cache_hint ) {
64 var http_status = jq_xhr && jq_xhr.status ? parseInt( jq_xhr.status, 10 ) : 0;
65 var safe_server_message = wpbc_front_end__get_safe_ajax_error_message( jq_xhr );
66 var error_message = safe_server_message || String( fallback_message || '' ).trim();
67
68 if ( 409 === http_status && http_conflict_cache_hint ) {
69 error_message += ( error_message ? ' ' : '' ) + String( http_conflict_cache_hint ).trim();
70 }
71 if ( http_status ) {
72 error_message += ' (' + http_status + ')';
73 }
74
75 return error_message;
76 }
77
78 /**
79 * Log an unexpected AJAX response for browser-console diagnostics.
80 *
81 * The response is intentionally kept out of frontend notices because it can contain a complete HTML error or login page.
82 * Logging it here preserves the server payload and request context for support troubleshooting.
83 *
84 * @param {string} ajax_action WordPress AJAX action that produced the unexpected response.
85 * @param {*} response_data Raw response payload returned by the server.
86 * @param {jqXHR|null} jq_xhr jQuery XHR object, when available.
87 * @param {string} text_status jQuery request status or success state.
88 * @param {string|Error} error_thrown Error description or exception reported by jQuery.
89 * @return {void}
90 */
91 function wpbc_front_end__log_unexpected_ajax_response( ajax_action, response_data, jq_xhr, text_status, error_thrown ) {
92 if ( ! window.console || 'function' !== typeof window.console.error ) {
93 return;
94 }
95
96 try {
97 var content_type = '';
98 var response_text = 'string' === typeof response_data ? response_data : '';
99 var response_length = response_text.length;
100 var response_format = response_text ? 'text' : 'empty';
101 var http_status = jq_xhr && jq_xhr.status ? parseInt( jq_xhr.status, 10 ) : 0;
102 var diagnostic_action = String( ajax_action || 'AJAX' ).replace( /^WPBC_AJX_/, '' ).replace( /[^A-Za-z0-9]+/g, '-' ).toUpperCase();
103 var diagnostic_code = 'WPBC-' + diagnostic_action + '-' + ( http_status || 'NETWORK' );
104 var response_headers = {};
105 if ( jq_xhr && 'function' === typeof jq_xhr.getResponseHeader ) {
106 content_type = jq_xhr.getResponseHeader( 'content-type' ) || '';
107 response_headers = {
108 'server' : jq_xhr.getResponseHeader( 'server' ) || '',
109 'cf_ray' : jq_xhr.getResponseHeader( 'cf-ray' ) || '',
110 'x_cache' : jq_xhr.getResponseHeader( 'x-cache' ) || '',
111 'x_cache_status' : jq_xhr.getResponseHeader( 'x-cache-status' ) || '',
112 'x_litespeed_cache': jq_xhr.getResponseHeader( 'x-litespeed-cache' ) || '',
113 'x_request_id' : jq_xhr.getResponseHeader( 'x-request-id' ) || ''
114 };
115 }
116 if ( jq_xhr && jq_xhr.responseJSON && 'object' === typeof jq_xhr.responseJSON ) {
117 response_format = 'json';
118 try {
119 response_length = JSON.stringify( jq_xhr.responseJSON ).length;
120 } catch ( response_json_error ) {
121 response_length = 0;
122 }
123 } else if ( -1 !== content_type.toLowerCase().indexOf( 'html' ) || /^\s*(?:<!doctype|<html)/i.test( response_text ) ) {
124 response_format = 'html';
125 }
126
127 window.console.error( '[WPBC][AJAX-UNEXPECTED-RESPONSE] ' + ajax_action, {
128 'action' : ajax_action,
129 'diagnostic_code' : diagnostic_code,
130 'http_status' : http_status,
131 'http_status_text': jq_xhr && jq_xhr.statusText ? jq_xhr.statusText : '',
132 'text_status' : text_status || '',
133 'error' : error_thrown || '',
134 'content_type' : content_type,
135 'response_format' : response_format,
136 'response_length' : response_length,
137 'server_message' : wpbc_front_end__get_safe_ajax_error_message( jq_xhr ),
138 'response_headers': response_headers,
139 'response' : response_data
140 } );
141 } catch ( logging_error ) {
142 // Console diagnostics must never interrupt frontend error recovery.
143 }
144 }
145
146 /**
147 * Show message in content
148 *
149 * @param message Message HTML
150 * @param params = {
151 * 'type' : 'warning', // 'error' | 'warning' | 'info' | 'success'
152 * 'show_here' : {
153 * 'jq_node' : '', // any jQuery node definition
154 * 'where' : 'inside' // 'inside' | 'before' | 'after' | 'right' | 'left'
155 * },
156 * 'is_append': true, // Apply only if 'where' : 'inside'
157 * 'style' : 'text-align:left;', // styles, if needed
158 * 'css_class': '', // For example can be: 'wpbc_fe_message_alt'
159 * 'delay' : 0, // how many microsecond to show, if 0 then show forever
160 * 'if_visible_not_show': false // if true, then do not show message, if previos message was not hided (not apply if 'where' : 'inside' )
161 * };
162 * Examples:
163 * var html_id = wpbc_front_end__show_message( 'You can test days selection in calendar', {} );
164 *
165 * var notice_message_id = wpbc_front_end__show_message( _wpbc.get_message( 'message_check_required' ), { 'type': 'warning', 'delay': 10000, 'if_visible_not_show': true,
166 * 'show_here': {'where': 'right', 'jq_node': el,} } );
167 *
168 * wpbc_front_end__show_message( response_data[ 'ajx_data' ][ 'ajx_after_action_message' ].replace( /\n/g, "<br />" ),
169 * { 'type' : ( 'undefined' !== typeof( response_data[ 'ajx_data' ][ 'ajx_after_action_message_status' ] ) )
170 * ? response_data[ 'ajx_data' ][ 'ajx_after_action_message_status' ] : 'info',
171 * 'show_here': {'jq_node': jq_node, 'where': 'after'},
172 * 'css_class':'wpbc_fe_message_alt',
173 * 'delay' : 10000
174 * } );
175 *
176 *
177 * @returns string - HTML ID or 0 if not showing during this time.
178 */
179 function wpbc_front_end__show_message( message, params = {} ){
180
181 var params_default = {
182 'type' : 'warning', // 'error' | 'warning' | 'info' | 'success'
183 'show_here' : {
184 'jq_node' : '', // any jQuery node definition
185 'where' : 'inside' // 'inside' | 'before' | 'after' | 'right' | 'left'
186 },
187 'is_append': true, // Apply only if 'where' : 'inside'
188 'style' : 'text-align:left;', // styles, if needed
189 'css_class': '', // For example can be: 'wpbc_fe_message_alt'
190 'delay' : 0, // how many microsecond to show, if 0 then show forever
191 'if_visible_not_show': false, // if true, then do not show message, if previos message was not hided (not apply if 'where' : 'inside' )
192 'is_scroll': true, // is scroll to this element
193 'content_mode': 'html' // 'html' for trusted legacy content | 'text' for editable/user content
194 };
195 for ( var p_key in params ){
196 params_default[ p_key ] = params[ p_key ];
197 }
198 params = params_default;
199
200 if ( 'text' === params[ 'content_mode' ] ) {
201 message = jQuery( '<div>' ).text( null === message || 'undefined' === typeof message ? '' : String( message ) ).html().replace( /\n/g, '<br />' );
202 }
203
204 var unique_div_id = new Date();
205 unique_div_id = 'wpbc_notice_' + unique_div_id.getTime();
206
207 params['css_class'] += ' wpbc_fe_message';
208 if ( params['type'] == 'error' ){
209 params['css_class'] += ' wpbc_fe_message_error';
210 message = '<i class="menu_icon icon-1x wpbc_icn_report_gmailerrorred"></i>' + message;
211 }
212 if ( params['type'] == 'warning' ){
213 params['css_class'] += ' wpbc_fe_message_warning';
214 message = '<i class="menu_icon icon-1x wpbc_icn_warning"></i>' + message;
215 }
216 if ( params['type'] == 'info' ){
217 params['css_class'] += ' wpbc_fe_message_info';
218 }
219 if ( params['type'] == 'success' ){
220 params['css_class'] += ' wpbc_fe_message_success';
221 message = '<i class="menu_icon icon-1x wpbc_icn_done_outline"></i>' + message;
222 }
223
224 var scroll_to_element = '<div id="' + unique_div_id + '_scroll" style="display:none;"></div>';
225 message = '<div id="' + unique_div_id + '" class="wpbc_front_end__message ' + params['css_class'] + '" style="' + params[ 'style' ] + '">' + message + '</div>';
226
227
228 var jq_el_message = false;
229 var is_show_message = true;
230
231 if ( 'inside' === params[ 'show_here' ][ 'where' ] ){
232
233 if ( params[ 'is_append' ] ){
234 jQuery( params[ 'show_here' ][ 'jq_node' ] ).append( scroll_to_element );
235 jQuery( params[ 'show_here' ][ 'jq_node' ] ).append( message );
236 } else {
237 jQuery( params[ 'show_here' ][ 'jq_node' ] ).html( scroll_to_element + message );
238 }
239
240 } else if ( 'before' === params[ 'show_here' ][ 'where' ] ){
241
242 jq_el_message = jQuery( params[ 'show_here' ][ 'jq_node' ] ).siblings( '[id^="wpbc_notice_"]' );
243 if ( (params[ 'if_visible_not_show' ]) && (jq_el_message.is( ':visible' )) ){
244 is_show_message = false;
245 unique_div_id = jQuery( jq_el_message.get( 0 ) ).attr( 'id' );
246 }
247 if ( is_show_message ){
248 jQuery( params[ 'show_here' ][ 'jq_node' ] ).before( scroll_to_element );
249 jQuery( params[ 'show_here' ][ 'jq_node' ] ).before( message );
250 }
251
252 } else if ( 'after' === params[ 'show_here' ][ 'where' ] ){
253
254 jq_el_message = jQuery( params[ 'show_here' ][ 'jq_node' ] ).nextAll( '[id^="wpbc_notice_"]' );
255 if ( (params[ 'if_visible_not_show' ]) && (jq_el_message.is( ':visible' )) ){
256 is_show_message = false;
257 unique_div_id = jQuery( jq_el_message.get( 0 ) ).attr( 'id' );
258 }
259 if ( is_show_message ){
260 jQuery( params[ 'show_here' ][ 'jq_node' ] ).before( scroll_to_element ); // We need to set here before(for handy scroll)
261 jQuery( params[ 'show_here' ][ 'jq_node' ] ).after( message );
262 }
263
264 } else if ( 'right' === params[ 'show_here' ][ 'where' ] ){
265
266 jq_el_message = jQuery( params[ 'show_here' ][ 'jq_node' ] ).nextAll( '.wpbc_front_end__message_container_right' ).find( '[id^="wpbc_notice_"]' );
267 if ( (params[ 'if_visible_not_show' ]) && (jq_el_message.is( ':visible' )) ){
268 is_show_message = false;
269 unique_div_id = jQuery( jq_el_message.get( 0 ) ).attr( 'id' );
270 }
271 if ( is_show_message ){
272 jQuery( params[ 'show_here' ][ 'jq_node' ] ).before( scroll_to_element ); // We need to set here before(for handy scroll)
273 jQuery( params[ 'show_here' ][ 'jq_node' ] ).after( '<div class="wpbc_front_end__message_container_right">' + message + '</div>' );
274 }
275 } else if ( 'left' === params[ 'show_here' ][ 'where' ] ){
276
277 jq_el_message = jQuery( params[ 'show_here' ][ 'jq_node' ] ).siblings( '.wpbc_front_end__message_container_left' ).find( '[id^="wpbc_notice_"]' );
278 if ( (params[ 'if_visible_not_show' ]) && (jq_el_message.is( ':visible' )) ){
279 is_show_message = false;
280 unique_div_id = jQuery( jq_el_message.get( 0 ) ).attr( 'id' );
281 }
282 if ( is_show_message ){
283 jQuery( params[ 'show_here' ][ 'jq_node' ] ).before( scroll_to_element ); // We need to set here before(for handy scroll)
284 jQuery( params[ 'show_here' ][ 'jq_node' ] ).before( '<div class="wpbc_front_end__message_container_left">' + message + '</div>' );
285 }
286 }
287
288 if ( ( is_show_message ) && ( parseInt( params[ 'delay' ] ) > 0 ) ){
289 var closed_timer = setTimeout( function (){
290 jQuery( '#' + unique_div_id ).fadeOut( 1500 );
291 } , parseInt( params[ 'delay' ] ) );
292
293 var closed_timer2 = setTimeout( function (){
294 jQuery( '#' + unique_div_id ).trigger( 'hide' );
295 }, ( parseInt( params[ 'delay' ] ) + 1501 ) );
296 }
297
298 // Check if showed message in some hidden parent section and show it. But it must be lower than '.wpbc_container'
299 var parent_els = jQuery( '#' + unique_div_id ).parents().map( function (){
300 if ( (!jQuery( this ).is( 'visible' )) && (jQuery( '.wpbc_container' ).has( this )) ){
301 jQuery( this ).show();
302 }
303 } );
304
305 if ( params[ 'is_scroll' ] ){
306 wpbc_do_scroll( '#' + unique_div_id + '_scroll' );
307 }
308
309 return unique_div_id;
310 }
311
312
313 /**
314 * Error message. Preset of parameters for real message function.
315 *
316 * @param el - any jQuery node definition
317 * @param message - Message HTML
318 * @returns string - HTML ID or 0 if not showing during this time.
319 */
320 function wpbc_front_end__show_message__error( jq_node, message, content_mode ){
321
322 content_mode = ( 'undefined' === typeof content_mode ) ? 'html' : content_mode;
323
324 var notice_message_id = wpbc_front_end__show_message(
325 message,
326 {
327 'type' : 'error',
328 'delay' : 10000,
329 'if_visible_not_show': true,
330 'content_mode' : content_mode,
331 'show_here' : {
332 'where' : 'right',
333 'jq_node': jq_node
334 }
335 }
336 );
337 return notice_message_id;
338 }
339
340
341 /**
342 * Error message UNDER element. Preset of parameters for real message function.
343 *
344 * @param el - any jQuery node definition
345 * @param message - Message HTML
346 * @returns string - HTML ID or 0 if not showing during this time.
347 */
348 function wpbc_front_end__show_message__error_under_element( jq_node, message, message_delay, content_mode ){
349
350 if ( 'undefined' === typeof (message_delay) ){
351 message_delay = 0
352 }
353 content_mode = ( 'undefined' === typeof content_mode ) ? 'html' : content_mode;
354
355 var notice_message_id = wpbc_front_end__show_message(
356 message,
357 {
358 'type' : 'error',
359 'delay' : message_delay,
360 'if_visible_not_show': true,
361 'content_mode' : content_mode,
362 'show_here' : {
363 'where' : 'after',
364 'jq_node': jq_node
365 }
366 }
367 );
368 return notice_message_id;
369 }
370
371
372 /**
373 * Error message UNDER element. Preset of parameters for real message function.
374 *
375 * @param el - any jQuery node definition
376 * @param message - Message HTML
377 * @returns string - HTML ID or 0 if not showing during this time.
378 */
379 function wpbc_front_end__show_message__error_above_element( jq_node, message, message_delay, content_mode ){
380
381 if ( 'undefined' === typeof (message_delay) ){
382 message_delay = 10000
383 }
384 content_mode = ( 'undefined' === typeof content_mode ) ? 'html' : content_mode;
385
386 var notice_message_id = wpbc_front_end__show_message(
387 message,
388 {
389 'type' : 'error',
390 'delay' : message_delay,
391 'if_visible_not_show': true,
392 'content_mode' : content_mode,
393 'show_here' : {
394 'where' : 'before',
395 'jq_node': jq_node
396 }
397 }
398 );
399 return notice_message_id;
400 }
401
402
403 /**
404 * Warning message. Preset of parameters for real message function.
405 *
406 * @param el - any jQuery node definition
407 * @param message - Message HTML
408 * @returns string - HTML ID or 0 if not showing during this time.
409 */
410 function wpbc_front_end__show_message__warning( jq_node, message, content_mode ){
411
412 content_mode = ( 'undefined' === typeof content_mode ) ? 'html' : content_mode;
413
414 var notice_message_id = wpbc_front_end__show_message(
415 message,
416 {
417 'type' : 'warning',
418 'delay' : 10000,
419 'if_visible_not_show': true,
420 'content_mode' : content_mode,
421 'show_here' : {
422 'where' : 'right',
423 'jq_node': jq_node
424 }
425 }
426 );
427 wpbc_highlight_error_on_form_field( jq_node );
428 return notice_message_id;
429 }
430
431
432 /**
433 * Warning message UNDER element. Preset of parameters for real message function.
434 *
435 * @param el - any jQuery node definition
436 * @param message - Message HTML
437 * @returns string - HTML ID or 0 if not showing during this time.
438 */
439 function wpbc_front_end__show_message__warning_under_element( jq_node, message, content_mode ){
440
441 content_mode = ( 'undefined' === typeof content_mode ) ? 'html' : content_mode;
442
443 var notice_message_id = wpbc_front_end__show_message(
444 message,
445 {
446 'type' : 'warning',
447 'delay' : 10000,
448 'if_visible_not_show': true,
449 'content_mode' : content_mode,
450 'show_here' : {
451 'where' : 'after',
452 'jq_node': jq_node
453 }
454 }
455 );
456 return notice_message_id;
457 }
458
459
460 /**
461 * Warning message ABOVE element. Preset of parameters for real message function.
462 *
463 * @param el - any jQuery node definition
464 * @param message - Message HTML
465 * @returns string - HTML ID or 0 if not showing during this time.
466 */
467 function wpbc_front_end__show_message__warning_above_element( jq_node, message, content_mode ){
468
469 content_mode = ( 'undefined' === typeof content_mode ) ? 'html' : content_mode;
470
471 var notice_message_id = wpbc_front_end__show_message(
472 message,
473 {
474 'type' : 'warning',
475 'delay' : 10000,
476 'if_visible_not_show': true,
477 'content_mode' : content_mode,
478 'show_here' : {
479 'where' : 'before',
480 'jq_node': jq_node
481 }
482 }
483 );
484 return notice_message_id;
485 }
486
487 /**
488 * Highlight Error in specific field
489 *
490 * @param jq_node string or jQuery element, where scroll to
491 */
492 function wpbc_highlight_error_on_form_field( jq_node ){
493
494 if ( !jQuery( jq_node ).length ){
495 return;
496 }
497 if ( ! jQuery( jq_node ).is( ':input' ) ){
498 // Situation with checkboxes or radio buttons
499 var jq_node_arr = jQuery( jq_node ).find( ':input' );
500 if ( !jq_node_arr.length ){
501 return
502 }
503 jq_node = jq_node_arr.get( 0 );
504 }
505 var params = {};
506 params[ 'delay' ] = 10000;
507
508 if ( !jQuery( jq_node ).hasClass( 'wpbc_form_field_error' ) ){
509
510 jQuery( jq_node ).addClass( 'wpbc_form_field_error' )
511
512 if ( parseInt( params[ 'delay' ] ) > 0 ){
513 var closed_timer = setTimeout( function (){
514 jQuery( jq_node ).removeClass( 'wpbc_form_field_error' );
515 }
516 , parseInt( params[ 'delay' ] )
517 );
518
519 }
520 }
521 }
522
523 /**
524 * Scroll to specific element
525 *
526 * @param jq_node string or jQuery element, where scroll to
527 * @param extra_shift_offset int shift offset from jq_node
528 */
529 function wpbc_do_scroll( jq_node , extra_shift_offset = 0 ){
530
531 if ( !jQuery( jq_node ).length ){
532 return;
533 }
534 var targetOffset = jQuery( jq_node ).offset().top;
535
536 if ( targetOffset <= 0 ){
537 if ( 0 != jQuery( jq_node ).nextAll( ':visible' ).length ){
538 targetOffset = jQuery( jq_node ).nextAll( ':visible' ).first().offset().top;
539 } else if ( 0 != jQuery( jq_node ).parent().nextAll( ':visible' ).length ){
540 targetOffset = jQuery( jq_node ).parent().nextAll( ':visible' ).first().offset().top;
541 }
542 }
543
544 if ( jQuery( '#wpadminbar' ).length > 0 ){
545 targetOffset = targetOffset - 50 - 50;
546 } else {
547 targetOffset = targetOffset - 20 - 50;
548 }
549 targetOffset += extra_shift_offset;
550
551 // Scroll only if we did not scroll before
552 if ( ! jQuery( 'html,body' ).is( ':animated' ) ){
553 jQuery( 'html,body' ).animate( {scrollTop: targetOffset}, 500 );
554 }
555 }
556
557