PluginProbe
Booking Calendar / 11.8.4
Booking Calendar v11.8.4
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 10.11 All 204 releases
booking / includes / __js / client / cal / wpbc_cal.js

wpbc_cal.js in Booking Calendar 11.8.4, at includes/__js/client/cal/wpbc_cal.js

2,621 lines 99.7 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 /**
2 * =====================================================================================================================
3 * includes/__js/cal/wpbc_cal.js
4 * =====================================================================================================================
5 */
6
7 /**
8 * Order or child booking resources saved here: _wpbc.booking__get_param_value( resource_id,
9 * 'resources_id_arr__in_dates' ) [2,10,12,11]
10 */
11
12 /**
13 * How to check booked times on specific date: ?
14 *
15 _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21');
16
17 console.log(
18 _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[2].booked_time_slots.merged_seconds,
19 _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[10].booked_time_slots.merged_seconds,
20 _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[11].booked_time_slots.merged_seconds,
21 _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[12].booked_time_slots.merged_seconds
22 );
23 * OR
24 console.log(
25 _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[2].booked_time_slots.merged_readable,
26 _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[10].booked_time_slots.merged_readable,
27 _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[11].booked_time_slots.merged_readable,
28 _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[12].booked_time_slots.merged_readable
29 );
30 *
31 */
32
33 /**
34 * Days selection:
35 * wpbc_calendar__unselect_all_dates( resource_id );
36 *
37 * var resource_id = 1;
38 * Example 1: var num_selected_days = wpbc_auto_select_dates_in_calendar( resource_id, '2024-05-15',
39 * '2024-05-25' ); Example 2: var num_selected_days = wpbc_auto_select_dates_in_calendar( resource_id,
40 * ['2024-05-09','2024-05-19','2024-05-25'] );
41 *
42 */
43
44
45 /**
46 * C A L E N D A R ---------------------------------------------------------------------------------------------------
47 */
48
49
50 /**
51 * Show WPBC Calendar
52 *
53 * @param resource_id - resource ID
54 * @returns {boolean}
55 */
56 function wpbc_calendar_show( resource_id ){
57
58 // If no calendar HTML tag, then exit
59 if ( 0 === jQuery( '#calendar_booking' + resource_id ).length ){ return false; }
60
61 // If the calendar with the same Booking resource is activated already, then exit. But in Elementor the class can be stale, so verify instance.
62 if ( jQuery( '#calendar_booking' + resource_id ).hasClass( 'hasDatepick' ) ) {
63
64 var existing_inst = null;
65
66 try {
67 existing_inst = jQuery.datepick._getInst( jQuery( '#calendar_booking' + resource_id ).get( 0 ) );
68 } catch ( e ) {
69 existing_inst = null;
70 }
71
72 if ( existing_inst ) {
73 return false;
74 }
75
76 // Stale marker: remove and continue with init.
77 jQuery( '#calendar_booking' + resource_id ).removeClass( 'hasDatepick' );
78 }
79
80 wpbc_range_selection__prepare_guidance( resource_id );
81
82
83
84 // -----------------------------------------------------------------------------------------------------------------
85 // Days selection
86 // -----------------------------------------------------------------------------------------------------------------
87 var local__is_range_select = false;
88 var local__multi_days_select_num = 365; // multiple | fixed
89 if ( 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ){
90 local__is_range_select = true;
91 local__multi_days_select_num = 0;
92 }
93 if ( 'single' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ){
94 local__multi_days_select_num = 0;
95 }
96
97 // -----------------------------------------------------------------------------------------------------------------
98 // Min - Max days to scroll/show
99 // -----------------------------------------------------------------------------------------------------------------
100 var local__min_date = 0;
101 local__min_date = new Date( _wpbc.get_other_param( 'today_arr' )[ 0 ], (parseInt( _wpbc.get_other_param( 'today_arr' )[ 1 ] ) - 1), _wpbc.get_other_param( 'today_arr' )[ 2 ], 0, 0, 0 ); // FixIn: 9.9.0.17.
102 //console.log( local__min_date );
103 var local__max_date = _wpbc.calendar__get_param_value( resource_id, 'booking_max_monthes_in_calendar' );
104 //local__max_date = new Date(2024, 5, 28); It is here issue of not selectable dates, but some dates showing in calendar as available, but we can not select it.
105
106 //// Define last day in calendar (as a last day of month (and not date, which is related to actual 'Today' date).
107 //// E.g. if today is 2023-09-25, and we set 'Number of months to scroll' as 5 months, then last day will be 2024-02-29 and not the 2024-02-25.
108 // var cal_last_day_in_month = jQuery.datepick._determineDate( null, local__max_date, new Date() );
109 // cal_last_day_in_month = new Date( cal_last_day_in_month.getFullYear(), cal_last_day_in_month.getMonth() + 1, 0 );
110 // local__max_date = cal_last_day_in_month; // FixIn: 10.0.0.26.
111
112 // Get start / end dates from the Booking Calendar shortcode. Example: [booking calendar_dates_start='2026-01-01' calendar_dates_end='2026-12-31' resource_id=1] // FixIn: 10.13.1.4.
113 if ( false !== wpbc_calendar__get_dates_start( resource_id ) ) {
114 local__min_date = wpbc_calendar__get_dates_start( resource_id ); // E.g. - local__min_date = new Date( 2025, 0, 1 );
115 }
116 if ( false !== wpbc_calendar__get_dates_end( resource_id ) ) {
117 local__max_date = wpbc_calendar__get_dates_end( resource_id ); // E.g. - local__max_date = new Date( 2025, 11, 31 );
118 }
119
120 // In case we edit booking in past or have specific parameter in URL.
121 var wpbc_edit_booking_hash = _wpbc.get_other_param( 'this_page_booking_hash' );
122 var wpbc_is_edit_booking_context = ( 'undefined' !== typeof wpbc_edit_booking_hash ) && ( '' !== wpbc_edit_booking_hash );
123 var wpbc_allow_past_context = _wpbc.get_other_param( 'this_page_allow_past' );
124 var wpbc_is_allow_past_context = ( '1' === String( wpbc_allow_past_context ) ) || ( 1 === wpbc_allow_past_context ) || ( true === wpbc_allow_past_context );
125 var wpbc_allow_past_date_arr = _wpbc.get_other_param( 'this_page_allow_past_arr' );
126 var wpbc_is_add_booking_admin_page = ( location.href.indexOf( 'page=wpbc' ) != -1 ) && ( location.href.indexOf( 'tab=add-booking' ) != -1 );
127 if ( ( wpbc_is_add_booking_admin_page || wpbc_is_edit_booking_context || wpbc_is_allow_past_context )
128 && (
129 wpbc_is_edit_booking_context
130 || wpbc_is_allow_past_context
131 || ( location.href.indexOf('booking_hash') != -1 ) // Comment this line for ability to add booking in past days at Booking > Add booking page.
132 || ( location.href.indexOf('allow_past') != -1 ) // FixIn: 10.7.1.2.
133 )
134 ){
135 // local__min_date = null;
136 // FixIn: 10.14.1.4.
137 var wpbc_min_date_arr = ( wpbc_is_allow_past_context && wpbc_allow_past_date_arr && ( 5 <= wpbc_allow_past_date_arr.length ) ) ? wpbc_allow_past_date_arr : _wpbc.get_other_param( 'time_local_arr' );
138 local__min_date = new Date( wpbc_min_date_arr[0], ( parseInt( wpbc_min_date_arr[1] ) - 1), wpbc_min_date_arr[2], wpbc_min_date_arr[3], wpbc_min_date_arr[4], 0 );
139 local__max_date = null;
140 }
141
142 var local__start_weekday = _wpbc.calendar__get_param_value( resource_id, 'booking_start_day_weeek' );
143 var local__number_of_months = parseInt( _wpbc.calendar__get_param_value( resource_id, 'calendar_number_of_months' ) );
144
145 jQuery( '#calendar_booking' + resource_id ).text( '' ); // Remove all HTML in calendar tag
146 // -----------------------------------------------------------------------------------------------------------------
147 // Show calendar
148 // -----------------------------------------------------------------------------------------------------------------
149 jQuery('#calendar_booking'+ resource_id).datepick(
150 {
151 beforeShowDay: function ( js_date ){
152 return wpbc__calendar__apply_css_to_days( js_date, {'resource_id': resource_id}, this );
153 },
154 onSelect: function ( string_dates, js_dates_arr ){ /**
155 * string_dates = '23.08.2023 - 26.08.2023'
156 * | '23.08.2023 - 23.08.2023' |
157 * '19.09.2023, 24.08.2023, 30.09.2023'
158 * js_dates_arr = range: [ Date (Aug 23 2023),
159 * Date (Aug 25 2023)] | multiple: [
160 * Date(Oct 24 2023), Date(Oct 20 2023), Date(Oct
161 * 16 2023) ]
162 */
163 return wpbc__calendar__on_select_days( string_dates, {'resource_id': resource_id}, this );
164 },
165 onHover: function ( string_date, js_date ){
166 return wpbc__calendar__on_hover_days( string_date, js_date, {'resource_id': resource_id}, this );
167 },
168 onChangeMonthYear: function ( year, real_month, js_date__1st_day_in_month ){ },
169 showOn : 'both',
170 numberOfMonths: local__number_of_months,
171 stepMonths : 1,
172 // prevText : '&laquo;',
173 // nextText : '&raquo;',
174 prevText : '&lsaquo;',
175 nextText : '&rsaquo;',
176 dateFormat : 'dd.mm.yy',
177 changeMonth : false,
178 changeYear : false,
179 minDate : local__min_date,
180 maxDate : local__max_date, // '1Y',
181 // minDate: new Date(2020, 2, 1), maxDate: new Date(2020, 9, 31), // Ability to set any start and end date in calendar
182 showStatus : false,
183 multiSeparator : ', ',
184 closeAtTop : false,
185 firstDay : local__start_weekday,
186 gotoCurrent : false,
187 hideIfNoPrevNext: true,
188 multiSelect : local__multi_days_select_num,
189 rangeSelect : local__is_range_select,
190 // showWeeks: true,
191 useThemeRoller: false
192 }
193 );
194
195
196
197 // -----------------------------------------------------------------------------------------------------------------
198 // Clear today date highlighting
199 // -----------------------------------------------------------------------------------------------------------------
200 setTimeout( function (){ wpbc_calendars__clear_days_highlighting( resource_id ); }, 500 ); // FixIn: 7.1.2.8.
201
202 // -----------------------------------------------------------------------------------------------------------------
203 // Scroll calendar to specific month
204 // -----------------------------------------------------------------------------------------------------------------
205 var start_bk_month = _wpbc.calendar__get_param_value( resource_id, 'calendar_scroll_to' );
206 if ( false !== start_bk_month ){
207 wpbc_calendar__scroll_to( resource_id, start_bk_month[ 0 ], start_bk_month[ 1 ] );
208 }
209 }
210
211
212 /**
213 * Get booking statuses as array from the structured summary field, with fallback to the legacy string field.
214 *
215 * @param date_bookings_obj
216 * @returns {[]}
217 */
218 function wpbc_get_booking_statuses__as_arr( date_bookings_obj ){
219
220 if (
221 ( ! date_bookings_obj )
222 || ( 'undefined' === typeof (date_bookings_obj[ 'summary' ]) )
223 ){
224 return [];
225 }
226
227 if ( Array.isArray( date_bookings_obj[ 'summary' ][ 'status_for_bookings_arr' ] ) ){
228 return date_bookings_obj[ 'summary' ][ 'status_for_bookings_arr' ].filter( function ( booking_status ){
229 return '' !== booking_status;
230 } );
231 }
232
233 if ( ! date_bookings_obj[ 'summary' ][ 'status_for_bookings' ] ){
234 return [];
235 }
236
237 return date_bookings_obj[ 'summary' ][ 'status_for_bookings' ].toString().trim().split( /\s+/ ).filter( function ( booking_status ){
238 return '' !== booking_status;
239 } );
240 }
241
242
243 /**
244 * Check exact booking status in statuses array.
245 *
246 * @param {[]} booking_statuses_arr
247 * @param {string} booking_status
248 * @returns {boolean}
249 */
250 function wpbc_booking_statuses__has( booking_statuses_arr, booking_status ){
251 return booking_statuses_arr.indexOf( booking_status ) > -1;
252 }
253
254
255 /**
256 * Check booking status part, e.g. "pending" in "approved_pending".
257 *
258 * @param {[]} booking_statuses_arr
259 * @param {string} booking_status_part
260 * @returns {boolean}
261 */
262 function wpbc_booking_statuses__has_part( booking_statuses_arr, booking_status_part ){
263
264 for ( var i = 0; i < booking_statuses_arr.length; i++ ){
265 if ( booking_statuses_arr[ i ].split( '_' ).indexOf( booking_status_part ) > -1 ){
266 return true;
267 }
268 }
269
270 return false;
271 }
272
273
274 /**
275 * Apply CSS to calendar date cells
276 *
277 * @param date - JavaScript Date Obj: Mon Dec 11 2023 00:00:00
278 * GMT+0200 (Eastern European Standard Time)
279 * @param calendar_params_arr - Calendar Settings Object: {
280 * "resource_id": 4
281 * }
282 * @param datepick_this - this of datepick Obj
283 * @returns {(*|string)[]|(boolean|string)[]} - [ {true -available | false - unavailable}, 'CSS classes for
284 * calendar day cell' ]
285 */
286 function wpbc__calendar__apply_css_to_days( date, calendar_params_arr, datepick_this ){
287
288 var today_date = new Date( _wpbc.get_other_param( 'today_arr' )[ 0 ], (parseInt( _wpbc.get_other_param( 'today_arr' )[ 1 ] ) - 1), _wpbc.get_other_param( 'today_arr' )[ 2 ], 0, 0, 0 ); // Today JS_Date_Obj.
289 var class_day = wpbc__get__td_class_date( date ); // '1-9-2023'
290 var sql_class_day = wpbc__get__sql_class_date( date ); // '2023-01-09'
291 var resource_id = ( 'undefined' !== typeof(calendar_params_arr[ 'resource_id' ]) ) ? calendar_params_arr[ 'resource_id' ] : '1'; // '1'
292
293 // Get Selected dates in calendar
294 var selected_dates_sql = wpbc_get__selected_dates_sql__as_arr( resource_id );
295
296 // Get Data --------------------------------------------------------------------------------------------------------
297 var date_bookings_obj = _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_class_day );
298
299
300 // Array with CSS classes for date ---------------------------------------------------------------------------------
301 var css_classes__for_date = [];
302 css_classes__for_date.push( 'sql_date_' + sql_class_day ); // 'sql_date_2023-07-21'
303 css_classes__for_date.push( 'cal4date-' + class_day ); // 'cal4date-7-21-2023'
304 css_classes__for_date.push( 'wpbc_weekday_' + date.getDay() ); // 'wpbc_weekday_4'
305
306 // Define Selected Check In/Out dates in TD -----------------------------------------------------------------------
307 if (
308 ( selected_dates_sql.length )
309 //&& ( selected_dates_sql[ 0 ] !== selected_dates_sql[ (selected_dates_sql.length - 1) ] )
310 ){
311 if ( sql_class_day === selected_dates_sql[ 0 ] ){
312 css_classes__for_date.push( 'selected_check_in' );
313 css_classes__for_date.push( 'selected_check_in_out' );
314 }
315 if ( ( selected_dates_sql.length > 1 ) && ( sql_class_day === selected_dates_sql[ (selected_dates_sql.length - 1) ] ) ) {
316 css_classes__for_date.push( 'selected_check_out' );
317 css_classes__for_date.push( 'selected_check_in_out' );
318 }
319 }
320
321
322 var is_day_selectable = false;
323
324 // If something not defined, then this date closed --------------------------------------------------------------- // FixIn: 10.12.4.6.
325 if ( (false === date_bookings_obj) || ('undefined' === typeof (date_bookings_obj[resource_id])) ) {
326
327 css_classes__for_date.push( 'date_user_unavailable' );
328
329 return [ is_day_selectable, css_classes__for_date.join(' ') ];
330 }
331
332
333 // -----------------------------------------------------------------------------------------------------------------
334 // date_bookings_obj - Defined. Dates can be selectable.
335 // -----------------------------------------------------------------------------------------------------------------
336 var booking_statuses_arr = wpbc_get_booking_statuses__as_arr( date_bookings_obj );
337
338 // -----------------------------------------------------------------------------------------------------------------
339 // Add season names to the day CSS classes -- it is required for correct work of conditional fields --------------
340 var season_names_arr = _wpbc.seasons__get_for_date( resource_id, sql_class_day );
341
342 for ( var season_key in season_names_arr ){
343
344 css_classes__for_date.push( season_names_arr[ season_key ] ); // 'wpdevbk_season_september_2023'
345 }
346 // -----------------------------------------------------------------------------------------------------------------
347
348
349 // Cost Rate -------------------------------------------------------------------------------------------------------
350 css_classes__for_date.push( 'rate_' + date_bookings_obj[ resource_id ][ 'date_cost_rate' ].toString().replace( /[\.\s]/g, '_' ) ); // 'rate_99_00' -> 99.00
351
352
353 if ( parseInt( date_bookings_obj[ 'day_availability' ] ) > 0 ){
354 is_day_selectable = true;
355 css_classes__for_date.push( 'date_available' );
356 css_classes__for_date.push( 'reserved_days_count' + parseInt( date_bookings_obj[ 'max_capacity' ] - date_bookings_obj[ 'day_availability' ] ) );
357 } else {
358 is_day_selectable = false;
359 css_classes__for_date.push( 'date_user_unavailable' );
360 }
361
362
363 switch ( date_bookings_obj[ 'summary']['status_for_day' ] ){
364
365 case 'available':
366 break;
367
368 case 'time_slots_booking':
369 css_classes__for_date.push( 'timespartly', 'times_clock' );
370 break;
371
372 case 'full_day_booking':
373 css_classes__for_date.push( 'full_day_booking' );
374 break;
375
376 case 'season_filter':
377 css_classes__for_date.push( 'date_user_unavailable', 'season_unavailable' );
378 date_bookings_obj[ 'summary']['status_for_bookings' ] = ''; // Reset booking status color for possible old bookings on this date
379 date_bookings_obj[ 'summary']['status_for_bookings_arr' ] = [];
380 booking_statuses_arr = [];
381 break;
382
383 case 'resource_availability':
384 css_classes__for_date.push( 'date_user_unavailable', 'resource_unavailable' );
385 date_bookings_obj[ 'summary']['status_for_bookings' ] = ''; // Reset booking status color for possible old bookings on this date
386 date_bookings_obj[ 'summary']['status_for_bookings_arr' ] = [];
387 booking_statuses_arr = [];
388 break;
389
390 case 'weekday_unavailable':
391 css_classes__for_date.push( 'date_user_unavailable', 'weekday_unavailable' );
392 date_bookings_obj[ 'summary']['status_for_bookings' ] = ''; // Reset booking status color for possible old bookings on this date
393 date_bookings_obj[ 'summary']['status_for_bookings_arr' ] = [];
394 booking_statuses_arr = [];
395 break;
396
397 case 'from_today_unavailable':
398 css_classes__for_date.push( 'date_user_unavailable', 'from_today_unavailable' );
399 date_bookings_obj[ 'summary']['status_for_bookings' ] = ''; // Reset booking status color for possible old bookings on this date
400 date_bookings_obj[ 'summary']['status_for_bookings_arr' ] = [];
401 booking_statuses_arr = [];
402 break;
403
404 case 'limit_available_from_today':
405 css_classes__for_date.push( 'date_user_unavailable', 'limit_available_from_today' );
406 date_bookings_obj[ 'summary']['status_for_bookings' ] = ''; // Reset booking status color for possible old bookings on this date
407 date_bookings_obj[ 'summary']['status_for_bookings_arr' ] = [];
408 booking_statuses_arr = [];
409 break;
410
411 case 'change_over':
412 /*
413 *
414 // check_out_time_date2approve check_in_time_date2approve
415 // check_out_time_date2approve check_in_time_date_approved
416 // check_in_time_date2approve check_out_time_date_approved
417 // check_out_time_date_approved check_in_time_date_approved
418 */
419
420 css_classes__for_date.push( 'timespartly', 'check_in_time', 'check_out_time' );
421 // FixIn: 10.0.0.2.
422 if ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved_pending' ) ){
423 css_classes__for_date.push( 'check_out_time_date_approved', 'check_in_time_date2approve' );
424 }
425 if ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_approved' ) ){
426 css_classes__for_date.push( 'check_out_time_date2approve', 'check_in_time_date_approved' );
427 }
428 break;
429
430 case 'check_in':
431 css_classes__for_date.push( 'timespartly', 'check_in_time' );
432
433 // FixIn: 9.9.0.33.
434 if ( wpbc_booking_statuses__has_part( booking_statuses_arr, 'pending' ) ){
435 css_classes__for_date.push( 'check_in_time_date2approve' );
436 } else if ( wpbc_booking_statuses__has_part( booking_statuses_arr, 'approved' ) ){
437 css_classes__for_date.push( 'check_in_time_date_approved' );
438 }
439 break;
440
441 case 'check_out':
442 css_classes__for_date.push( 'timespartly', 'check_out_time' );
443
444 // FixIn: 9.9.0.33.
445 if ( wpbc_booking_statuses__has_part( booking_statuses_arr, 'pending' ) ){
446 css_classes__for_date.push( 'check_out_time_date2approve' );
447 } else if ( wpbc_booking_statuses__has_part( booking_statuses_arr, 'approved' ) ){
448 css_classes__for_date.push( 'check_out_time_date_approved' );
449 }
450 break;
451
452 default:
453 // mixed statuses: 'change_over check_out' .... variations.... check more in function wpbc_get_availability_per_days_arr()
454 date_bookings_obj[ 'summary']['status_for_day' ] = 'available';
455 }
456
457
458
459 if ( 'available' != date_bookings_obj[ 'summary']['status_for_day' ] ){
460
461 var is_set_pending_days_selectable = _wpbc.calendar__get_param_value( resource_id, 'pending_days_selectable' ); // set pending days selectable // FixIn: 8.6.1.18.
462
463 if ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending' ) ){
464 css_classes__for_date.push( 'date2approve' );
465 is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable;
466 }
467 if ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved' ) ){
468 css_classes__for_date.push( 'date_approved' );
469 }
470 if ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_pending' ) ){
471 css_classes__for_date.push( 'check_out_time_date2approve', 'check_in_time_date2approve' );
472 is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable;
473 }
474 if ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_approved' ) ){
475 css_classes__for_date.push( 'check_out_time_date2approve', 'check_in_time_date_approved' );
476 is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable;
477 }
478 if ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved_pending' ) ){
479 css_classes__for_date.push( 'check_out_time_date_approved', 'check_in_time_date2approve' );
480 is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable;
481 }
482 if ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved_approved' ) ){
483 css_classes__for_date.push( 'check_out_time_date_approved', 'check_in_time_date_approved' );
484 }
485 }
486
487 return [ is_day_selectable, css_classes__for_date.join( ' ' ) ];
488 }
489
490
491 /**
492 * Mouseover calendar date cells
493 *
494 * @param string_date
495 * @param date - JavaScript Date Obj: Mon Dec 11 2023 00:00:00
496 * GMT+0200 (Eastern European Standard Time)
497 * @param calendar_params_arr - Calendar Settings Object: {
498 * "resource_id": 4
499 * }
500 * @param datepick_this - this of datepick Obj
501 * @returns {boolean}
502 */
503 function wpbc__calendar__on_hover_days( string_date, date, calendar_params_arr, datepick_this ) {
504
505 if ( null === date ) {
506 wpbc_calendars__clear_days_highlighting( ('undefined' !== typeof (calendar_params_arr[ 'resource_id' ])) ? calendar_params_arr[ 'resource_id' ] : '1' ); // FixIn: 10.5.2.4.
507 return false;
508 }
509
510 var class_day = wpbc__get__td_class_date( date ); // '1-9-2023'
511 var sql_class_day = wpbc__get__sql_class_date( date ); // '2023-01-09'
512 var resource_id = ( 'undefined' !== typeof(calendar_params_arr[ 'resource_id' ]) ) ? calendar_params_arr[ 'resource_id' ] : '1'; // '1'
513
514 // Get Data --------------------------------------------------------------------------------------------------------
515 var date_booking_obj = _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_class_day ); // {...}
516
517 if ( ! date_booking_obj ){ return false; }
518
519
520 // T o o l t i p s -------------------------------------------------------------------------------------------------
521 var tooltip_text = '';
522 if ( date_booking_obj[ 'summary']['tooltip_availability' ].length > 0 ){
523 tooltip_text += date_booking_obj[ 'summary']['tooltip_availability' ];
524 }
525 if ( date_booking_obj[ 'summary']['tooltip_day_cost' ].length > 0 ){
526 tooltip_text += date_booking_obj[ 'summary']['tooltip_day_cost' ];
527 }
528 if ( date_booking_obj[ 'summary']['tooltip_times' ].length > 0 ){
529 tooltip_text += date_booking_obj[ 'summary']['tooltip_times' ];
530 }
531 if ( date_booking_obj[ 'summary']['tooltip_booking_details' ].length > 0 ){
532 tooltip_text += date_booking_obj[ 'summary']['tooltip_booking_details' ];
533 }
534 wpbc_set_tooltip___for__calendar_date( tooltip_text, resource_id, class_day );
535
536
537
538 // U n h o v e r i n g in UNSELECTABLE_CALENDAR ------------------------------------------------------------
539 var is_unselectable_calendar = ( jQuery( '#calendar_booking_unselectable' + resource_id ).length > 0); // FixIn: 8.0.1.2.
540 var is_booking_form_exist = ( jQuery( '#booking_form_div' + resource_id ).length > 0 );
541 var is_add_booking_modal_calendar = ( jQuery( '#calendar_booking' + resource_id ).closest( '#wpbc_modal__add_booking__section' ).length > 0 );
542 var is_admin_calendar_preview = ( jQuery( '#calendar_booking' + resource_id ).closest( '[data-wpbc-admin-calendar-preview="1"]' ).length > 0 );
543
544 if ( ( is_unselectable_calendar ) && ( ! is_booking_form_exist ) ){
545
546 /**
547 * Un Hover all dates in calendar (without the booking form), if only Availability Calendar here and we do
548 * not insert Booking form by mistake.
549 */
550
551 wpbc_calendars__clear_days_highlighting( resource_id ); // Clear days highlighting
552
553 var css_of_calendar = '.wpbc_only_calendar #calendar_booking' + resource_id;
554 jQuery( css_of_calendar + ' .datepick-days-cell, '
555 + css_of_calendar + ' .datepick-days-cell a' ).css( 'cursor', 'default' ); // Set cursor to Default
556 return false;
557 }
558
559
560
561 // D a y s H o v e r i n g ------------------------------------------------------------------------------------
562 if (
563 ( location.href.indexOf( 'page=wpbc' ) == -1 )
564 || ( ( location.href.indexOf( 'page=wpbc' ) > 0 ) && ( location.href.indexOf( 'tab=add-booking' ) > 0 ) )
565 || ( is_add_booking_modal_calendar )
566 || ( is_admin_calendar_preview )
567 || ( location.href.indexOf( 'page=wpbc-setup' ) > 0 )
568 || ( location.href.indexOf( 'page=wpbc-availability' ) > 0 )
569 || ( ( location.href.indexOf( 'page=wpbc-settings' ) > 0 ) &&
570 ( location.href.indexOf( 'tab=builder_booking_form' ) > 0 )
571 )
572 ){
573 // The same as dates selection, but for days hovering
574
575 if ( 'function' == typeof( wpbc__calendar__do_days_highlight__bs ) ){
576 wpbc__calendar__do_days_highlight__bs( sql_class_day, date, resource_id );
577 } else if ( 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ) {
578 wpbc__calendar__do_days_highlight__unrestricted_range( sql_class_day, date, resource_id );
579 }
580 }
581
582 }
583
584
585 /**
586 * Highlight an unrestricted proposed range after the first click when Business Small is not loaded.
587 *
588 * @param {string} sql_class_day Hovered date in SQL format.
589 * @param {Date} date Hovered date.
590 * @param {number|string} resource_id Booking resource ID.
591 * @returns {boolean} True when the complete proposed range is available and highlighted.
592 */
593 function wpbc__calendar__do_days_highlight__unrestricted_range( sql_class_day, date, resource_id ){
594
595 var inst = wpbc_calendar__get_inst( resource_id );
596 var range_start;
597 var range_end;
598 var working_date;
599 var td_overs = [];
600
601 wpbc_calendars__clear_days_highlighting( resource_id );
602
603 if (
604 null === inst
605 || 'dynamic' !== _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' )
606 ) {
607 return false;
608 }
609
610 // Before a range starts, or after it is complete, preview the hovered date as the possible first day.
611 if ( 1 !== inst.dates.length || ! inst.dates[ 0 ] ) {
612 if ( ! wpbc_is_this_day_selectable( resource_id, sql_class_day ) ) {
613 return false;
614 }
615
616 jQuery( '#calendar_booking' + resource_id + ' .cal4date-' + wpbc__get__td_class_date( date ) )
617 .addClass( 'datepick-days-cell-over' );
618 return true;
619 }
620
621 range_start = new Date( inst.dates[ 0 ].getFullYear(), inst.dates[ 0 ].getMonth(), inst.dates[ 0 ].getDate(), 0, 0, 0 );
622 range_end = new Date( date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0 );
623
624 // Datepick only permits a check-out date on/after the first click. Do not preview a backward range.
625 if ( range_end.getTime() < range_start.getTime() ) {
626 return false;
627 }
628
629 working_date = new Date( range_start.getTime() );
630 while ( working_date.getTime() <= range_end.getTime() ) {
631 sql_class_day = wpbc__get__sql_class_date( working_date );
632 if ( ! wpbc_is_this_day_selectable( resource_id, sql_class_day ) ) {
633 return false;
634 }
635
636 td_overs.push( '#calendar_booking' + resource_id + ' .cal4date-' + wpbc__get__td_class_date( working_date ) );
637 working_date.setDate( working_date.getDate() + 1 );
638 }
639
640 if ( td_overs.length ) {
641 jQuery( td_overs.join( ',' ) ).addClass( 'datepick-days-cell-over' );
642 }
643
644 return true;
645 }
646
647
648 /**
649 * Complete an unrestricted two-click range when the Business Small range engine is not loaded.
650 * The hidden booking field must contain every date because Free time/hint handlers consume a comma-separated list.
651 *
652 * @param {string} selected_dates Datepick's endpoint string.
653 * @param {number|string} resource_id Booking resource ID.
654 * @returns {boolean} True after a complete valid range, false while incomplete or invalid.
655 */
656 function wpbc__calendar__do_days_select__unrestricted_range( selected_dates, resource_id ){
657
658 var inst = wpbc_calendar__get_inst( resource_id );
659 var range_start;
660 var range_end;
661 var current_date;
662 var selected_range = [];
663
664 if ( null === inst ) {
665 return false;
666 }
667
668 // Datepick writes "start - start" after click one. Keep its internal endpoint, but do not expose a submittable range yet.
669 if ( true === inst.stayOpen || inst.dates.length < 2 || ! inst.dates[ 0 ] || ! inst.dates[ 1 ] ) {
670 jQuery( '#date_booking' + resource_id ).val( '' );
671 return false;
672 }
673
674 range_start = new Date( inst.dates[ 0 ].getFullYear(), inst.dates[ 0 ].getMonth(), inst.dates[ 0 ].getDate(), 0, 0, 0 );
675 range_end = new Date( inst.dates[ 1 ].getFullYear(), inst.dates[ 1 ].getMonth(), inst.dates[ 1 ].getDate(), 0, 0, 0 );
676
677 // Defensive normalization. Datepick normally prevents an end date before the first endpoint.
678 if ( range_end.getTime() < range_start.getTime() ) {
679 current_date = range_start;
680 range_start = range_end;
681 range_end = current_date;
682 }
683
684 current_date = new Date( range_start.getTime() );
685 while ( current_date.getTime() <= range_end.getTime() ) {
686 if ( ! wpbc_is_this_day_selectable( resource_id, wpbc__get__sql_class_date( current_date ) ) ) {
687 wpbc_calendar__unselect_all_dates( resource_id );
688 if ( 'function' === typeof ( wpbc_front_end__show_message__warning ) ) {
689 wpbc_front_end__show_message__warning(
690 '#booking_form_div' + resource_id + ' .bk_calendar_frame',
691 _wpbc.get_message( 'message_range_selection_has_unavailable_dates' ),
692 'text'
693 );
694 }
695 return false;
696 }
697
698 selected_range.push( jQuery.datepick._formatDate( inst, current_date ) );
699 current_date.setDate( current_date.getDate() + 1 );
700 }
701
702 jQuery( '#date_booking' + resource_id ).val( selected_range.join( ', ' ) );
703 return true;
704 }
705
706
707 /**
708 * Prepare a collapsed range-selection guidance note for one calendar.
709 * Keeping one reusable node avoids repeated insertion while inactive guidance occupies no layout space.
710 *
711 * @param {number|string} resource_id Booking resource ID.
712 * @returns {jQuery} The prepared note, or an empty collection when guidance is unavailable.
713 */
714 function wpbc_range_selection__prepare_guidance( resource_id ){
715
716 var is_enabled = _wpbc.calendar__get_param_value( resource_id, 'range_selection_guidance_is_enabled' );
717 var $booking_form = jQuery( '#booking_form_div' + resource_id );
718 var $calendar_frame;
719 var $note = jQuery( '#wpbc_range_selection_guidance' + resource_id );
720
721 if (
722 false === is_enabled
723 || 0 === is_enabled
724 || '0' === is_enabled
725 || 'off' === String( is_enabled ).toLowerCase()
726 || 'false' === String( is_enabled ).toLowerCase()
727 || 'dynamic' !== _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' )
728 || 0 === $booking_form.length
729 ) {
730 $note.remove();
731 return jQuery();
732 }
733
734 if ( 0 < $note.length ) {
735 $note.stop( true, true ).hide().attr( 'aria-hidden', 'true' );
736 return $note;
737 }
738
739 $calendar_frame = $booking_form.find( '.block_hints.datepick' ).first();
740 if ( 0 === $calendar_frame.length ) {
741 $calendar_frame = $booking_form.find( '.wpbc_cal_powered_by' ).first();
742 if ( 0 === $calendar_frame.length ) {
743 $calendar_frame = $booking_form.find( '.bk_calendar_frame' ).first();
744 if ( 0 === $calendar_frame.length ) {
745 return jQuery();
746 }
747 }
748 }
749
750 $note = jQuery( '<div>', {
751 'id': 'wpbc_range_selection_guidance' + resource_id,
752 'class': 'wpbc_range_selection_guidance wpbc_front_end__message wpbc_fe_message_info',
753 'role': 'status',
754 'aria-live': 'polite',
755 'aria-hidden': 'true',
756 'style': 'display: none;'
757 } );
758 $note.append( jQuery( '<i>', {
759 'class': 'menu_icon icon-1x wpbc_icn_info_outline',
760 'aria-hidden': 'true'
761 } ) );
762 $note.append( jQuery( '<span>' ).text( _wpbc.get_message( 'message_range_selection_click_last_date' ) ) );
763 $calendar_frame.after( $note );
764
765 return $note;
766 }
767
768
769 /**
770 * Get the range-guidance animation duration for the current motion preference.
771 *
772 * @returns {number} Animation duration in milliseconds, or zero when reduced motion is preferred.
773 */
774 function wpbc_range_selection__get_guidance_animation_duration(){
775
776 if (
777 'function' === typeof window.matchMedia
778 && window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches
779 ) {
780 return 0;
781 }
782
783 return 160;
784 }
785
786
787 /**
788 * Collapse the range-selection guidance so it does not reserve empty layout space.
789 *
790 * @param {number|string} resource_id Booking resource ID.
791 */
792 function wpbc_range_selection__hide_guidance( resource_id ){
793
794 jQuery( '#wpbc_range_selection_guidance' + resource_id )
795 .attr( 'aria-hidden', 'true' )
796 .stop( true, true )
797 .slideUp( wpbc_range_selection__get_guidance_animation_duration() );
798 }
799
800
801 /**
802 * Show the prepared informational note after the first range click.
803 * The calendar parameter can be set to false by PHP or JavaScript to suppress this UI per resource.
804 *
805 * @param {number|string} resource_id Booking resource ID.
806 * @returns {boolean} True when the note was shown.
807 */
808 function wpbc_range_selection__show_guidance( resource_id ){
809
810 var $note = wpbc_range_selection__prepare_guidance( resource_id );
811
812 if ( 0 === $note.length ) {
813 return false;
814 }
815
816 $note
817 .stop( true, true )
818 .slideDown( wpbc_range_selection__get_guidance_animation_duration() )
819 .removeAttr( 'aria-hidden' );
820 $note.find( 'span' ).text( _wpbc.get_message( 'message_range_selection_click_last_date' ) );
821
822 return true;
823 }
824
825
826 /**
827 * Synchronize the guidance with Datepick's two-click range state.
828 *
829 * @param {number|string} resource_id Booking resource ID.
830 */
831 function wpbc_range_selection__sync_guidance( resource_id ){
832
833 var inst = wpbc_calendar__get_inst( resource_id );
834
835 if (
836 null !== inst
837 && 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' )
838 && true === inst.stayOpen
839 && 1 === inst.dates.length
840 && inst.dates[ 0 ]
841 ) {
842 wpbc_range_selection__show_guidance( resource_id );
843 return;
844 }
845
846 wpbc_range_selection__hide_guidance( resource_id );
847 }
848
849
850 /**
851 * Select calendar date cells
852 *
853 * @param date - JavaScript Date Obj: Mon Dec 11 2023 00:00:00
854 * GMT+0200 (Eastern European Standard Time)
855 * @param calendar_params_arr - Calendar Settings Object: {
856 * "resource_id": 4
857 * }
858 * @param datepick_this - this of datepick Obj
859 *
860 */
861 function wpbc__calendar__on_select_days( date, calendar_params_arr, datepick_this ){
862
863 var resource_id = ( 'undefined' !== typeof(calendar_params_arr[ 'resource_id' ]) ) ? calendar_params_arr[ 'resource_id' ] : '1'; // '1'
864
865 // Set unselectable, if only Availability Calendar here (and we do not insert Booking form by mistake).
866 var is_unselectable_calendar = ( jQuery( '#calendar_booking_unselectable' + resource_id ).length > 0); // FixIn: 8.0.1.2.
867 var is_booking_form_exist = ( jQuery( '#booking_form_div' + resource_id ).length > 0 );
868 if ( ( is_unselectable_calendar ) && ( ! is_booking_form_exist ) ){
869 wpbc_calendar__unselect_all_dates( resource_id ); // Unselect Dates
870 jQuery('.wpbc_only_calendar .popover_calendar_hover').remove(); // Hide all opened popovers
871 return false;
872 }
873
874 jQuery( '#date_booking' + resource_id ).val( date ); // Add selected dates to hidden textarea
875
876
877 if ( 'function' === typeof (wpbc__calendar__do_days_select__bs) ) {
878 wpbc__calendar__do_days_select__bs( date, resource_id );
879 } else if ( 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ) {
880 wpbc__calendar__do_days_select__unrestricted_range( date, resource_id );
881 }
882
883 wpbc_range_selection__sync_guidance( resource_id );
884
885 wpbc_disable_time_fields_in_booking_form( resource_id );
886
887 // Hook -- trigger day selection -----------------------------------------------------------------------------------
888 var mouse_clicked_dates = date; // Can be: "05.10.2023 - 07.10.2023" | "10.10.2023 - 10.10.2023" |
889 var all_selected_dates_arr = wpbc_get__selected_dates_sql__as_arr( resource_id ); // Can be: [ "2023-10-05", "2023-10-06", "2023-10-07", … ]
890 jQuery( ".booking_form_div" ).trigger( "date_selected", [ resource_id, mouse_clicked_dates, all_selected_dates_arr ] );
891 }
892
893 // Mark middle selected dates with 0.5 opacity // FixIn: 10.3.0.9.
894 jQuery( document ).ready( function (){
895 jQuery( ".booking_form_div" ).on( 'date_selected', function ( event, resource_id, date ){
896 if (
897 ( 'fixed' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ))
898 || ('dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ))
899 ){
900 var closed_timer = setTimeout( function (){
901 var middle_days_opacity = _wpbc.get_other_param( 'calendars__days_selection__middle_days_opacity' );
902 jQuery( '#calendar_booking' + resource_id + ' .datepick-current-day' ).not( ".selected_check_in_out" ).css( 'opacity', middle_days_opacity );
903 }, 10 );
904 }
905 } );
906 } );
907
908
909 /**
910 * -- T i m e F i e l d s start --------------------------------------------------------------------------
911 */
912
913 /**
914 * Disable time slots in booking form depend on selected dates and booked dates/times
915 *
916 * @param resource_id
917 */
918 function wpbc_disable_time_fields_in_booking_form( resource_id ){
919
920 /**
921 * 1. Get all time fields in the booking form as array of objects
922 * [
923 * { jquery_option: jQuery_Object {}
924 * name: 'rangetime2[]'
925 * times_as_seconds: [ 21600, 23400 ]
926 * value_option_24h: '06:00 - 06:30'
927 * }
928 * ...
929 * { jquery_option: jQuery_Object {}
930 * name: 'starttime2[]'
931 * times_as_seconds: [ 21600 ]
932 * value_option_24h: '06:00'
933 * }
934 * ]
935 */
936 var time_fields_obj_arr = wpbc_get__time_fields__in_booking_form__as_arr( resource_id );
937
938 // 2. Get all selected dates in SQL format like this [ "2023-08-23", "2023-08-24", "2023-08-25", ... ]
939 var selected_dates_arr = wpbc_get__selected_dates_sql__as_arr( resource_id );
940
941 // 3. Get child booking resources or single booking resource that exist in dates
942 var child_resources_arr = wpbc_clone_obj( _wpbc.booking__get_param_value( resource_id, 'resources_id_arr__in_dates' ) );
943
944 var sql_date;
945 var child_resource_id;
946 var merged_seconds;
947 var time_fields_obj;
948 var is_intersect;
949 var is_check_in;
950
951 var today_time__real = new Date( _wpbc.get_other_param( 'time_local_arr' )[0], ( parseInt( _wpbc.get_other_param( 'time_local_arr' )[1] ) - 1), _wpbc.get_other_param( 'time_local_arr' )[2], _wpbc.get_other_param( 'time_local_arr' )[3], _wpbc.get_other_param( 'time_local_arr' )[4], 0 );
952 var today_time__shift = new Date( _wpbc.get_other_param( 'today_arr' )[0], ( parseInt( _wpbc.get_other_param( 'today_arr' )[1] ) - 1), _wpbc.get_other_param( 'today_arr' )[2], _wpbc.get_other_param( 'today_arr' )[3], _wpbc.get_other_param( 'today_arr' )[4], 0 );
953 var allow_past_context = _wpbc.get_other_param( 'this_page_allow_past' );
954 var edit_booking_hash_context = _wpbc.get_other_param( 'this_page_booking_hash' );
955 var is_allow_past_context =
956 ( '1' === String( allow_past_context ) )
957 || ( 1 === allow_past_context )
958 || ( true === allow_past_context )
959 || ( '' !== String( edit_booking_hash_context || '' ) )
960 || ( location.href.indexOf( 'booking_hash' ) > -1 )
961 || ( location.href.indexOf( 'allow_past' ) > -1 );
962
963 // 4. Loop all time Fields options // FixIn: 10.3.0.2.
964 for ( let field_key = 0; field_key < time_fields_obj_arr.length; field_key++ ){
965
966 time_fields_obj_arr[ field_key ].disabled = 0; // By default, this time field is not disabled.
967
968 time_fields_obj = time_fields_obj_arr[ field_key ]; // { times_as_seconds: [ 21600, 23400 ], value_option_24h: '06:00 - 06:30', name: 'rangetime2[]', jquery_option: jQuery_Object {}}
969
970 // Loop all selected dates.
971 for ( var i = 0; i < selected_dates_arr.length; i++ ) {
972
973 // Get Date: '2023-08-18'.
974 sql_date = selected_dates_arr[i];
975
976 var is_time_in_past = is_allow_past_context ? false : wpbc_check_is_time_in_past( today_time__shift, sql_date, time_fields_obj );
977 // Exception for 'End Time' field, when selected several dates. // FixIn: 10.14.1.5.
978 if ( ( ! is_allow_past_context ) &&
979 ('On' !== _wpbc.calendar__get_param_value( resource_id, 'booking_recurrent_time' )) &&
980 (-1 !== time_fields_obj.name.indexOf( 'endtime' )) &&
981 (selected_dates_arr.length > 1)
982 ) {
983 is_time_in_past = wpbc_check_is_time_in_past( today_time__shift, selected_dates_arr[(selected_dates_arr.length - 1)], time_fields_obj );
984 }
985 if ( is_time_in_past ) {
986 // This time for selected date already in the past.
987 time_fields_obj_arr[field_key].disabled = 1;
988 break; // exist from Dates LOOP.
989 }
990 // FixIn: 9.9.0.31.
991 if (
992 ( 'Off' === _wpbc.calendar__get_param_value( resource_id, 'booking_recurrent_time' ) )
993 && ( selected_dates_arr.length>1 )
994 ){
995 //TODO: skip some fields checking if it's start / end time for mulple dates selection mode.
996 //TODO: we need to fix situation for entimes, when user select several dates, and in start time booked 00:00 - 15:00 , but systsme block untill 15:00 the end time as well, which is wrong, because it 2 or 3 dates selection and end date can be fullu available
997
998 if ( (0 == i) && (time_fields_obj[ 'name' ].indexOf( 'endtime' ) >= 0) ){
999 break;
1000 }
1001 if ( ( (selected_dates_arr.length-1) == i ) && (time_fields_obj[ 'name' ].indexOf( 'starttime' ) >= 0) ){
1002 break;
1003 }
1004 }
1005
1006
1007
1008 var how_many_resources_intersected = 0;
1009 // Loop all resources ID
1010 // for ( var res_key in child_resources_arr ){ // FixIn: 10.3.0.2.
1011 if ( null === child_resources_arr ) {
1012 child_resources_arr = [];
1013 }
1014 for ( let res_key = 0; res_key < child_resources_arr.length; res_key++ ){
1015
1016 child_resource_id = child_resources_arr[ res_key ];
1017
1018 // _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[12].booked_time_slots.merged_seconds = [ "07:00:11 - 07:30:02", "10:00:11 - 00:00:00" ]
1019 // _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[2].booked_time_slots.merged_seconds = [ [ 25211, 27002 ], [ 36011, 86400 ] ]
1020
1021 if ( false !== _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_date ) ){
1022 merged_seconds = _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_date )[ child_resource_id ].booked_time_slots.merged_seconds; // [ [ 25211, 27002 ], [ 36011, 86400 ] ]
1023 } else {
1024 merged_seconds = [];
1025 }
1026 if ( time_fields_obj.times_as_seconds.length > 1 ){
1027 is_intersect = wpbc_is_intersect__range_time_interval( [
1028 [
1029 ( parseInt( time_fields_obj.times_as_seconds[0] ) + 20 ),
1030 ( parseInt( time_fields_obj.times_as_seconds[1] ) - 20 )
1031 ]
1032 ]
1033 , merged_seconds );
1034 } else {
1035 is_check_in = (-1 !== time_fields_obj.name.indexOf( 'start' ));
1036 is_intersect = wpbc_is_intersect__one_time_interval(
1037 ( ( is_check_in )
1038 ? parseInt( time_fields_obj.times_as_seconds ) + 20
1039 : parseInt( time_fields_obj.times_as_seconds ) - 20
1040 )
1041 , merged_seconds );
1042 }
1043 if (is_intersect){
1044 how_many_resources_intersected++; // Increase
1045 }
1046
1047 }
1048
1049 if ( child_resources_arr.length == how_many_resources_intersected ) {
1050 // All resources intersected, then it's means that this time-slot or time must be Disabled, and we can exist from selected_dates_arr LOOP
1051
1052 time_fields_obj_arr[ field_key ].disabled = 1;
1053 break; // exist from Dates LOOP
1054 }
1055 }
1056 }
1057
1058
1059 // 5. Now we can disable time slot in HTML by using ( field.disabled == 1 ) property
1060 wpbc__html__time_field_options__set_disabled( time_fields_obj_arr );
1061
1062 jQuery( ".booking_form_div" ).trigger( 'wpbc_hook_timeslots_disabled', [resource_id, selected_dates_arr] ); // Trigger hook on disabling timeslots. Usage: jQuery( ".booking_form_div" ).on( 'wpbc_hook_timeslots_disabled', function ( event, bk_type, all_dates ){ ... } ); // FixIn: 8.7.11.9.
1063 }
1064
1065
1066 /**
1067 * Check if specific time(-slot) already in the past for selected date
1068 *
1069 * @param js_current_time_to_check - JS Date
1070 * @param sql_date - '2025-01-26'
1071 * @param time_fields_obj - Object
1072 * @returns {boolean}
1073 */
1074 function wpbc_check_is_time_in_past( js_current_time_to_check, sql_date, time_fields_obj ) {
1075
1076 // FixIn: 10.9.6.4
1077 var sql_date_arr = sql_date.split( '-' );
1078 var sql_date__midnight = new Date( parseInt( sql_date_arr[0] ), ( parseInt( sql_date_arr[1] ) - 1 ), parseInt( sql_date_arr[2] ), 0, 0, 0 );
1079 var sql_date__midnight_miliseconds = sql_date__midnight.getTime();
1080
1081 var is_intersect = false;
1082
1083 if ( time_fields_obj.times_as_seconds.length > 1 ) {
1084
1085 if ( js_current_time_to_check.getTime() > (sql_date__midnight_miliseconds + (parseInt( time_fields_obj.times_as_seconds[0] ) + 20) * 1000) ) {
1086 is_intersect = true;
1087 }
1088 if ( js_current_time_to_check.getTime() > (sql_date__midnight_miliseconds + (parseInt( time_fields_obj.times_as_seconds[1] ) - 20) * 1000) ) {
1089 is_intersect = true;
1090 }
1091
1092 } else {
1093 var is_check_in = (-1 !== time_fields_obj.name.indexOf( 'start' ));
1094
1095 var times_as_seconds_check = (is_check_in) ? parseInt( time_fields_obj.times_as_seconds ) + 20 : parseInt( time_fields_obj.times_as_seconds ) - 20;
1096
1097 times_as_seconds_check = sql_date__midnight_miliseconds + times_as_seconds_check * 1000;
1098
1099 if ( js_current_time_to_check.getTime() > times_as_seconds_check ) {
1100 is_intersect = true;
1101 }
1102 }
1103
1104 return is_intersect;
1105 }
1106
1107 /**
1108 * Is number inside /intersect of array of intervals ?
1109 *
1110 * @param time_A - 25800
1111 * @param time_interval_B - [ [ 25211, 27002 ], [ 36011, 86400 ] ]
1112 * @returns {boolean}
1113 */
1114 function wpbc_is_intersect__one_time_interval( time_A, time_interval_B ){
1115
1116 for ( var j = 0; j < time_interval_B.length; j++ ){
1117
1118 if ( (parseInt( time_A ) > parseInt( time_interval_B[ j ][ 0 ] )) && (parseInt( time_A ) < parseInt( time_interval_B[ j ][ 1 ] )) ){
1119 return true
1120 }
1121
1122 // if ( ( parseInt( time_A ) == parseInt( time_interval_B[ j ][ 0 ] ) ) || ( parseInt( time_A ) == parseInt( time_interval_B[ j ][ 1 ] ) ) ) {
1123 // // Time A just at the border of interval
1124 // }
1125 }
1126
1127 return false;
1128 }
1129
1130 /**
1131 * Is these array of intervals intersected ?
1132 *
1133 * @param time_interval_A - [ [ 21600, 23400 ] ]
1134 * @param time_interval_B - [ [ 25211, 27002 ], [ 36011, 86400 ] ]
1135 * @returns {boolean}
1136 */
1137 function wpbc_is_intersect__range_time_interval( time_interval_A, time_interval_B ){
1138
1139 var is_intersect;
1140
1141 for ( var i = 0; i < time_interval_A.length; i++ ){
1142
1143 for ( var j = 0; j < time_interval_B.length; j++ ){
1144
1145 is_intersect = wpbc_intervals__is_intersected( time_interval_A[ i ], time_interval_B[ j ] );
1146
1147 if ( is_intersect ){
1148 return true;
1149 }
1150 }
1151 }
1152
1153 return false;
1154 }
1155
1156 /**
1157 * Get all time fields in the booking form as array of objects
1158 *
1159 * @param resource_id
1160 * @returns []
1161 *
1162 * Example:
1163 * [
1164 * {
1165 * value_option_24h: '06:00 - 06:30'
1166 * times_as_seconds: [ 21600, 23400 ]
1167 * jquery_option: jQuery_Object {}
1168 * name: 'rangetime2[]'
1169 * }
1170 * ...
1171 * {
1172 * value_option_24h: '06:00'
1173 * times_as_seconds: [ 21600 ]
1174 * jquery_option: jQuery_Object {}
1175 * name: 'starttime2[]'
1176 * }
1177 * ]
1178 */
1179 function wpbc_get__time_fields__in_booking_form__as_arr( resource_id ){
1180 /**
1181 * Fields with [] like this select[name="rangetime1[]"]
1182 * it's when we have 'multiple' in shortcode: [select* rangetime multiple "06:00 - 06:30" ... ]
1183 */
1184 var time_fields_arr=[
1185 'select[name="rangetime' + resource_id + '"]',
1186 'select[name="rangetime' + resource_id + '[]"]',
1187 'select[name="starttime' + resource_id + '"]',
1188 'select[name="starttime' + resource_id + '[]"]',
1189 'select[name="endtime' + resource_id + '"]',
1190 'select[name="endtime' + resource_id + '[]"]'
1191 ];
1192
1193 var time_fields_obj_arr = [];
1194
1195 // Loop all Time Fields
1196 for ( var ctf= 0; ctf < time_fields_arr.length; ctf++ ){
1197
1198 var time_field = time_fields_arr[ ctf ];
1199 var time_option = jQuery( time_field + ' option' );
1200
1201 // Loop all options in time field
1202 for ( var j = 0; j < time_option.length; j++ ){
1203
1204 var jquery_option = jQuery( time_field + ' option:eq(' + j + ')' );
1205 var value_option_seconds_arr = jquery_option.val().split( '-' );
1206 var times_as_seconds = [];
1207
1208 // Get time as seconds
1209 if ( value_option_seconds_arr.length ){ // FixIn: 9.8.10.1.
1210 for ( let i = 0; i < value_option_seconds_arr.length; i++ ){ // FixIn: 10.0.0.56.
1211 // value_option_seconds_arr[i] = '14:00 ' | ' 16:00' (if from 'rangetime') and '16:00' if (start/end time)
1212
1213 var start_end_times_arr = value_option_seconds_arr[ i ].trim().split( ':' );
1214
1215 var time_in_seconds = parseInt( start_end_times_arr[ 0 ] ) * 60 * 60 + parseInt( start_end_times_arr[ 1 ] ) * 60;
1216
1217 times_as_seconds.push( time_in_seconds );
1218 }
1219 }
1220
1221 time_fields_obj_arr.push( {
1222 'name' : jQuery( time_field ).attr( 'name' ),
1223 'value_option_24h': jquery_option.val(),
1224 'jquery_option' : jquery_option,
1225 'times_as_seconds': times_as_seconds
1226 } );
1227 }
1228 }
1229
1230 return time_fields_obj_arr;
1231 }
1232
1233 /**
1234 * Disable HTML options and add booked CSS class
1235 *
1236 * @param time_fields_obj_arr - this value is from the func:
1237 * wpbc_get__time_fields__in_booking_form__as_arr( resource_id )
1238 * [
1239 * { jquery_option: jQuery_Object {}
1240 * name: 'rangetime2[]'
1241 * times_as_seconds: [ 21600, 23400 ]
1242 * value_option_24h: '06:00 - 06:30'
1243 * disabled = 1
1244 * }
1245 * ...
1246 * { jquery_option: jQuery_Object {}
1247 * name: 'starttime2[]'
1248 * times_as_seconds: [ 21600 ]
1249 * value_option_24h: '06:00'
1250 * disabled = 0
1251 * }
1252 * ]
1253 *
1254 */
1255 function wpbc__html__time_field_options__set_disabled( time_fields_obj_arr ){
1256
1257 var jquery_option;
1258
1259 for ( var i = 0; i < time_fields_obj_arr.length; i++ ){
1260
1261 var jquery_option = time_fields_obj_arr[ i ].jquery_option;
1262
1263 if ( 1 == time_fields_obj_arr[ i ].disabled ){
1264 jquery_option.prop( 'disabled', true ); // Make disable some options
1265 jquery_option.addClass( 'booked' ); // Add "booked" CSS class
1266
1267 // if this booked element selected --> then deselect it
1268 if ( jquery_option.prop( 'selected' ) ){
1269 jquery_option.prop( 'selected', false );
1270
1271 jquery_option.parent().find( 'option:not([disabled]):first' ).prop( 'selected', true ).trigger( "change" );
1272 }
1273
1274 } else {
1275 jquery_option.prop( 'disabled', false ); // Make active all times
1276 jquery_option.removeClass( 'booked' ); // Remove class "booked"
1277 }
1278 }
1279
1280 }
1281
1282 /**
1283 * Check if this time_range | Time_Slot is Full Day booked
1284 *
1285 * @param timeslot_arr_in_seconds - [ 36011, 86400 ]
1286 * @returns {boolean}
1287 */
1288 function wpbc_is_this_timeslot__full_day_booked( timeslot_arr_in_seconds ){
1289
1290 if (
1291 ( timeslot_arr_in_seconds.length > 1 )
1292 && ( parseInt( timeslot_arr_in_seconds[ 0 ] ) < 30 )
1293 && ( parseInt( timeslot_arr_in_seconds[ 1 ] ) > ( (24 * 60 * 60) - 30) )
1294 ){
1295 return true;
1296 }
1297
1298 return false;
1299 }
1300
1301
1302 // -----------------------------------------------------------------------------------------------------------------
1303 /* == S e l e c t e d D a t e s / T i m e - F i e l d s ==
1304 // ----------------------------------------------------------------------------------------------------------------- */
1305
1306 /**
1307 * Get all selected dates in SQL format like this [ "2023-08-23", "2023-08-24" , ... ]
1308 *
1309 * @param resource_id
1310 * @returns {[]} [ "2023-08-23", "2023-08-24", "2023-08-25", "2023-08-26", "2023-08-27", "2023-08-28",
1311 * "2023-08-29" ]
1312 */
1313 function wpbc_get__selected_dates_sql__as_arr( resource_id ){
1314
1315 var selected_dates_arr = [];
1316 selected_dates_arr = jQuery( '#date_booking' + resource_id ).val().split(',');
1317
1318 if ( selected_dates_arr.length ){ // FixIn: 9.8.10.1.
1319 for ( let i = 0; i < selected_dates_arr.length; i++ ){ // FixIn: 10.0.0.56.
1320 selected_dates_arr[ i ] = selected_dates_arr[ i ].trim();
1321 selected_dates_arr[ i ] = selected_dates_arr[ i ].split( '.' );
1322 if ( selected_dates_arr[ i ].length > 1 ){
1323 selected_dates_arr[ i ] = selected_dates_arr[ i ][ 2 ] + '-' + selected_dates_arr[ i ][ 1 ] + '-' + selected_dates_arr[ i ][ 0 ];
1324 }
1325 }
1326 }
1327
1328 // Remove empty elements from an array
1329 selected_dates_arr = selected_dates_arr.filter( function ( n ){ return parseInt(n); } );
1330
1331 selected_dates_arr.sort();
1332
1333 return selected_dates_arr;
1334 }
1335
1336
1337 /**
1338 * Get all time fields in the booking form as array of objects
1339 *
1340 * @param resource_id
1341 * @param is_only_selected_time
1342 * @returns []
1343 *
1344 * Example:
1345 * [
1346 * {
1347 * value_option_24h: '06:00 - 06:30'
1348 * times_as_seconds: [ 21600, 23400 ]
1349 * jquery_option: jQuery_Object {}
1350 * name: 'rangetime2[]'
1351 * }
1352 * ...
1353 * {
1354 * value_option_24h: '06:00'
1355 * times_as_seconds: [ 21600 ]
1356 * jquery_option: jQuery_Object {}
1357 * name: 'starttime2[]'
1358 * }
1359 * ]
1360 */
1361 function wpbc_get__selected_time_fields__in_booking_form__as_arr( resource_id, is_only_selected_time = true ){
1362 /**
1363 * Fields with [] like this select[name="rangetime1[]"]
1364 * it's when we have 'multiple' in shortcode: [select* rangetime multiple "06:00 - 06:30" ... ]
1365 */
1366 var time_fields_arr=[
1367 'select[name="rangetime' + resource_id + '"]',
1368 'select[name="rangetime' + resource_id + '[]"]',
1369 'select[name="starttime' + resource_id + '"]',
1370 'select[name="starttime' + resource_id + '[]"]',
1371 'select[name="endtime' + resource_id + '"]',
1372 'select[name="endtime' + resource_id + '[]"]',
1373 'select[name="durationtime' + resource_id + '"]',
1374 'select[name="durationtime' + resource_id + '[]"]'
1375 ];
1376
1377 var time_fields_obj_arr = [];
1378
1379 // Loop all Time Fields
1380 for ( var ctf= 0; ctf < time_fields_arr.length; ctf++ ){
1381
1382 var time_field = time_fields_arr[ ctf ];
1383
1384 var time_option;
1385 if ( is_only_selected_time ){
1386 time_option = jQuery( '#booking_form' + resource_id + ' ' + time_field + ' option:selected' ); // Exclude conditional fields, because of using '#booking_form3 ...'
1387 } else {
1388 time_option = jQuery( '#booking_form' + resource_id + ' ' + time_field + ' option' ); // All time fields
1389 }
1390
1391
1392 // Loop all options in time field
1393 for ( var j = 0; j < time_option.length; j++ ){
1394
1395 var jquery_option = jQuery( time_option[ j ] ); // Get only selected options //jQuery( time_field + ' option:eq(' + j + ')' );
1396 var value_option_seconds_arr = jquery_option.val().split( '-' );
1397 var times_as_seconds = [];
1398
1399 // Get time as seconds
1400 if ( value_option_seconds_arr.length ){ // FixIn: 9.8.10.1.
1401 for ( let i = 0; i < value_option_seconds_arr.length; i++ ){ // FixIn: 10.0.0.56.
1402 // value_option_seconds_arr[i] = '14:00 ' | ' 16:00' (if from 'rangetime') and '16:00' if (start/end time)
1403
1404 var start_end_times_arr = value_option_seconds_arr[ i ].trim().split( ':' );
1405
1406 var time_in_seconds = parseInt( start_end_times_arr[ 0 ] ) * 60 * 60 + parseInt( start_end_times_arr[ 1 ] ) * 60;
1407
1408 times_as_seconds.push( time_in_seconds );
1409 }
1410 }
1411
1412 time_fields_obj_arr.push( {
1413 'name' : jQuery( '#booking_form' + resource_id + ' ' + time_field ).attr( 'name' ),
1414 'value_option_24h': jquery_option.val(),
1415 'jquery_option' : jquery_option,
1416 'times_as_seconds': times_as_seconds
1417 } );
1418 }
1419 }
1420
1421 // Text: [starttime] - [endtime] -----------------------------------------------------------------------------
1422
1423 var text_time_fields_arr=[
1424 'input[name="starttime' + resource_id + '"]',
1425 'input[name="endtime' + resource_id + '"]',
1426 ];
1427 for ( var tf= 0; tf < text_time_fields_arr.length; tf++ ){
1428
1429 var text_jquery = jQuery( '#booking_form' + resource_id + ' ' + text_time_fields_arr[ tf ] ); // Exclude conditional fields, because of using '#booking_form3 ...'
1430 if ( text_jquery.length > 0 ){
1431
1432 var time__h_m__arr = text_jquery.val().trim().split( ':' ); // '14:00'
1433 if ( 0 == time__h_m__arr.length ){
1434 continue; // Not entered time value in a field
1435 }
1436 if ( 1 == time__h_m__arr.length ){
1437 if ( '' === time__h_m__arr[ 0 ] ){
1438 continue; // Not entered time value in a field
1439 }
1440 time__h_m__arr[ 1 ] = 0;
1441 }
1442 var text_time_in_seconds = parseInt( time__h_m__arr[ 0 ] ) * 60 * 60 + parseInt( time__h_m__arr[ 1 ] ) * 60;
1443
1444 var text_times_as_seconds = [];
1445 text_times_as_seconds.push( text_time_in_seconds );
1446
1447 time_fields_obj_arr.push( {
1448 'name' : text_jquery.attr( 'name' ),
1449 'value_option_24h': text_jquery.val(),
1450 'jquery_option' : text_jquery,
1451 'times_as_seconds': text_times_as_seconds
1452 } );
1453 }
1454 }
1455
1456 return time_fields_obj_arr;
1457 }
1458
1459
1460
1461 // ---------------------------------------------------------------------------------------------------------------------
1462 /* == S U P P O R T for C A L E N D A R ==
1463 // --------------------------------------------------------------------------------------------------------------------- */
1464
1465 /**
1466 * Get Calendar datepick Instance.
1467 *
1468 * @param {int|string} resource_id
1469 * @returns {*|null}
1470 */
1471 function wpbc_calendar__get_inst(resource_id) {
1472
1473 if ( 'undefined' === typeof (resource_id) ) {
1474 resource_id = '1';
1475 }
1476
1477 if ( jQuery( '#calendar_booking' + resource_id ).length > 0 ) {
1478
1479 try {
1480 var inst = jQuery.datepick._getInst( jQuery( '#calendar_booking' + resource_id ).get( 0 ) );
1481 return inst ? inst : null;
1482 } catch ( e ) {
1483 return null;
1484 }
1485 }
1486
1487 return null;
1488 }
1489
1490
1491 /**
1492 * Unselect all dates in calendar and visually update this calendar
1493 *
1494 * @param resource_id ID of booking resource
1495 * @returns {boolean} true on success | false, if no such calendar
1496 */
1497 function wpbc_calendar__unselect_all_dates( resource_id ){
1498
1499 if ( 'undefined' === typeof (resource_id) ){
1500 resource_id = '1';
1501 }
1502
1503 wpbc_range_selection__hide_guidance( resource_id );
1504
1505 var inst = wpbc_calendar__get_inst( resource_id )
1506
1507 if ( null !== inst ){
1508
1509 // Unselect all dates and set properties of Datepick
1510 jQuery( '#date_booking' + resource_id ).val( '' ); //FixIn: 5.4.3
1511 inst.stayOpen = false;
1512 inst.dates = [];
1513 jQuery.datepick._updateDatepick( inst );
1514
1515 return true
1516 }
1517
1518 return false;
1519
1520 }
1521
1522 /**
1523 * Clear days highlighting in All or specific Calendars
1524 *
1525 * @param resource_id - can be skiped to clear highlighting in all calendars
1526 */
1527 function wpbc_calendars__clear_days_highlighting( resource_id ){
1528
1529 if ( 'undefined' !== typeof ( resource_id ) ){
1530
1531 jQuery( '#calendar_booking' + resource_id + ' .datepick-days-cell-over' ).removeClass( 'datepick-days-cell-over' ); // Clear in specific calendar
1532
1533 } else {
1534 jQuery( '.datepick-days-cell-over' ).removeClass( 'datepick-days-cell-over' ); // Clear in all calendars
1535 }
1536 }
1537
1538 /**
1539 * Scroll to specific month in calendar
1540 *
1541 * @param resource_id ID of resource
1542 * @param year - real year - 2023
1543 * @param month - real month - 12
1544 * @returns {boolean}
1545 */
1546 function wpbc_calendar__scroll_to( resource_id, year, month ){
1547
1548 if ( 'undefined' === typeof (resource_id) ){ resource_id = '1'; }
1549 var inst = wpbc_calendar__get_inst( resource_id )
1550 if ( null !== inst ){
1551
1552 year = parseInt( year );
1553 month = parseInt( month ) - 1; // In JS date, month -1
1554
1555 inst.cursorDate = new Date();
1556 // In some cases, the setFullYear can set only Year, and not the Month and day // FixIn: 6.2.3.5.
1557 inst.cursorDate.setFullYear( year, month, 1 );
1558 inst.cursorDate.setMonth( month );
1559 inst.cursorDate.setDate( 1 );
1560
1561 inst.drawMonth = inst.cursorDate.getMonth();
1562 inst.drawYear = inst.cursorDate.getFullYear();
1563
1564 jQuery.datepick._notifyChange( inst );
1565 jQuery.datepick._adjustInstDate( inst );
1566 jQuery.datepick._showDate( inst );
1567 jQuery.datepick._updateDatepick( inst );
1568
1569 return true;
1570 }
1571 return false;
1572 }
1573
1574 /**
1575 * Is this date selectable in calendar (mainly it's means AVAILABLE date)
1576 *
1577 * @param {int|string} resource_id 1
1578 * @param {string} sql_class_day '2023-08-11'
1579 * @returns {boolean} true | false
1580 */
1581 function wpbc_is_this_day_selectable( resource_id, sql_class_day ){
1582
1583 // Get Data --------------------------------------------------------------------------------------------------------
1584 var date_bookings_obj = _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_class_day );
1585 if ( ! date_bookings_obj || 'undefined' === typeof ( date_bookings_obj[ 'day_availability' ] ) ) {
1586 return false;
1587 }
1588
1589 var is_day_selectable = ( parseInt( date_bookings_obj[ 'day_availability' ] ) > 0 );
1590
1591 if ( typeof (date_bookings_obj[ 'summary' ]) === 'undefined' ){
1592 return is_day_selectable;
1593 }
1594
1595 if ( 'available' != date_bookings_obj[ 'summary']['status_for_day' ] ){
1596
1597 var is_set_pending_days_selectable = _wpbc.calendar__get_param_value( resource_id, 'pending_days_selectable' ); // set pending days selectable // FixIn: 8.6.1.18.
1598 var booking_statuses_arr = wpbc_get_booking_statuses__as_arr( date_bookings_obj );
1599
1600 if (
1601 ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending' ) )
1602 || ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_pending' ) )
1603 || ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_approved' ) )
1604 || ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved_pending' ) )
1605 ){
1606 is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable;
1607 }
1608 }
1609
1610 return is_day_selectable;
1611 }
1612
1613 /**
1614 * Is date to check IN array of selected dates
1615 *
1616 * @param {date}js_date_to_check - JS Date - simple JavaScript Date object
1617 * @param {[]} js_dates_arr - [ JSDate, ... ] - array of JS dates
1618 * @returns {boolean}
1619 */
1620 function wpbc_is_this_day_among_selected_days( js_date_to_check, js_dates_arr ){
1621
1622 for ( var date_index = 0; date_index < js_dates_arr.length ; date_index++ ){ // FixIn: 8.4.5.16.
1623 if ( ( js_dates_arr[ date_index ].getFullYear() === js_date_to_check.getFullYear() ) &&
1624 ( js_dates_arr[ date_index ].getMonth() === js_date_to_check.getMonth() ) &&
1625 ( js_dates_arr[ date_index ].getDate() === js_date_to_check.getDate() ) ) {
1626 return true;
1627 }
1628 }
1629
1630 return false;
1631 }
1632
1633 /**
1634 * Get SQL Class Date '2023-08-01' from JS Date
1635 *
1636 * @param date JS Date
1637 * @returns {string} '2023-08-12'
1638 */
1639 function wpbc__get__sql_class_date( date ){
1640
1641 var sql_class_day = date.getFullYear() + '-';
1642 sql_class_day += ( ( date.getMonth() + 1 ) < 10 ) ? '0' : '';
1643 sql_class_day += ( date.getMonth() + 1 ) + '-'
1644 sql_class_day += ( date.getDate() < 10 ) ? '0' : '';
1645 sql_class_day += date.getDate();
1646
1647 return sql_class_day;
1648 }
1649
1650 /**
1651 * Get JS Date from the SQL date format '2024-05-14'
1652 * @param sql_class_date
1653 * @returns {Date}
1654 */
1655 function wpbc__get__js_date( sql_class_date ){
1656
1657 var sql_class_date_arr = sql_class_date.split( '-' );
1658
1659 var date_js = new Date();
1660
1661 date_js.setFullYear( parseInt( sql_class_date_arr[ 0 ] ), (parseInt( sql_class_date_arr[ 1 ] ) - 1), parseInt( sql_class_date_arr[ 2 ] ) ); // year, month, date
1662
1663 // Without this time adjust Dates selection in Datepicker can not work!!!
1664 date_js.setHours(0);
1665 date_js.setMinutes(0);
1666 date_js.setSeconds(0);
1667 date_js.setMilliseconds(0);
1668
1669 return date_js;
1670 }
1671
1672 /**
1673 * Get TD Class Date '1-31-2023' from JS Date
1674 *
1675 * @param date JS Date
1676 * @returns {string} '1-31-2023'
1677 */
1678 function wpbc__get__td_class_date( date ){
1679
1680 var td_class_day = (date.getMonth() + 1) + '-' + date.getDate() + '-' + date.getFullYear(); // '1-9-2023'
1681
1682 return td_class_day;
1683 }
1684
1685 /**
1686 * Get date params from string date
1687 *
1688 * @param date string date like '31.5.2023'
1689 * @param separator default '.' can be skipped.
1690 * @returns { {date: number, month: number, year: number} }
1691 */
1692 function wpbc__get__date_params__from_string_date( date , separator){
1693
1694 separator = ( 'undefined' !== typeof (separator) ) ? separator : '.';
1695
1696 var date_arr = date.split( separator );
1697 var date_obj = {
1698 'year' : parseInt( date_arr[ 2 ] ),
1699 'month': (parseInt( date_arr[ 1 ] ) - 1),
1700 'date' : parseInt( date_arr[ 0 ] )
1701 };
1702 return date_obj; // for = new Date( date_obj.year , date_obj.month , date_obj.date );
1703 }
1704
1705 /**
1706 * Add Spin Loader to calendar
1707 * @param resource_id
1708 */
1709 function wpbc_calendar__loading__start( resource_id ){
1710 if ( ! jQuery( '#calendar_booking' + resource_id ).next().hasClass( 'wpbc_spins_loader_wrapper' ) ){
1711 jQuery( '#calendar_booking' + resource_id ).after( '<div class="wpbc_spins_loader_wrapper"><div class="wpbc_spin_loader_one_new"></div></div>' );
1712 }
1713 if ( ! jQuery( '#calendar_booking' + resource_id ).hasClass( 'wpbc_calendar_blur_small' ) ){
1714 jQuery( '#calendar_booking' + resource_id ).addClass( 'wpbc_calendar_blur_small' );
1715 }
1716 wpbc_calendar__blur__start( resource_id );
1717 }
1718
1719 /**
1720 * Remove Spin Loader to calendar
1721 * @param resource_id
1722 */
1723 function wpbc_calendar__loading__stop( resource_id ){
1724 jQuery( '#calendar_booking' + resource_id + ' + .wpbc_spins_loader_wrapper' ).remove();
1725 jQuery( '#calendar_booking' + resource_id ).removeClass( 'wpbc_calendar_blur_small' );
1726 wpbc_calendar__blur__stop( resource_id );
1727 }
1728
1729 /**
1730 * Add Blur to calendar
1731 * @param resource_id
1732 */
1733 function wpbc_calendar__blur__start( resource_id ){
1734 if ( ! jQuery( '#calendar_booking' + resource_id ).hasClass( 'wpbc_calendar_blur' ) ){
1735 jQuery( '#calendar_booking' + resource_id ).addClass( 'wpbc_calendar_blur' );
1736 }
1737 }
1738
1739 /**
1740 * Remove Blur in calendar
1741 * @param resource_id
1742 */
1743 function wpbc_calendar__blur__stop( resource_id ){
1744 jQuery( '#calendar_booking' + resource_id ).removeClass( 'wpbc_calendar_blur' );
1745 }
1746
1747
1748 // .................................................................................................................
1749 /* == Calendar Update - View ==
1750 // ................................................................................................................. */
1751
1752 /**
1753 * Update look of calendar (safe).
1754 *
1755 * In Elementor preview the DOM can be re-rendered, so the calendar element may exist
1756 * while the Datepick instance is missing. In that case try to (re)initialize.
1757 *
1758 * @param {int|string} resource_id
1759 * @return {boolean} true if updated, false if not possible
1760 */
1761 function wpbc_calendar__update_look(resource_id) {
1762
1763 var inst = wpbc_calendar__get_inst( resource_id );
1764
1765 // If instance missing, try to re-init calendar once.
1766 if ( null === inst ) {
1767
1768 var jq_cal = jQuery( '#calendar_booking' + resource_id );
1769
1770 if ( jq_cal.length && ('function' === typeof wpbc_calendar_show) ) {
1771
1772 // Elementor sometimes leaves stale class without real instance.
1773 if ( jq_cal.hasClass( 'hasDatepick' ) ) {
1774 jq_cal.removeClass( 'hasDatepick' );
1775 }
1776
1777 // Try to init datepick markup now.
1778 wpbc_calendar_show( resource_id );
1779
1780 // Try again.
1781 inst = wpbc_calendar__get_inst( resource_id );
1782 }
1783 }
1784
1785 // Still no instance -> do not crash the whole ajax flow.
1786 if ( null === inst ) {
1787 return false;
1788 }
1789
1790 jQuery.datepick._updateDatepick( inst );
1791 return true;
1792 }
1793
1794
1795
1796 /**
1797 * Update dynamically Number of Months in calendar
1798 *
1799 * @param resource_id int
1800 * @param months_number int
1801 */
1802 function wpbc_calendar__update_months_number( resource_id, months_number ){
1803 var inst = wpbc_calendar__get_inst( resource_id );
1804 if ( null !== inst ){
1805 inst.settings[ 'numberOfMonths' ] = months_number;
1806 //_wpbc.calendar__set_param_value( resource_id, 'calendar_number_of_months', months_number );
1807 wpbc_calendar__update_look( resource_id );
1808 }
1809 }
1810
1811
1812 /**
1813 * Show calendar in different Skin
1814 *
1815 * @param selected_skin_url
1816 */
1817 function wpbc__calendar__change_skin( selected_skin_url ){
1818
1819 //console.log( 'SKIN SELECTION ::', selected_skin_url );
1820
1821 // Remove CSS skin
1822 var stylesheet = document.getElementById( 'wpbc-calendar-skin-css' );
1823 stylesheet.parentNode.removeChild( stylesheet );
1824
1825
1826 // Add new CSS skin
1827 var headID = document.getElementsByTagName( "head" )[ 0 ];
1828 var cssNode = document.createElement( 'link' );
1829 cssNode.type = 'text/css';
1830 cssNode.setAttribute( "id", "wpbc-calendar-skin-css" );
1831 cssNode.rel = 'stylesheet';
1832 cssNode.media = 'screen';
1833 cssNode.href = selected_skin_url; //"http://beta/wp-content/plugins/booking/css/skins/green-01.css";
1834 headID.appendChild( cssNode );
1835 }
1836
1837
1838 function wpbc__css__change_skin( selected_skin_url, stylesheet_id = 'wpbc-time_picker-skin-css' ){
1839
1840 // Remove CSS skin
1841 var stylesheet = document.getElementById( stylesheet_id );
1842 stylesheet.parentNode.removeChild( stylesheet );
1843
1844
1845 // Add new CSS skin
1846 var headID = document.getElementsByTagName( "head" )[ 0 ];
1847 var cssNode = document.createElement( 'link' );
1848 cssNode.type = 'text/css';
1849 cssNode.setAttribute( "id", stylesheet_id );
1850 cssNode.rel = 'stylesheet';
1851 cssNode.media = 'screen';
1852 cssNode.href = selected_skin_url; //"http://beta/wp-content/plugins/booking/css/skins/green-01.css";
1853 headID.appendChild( cssNode );
1854 }
1855
1856
1857 // ---------------------------------------------------------------------------------------------------------------------
1858 /* == S U P P O R T M A T H ==
1859 // --------------------------------------------------------------------------------------------------------------------- */
1860
1861 /**
1862 * Merge several intersected intervals or return not intersected:
1863 * [[1,3],[2,6],[8,10],[15,18]] -> [[1,6],[8,10],[15,18]]
1864 *
1865 * @param [] intervals [ [1,3],[2,4],[6,8],[9,10],[3,7] ]
1866 * @returns [] [ [1,8],[9,10] ]
1867 *
1868 * Exmample: wpbc_intervals__merge_inersected( [ [1,3],[2,4],[6,8],[9,10],[3,7] ] );
1869 */
1870 function wpbc_intervals__merge_inersected( intervals ){
1871
1872 if ( ! intervals || intervals.length === 0 ){
1873 return [];
1874 }
1875
1876 var merged = [];
1877 intervals.sort( function ( a, b ){
1878 return a[ 0 ] - b[ 0 ];
1879 } );
1880
1881 var mergedInterval = intervals[ 0 ];
1882
1883 for ( var i = 1; i < intervals.length; i++ ){
1884 var interval = intervals[ i ];
1885
1886 if ( interval[ 0 ] <= mergedInterval[ 1 ] ){
1887 mergedInterval[ 1 ] = Math.max( mergedInterval[ 1 ], interval[ 1 ] );
1888 } else {
1889 merged.push( mergedInterval );
1890 mergedInterval = interval;
1891 }
1892 }
1893
1894 merged.push( mergedInterval );
1895 return merged;
1896 }
1897
1898
1899 /**
1900 * Is 2 intervals intersected: [36011, 86392] <=> [1, 43192] => true ( intersected )
1901 *
1902 * Good explanation here
1903 * https://stackoverflow.com/questions/3269434/whats-the-most-efficient-way-to-test-if-two-ranges-overlap
1904 *
1905 * @param interval_A - [ 36011, 86392 ]
1906 * @param interval_B - [ 1, 43192 ]
1907 *
1908 * @return bool
1909 */
1910 function wpbc_intervals__is_intersected( interval_A, interval_B ) {
1911
1912 if (
1913 ( 0 == interval_A.length )
1914 || ( 0 == interval_B.length )
1915 ){
1916 return false;
1917 }
1918
1919 interval_A[ 0 ] = parseInt( interval_A[ 0 ] );
1920 interval_A[ 1 ] = parseInt( interval_A[ 1 ] );
1921 interval_B[ 0 ] = parseInt( interval_B[ 0 ] );
1922 interval_B[ 1 ] = parseInt( interval_B[ 1 ] );
1923
1924 var is_intersected = Math.max( interval_A[ 0 ], interval_B[ 0 ] ) - Math.min( interval_A[ 1 ], interval_B[ 1 ] );
1925
1926 // if ( 0 == is_intersected ) {
1927 // // Such ranges going one after other, e.g.: [ 12, 15 ] and [ 15, 21 ]
1928 // }
1929
1930 if ( is_intersected < 0 ) {
1931 return true; // INTERSECTED
1932 }
1933
1934 return false; // Not intersected
1935 }
1936
1937
1938 /**
1939 * Get the closets ABS value of element in array to the current myValue
1940 *
1941 * @param myValue - int element to search closet 4
1942 * @param myArray - array of elements where to search [5,8,1,7]
1943 * @returns int 5
1944 */
1945 function wpbc_get_abs_closest_value_in_arr( myValue, myArray ){
1946
1947 if ( myArray.length == 0 ){ // If the array is empty -> return the myValue
1948 return myValue;
1949 }
1950
1951 var obj = myArray[ 0 ];
1952 var diff = Math.abs( myValue - obj ); // Get distance between 1st element
1953 var closetValue = myArray[ 0 ]; // Save 1st element
1954
1955 for ( var i = 1; i < myArray.length; i++ ){
1956 obj = myArray[ i ];
1957
1958 if ( Math.abs( myValue - obj ) < diff ){ // we found closer value -> save it
1959 diff = Math.abs( myValue - obj );
1960 closetValue = obj;
1961 }
1962 }
1963
1964 return closetValue;
1965 }
1966
1967
1968 // ---------------------------------------------------------------------------------------------------------------------
1969 /* == T O O L T I P S ==
1970 // --------------------------------------------------------------------------------------------------------------------- */
1971
1972 /**
1973 * Define tooltip to show, when mouse over Date in Calendar
1974 *
1975 * @param tooltip_text - Text to show 'Booked time: 12:00 - 13:00<br>Cost: $20.00'
1976 * @param resource_id - ID of booking resource '1'
1977 * @param td_class - SQL class '1-9-2023'
1978 * @returns {boolean} - defined to show or not
1979 */
1980 function wpbc_set_tooltip___for__calendar_date( tooltip_text, resource_id, td_class ){
1981
1982 //TODO: make escaping of text for quot symbols, and JS/HTML...
1983
1984 jQuery( '#calendar_booking' + resource_id + ' td.cal4date-' + td_class ).attr( 'data-content', tooltip_text );
1985
1986 var td_el = jQuery( '#calendar_booking' + resource_id + ' td.cal4date-' + td_class ).get( 0 ); // FixIn: 9.0.1.1.
1987
1988 if (
1989 ( 'undefined' !== typeof(td_el) )
1990 && ( undefined == td_el._tippy )
1991 && ( '' !== tooltip_text )
1992 ){
1993
1994 wpbc_tippy( td_el , {
1995 content( reference ){
1996
1997 var popover_content = reference.getAttribute( 'data-content' );
1998
1999 return '<div class="popover popover_tippy">'
2000 + '<div class="popover-content">'
2001 + popover_content
2002 + '</div>'
2003 + '</div>';
2004 },
2005 allowHTML : true,
2006 trigger : 'mouseenter focus',
2007 interactive : false,
2008 hideOnClick : true,
2009 interactiveBorder: 10,
2010 maxWidth : 550,
2011 theme : 'wpbc-tippy-times',
2012 placement : 'top',
2013 delay : [400, 0], // FixIn: 9.4.2.2.
2014 //delay : [0, 9999999999], // Debuge tooltip
2015 ignoreAttributes : true,
2016 touch : true, //['hold', 500], // 500ms delay // FixIn: 9.2.1.5.
2017 appendTo: () => document.body,
2018 });
2019
2020 return true;
2021 }
2022
2023 return false;
2024 }
2025
2026
2027 // ---------------------------------------------------------------------------------------------------------------------
2028 /* == Dates Functions ==
2029 // --------------------------------------------------------------------------------------------------------------------- */
2030
2031 /**
2032 * Get number of dates between 2 JS Dates
2033 *
2034 * @param date1 JS Date
2035 * @param date2 JS Date
2036 * @returns {number}
2037 */
2038 function wpbc_dates__days_between(date1, date2) {
2039
2040 // The number of milliseconds in one day
2041 var ONE_DAY = 1000 * 60 * 60 * 24;
2042
2043 // Convert both dates to milliseconds
2044 var date1_ms = date1.getTime();
2045 var date2_ms = date2.getTime();
2046
2047 // Calculate the difference in milliseconds
2048 var difference_ms = date1_ms - date2_ms;
2049
2050 // Convert back to days and return
2051 return Math.round(difference_ms/ONE_DAY);
2052 }
2053
2054
2055 /**
2056 * Check if this array of dates is consecutive array of dates or not.
2057 * e.g. ['2024-05-09','2024-05-19','2024-05-30'] -> false
2058 * e.g. ['2024-05-09','2024-05-10','2024-05-11'] -> true
2059 * @param sql_dates_arr array e.g.: ['2024-05-09','2024-05-19','2024-05-30']
2060 * @returns {boolean}
2061 */
2062 function wpbc_dates__is_consecutive_dates_arr_range( sql_dates_arr ){ // FixIn: 10.0.0.50.
2063
2064 if ( sql_dates_arr.length > 1 ){
2065 var previos_date = wpbc__get__js_date( sql_dates_arr[ 0 ] );
2066 var current_date;
2067
2068 for ( var i = 1; i < sql_dates_arr.length; i++ ){
2069 current_date = wpbc__get__js_date( sql_dates_arr[i] );
2070
2071 if ( wpbc_dates__days_between( current_date, previos_date ) != 1 ){
2072 return false;
2073 }
2074
2075 previos_date = current_date;
2076 }
2077 }
2078
2079 return true;
2080 }
2081
2082
2083 // ---------------------------------------------------------------------------------------------------------------------
2084 /* == Auto Dates Selection ==
2085 // --------------------------------------------------------------------------------------------------------------------- */
2086
2087 /**
2088 * == How to use ? ==
2089 *
2090 * For Dates selection, we need to use this logic! We need select the dates only after booking data loaded!
2091 *
2092 * Check example bellow.
2093 *
2094 * // Fire on all booking dates loaded
2095 * jQuery( 'body' ).on( 'wpbc_calendar_ajx__loaded_data', function ( event, loaded_resource_id ){
2096 *
2097 * if ( loaded_resource_id == select_dates_in_calendar_id ){
2098 * wpbc_auto_select_dates_in_calendar( select_dates_in_calendar_id, '2024-05-15', '2024-05-25' );
2099 * }
2100 * } );
2101 *
2102 */
2103
2104
2105 /**
2106 * Try to Auto select dates in specific calendar by simulated clicks in datepicker
2107 *
2108 * @param resource_id 1
2109 * @param check_in_ymd '2024-05-09' OR ['2024-05-09','2024-05-19','2024-05-20']
2110 * @param check_out_ymd '2024-05-15' Optional
2111 *
2112 * @returns {number} number of selected dates
2113 *
2114 * Example 1: var num_selected_days = wpbc_auto_select_dates_in_calendar( 1, '2024-05-15',
2115 * '2024-05-25' ); Example 2: var num_selected_days = wpbc_auto_select_dates_in_calendar( 1,
2116 * ['2024-05-09','2024-05-19','2024-05-20'] );
2117 */
2118 function wpbc_auto_select_dates_in_calendar( resource_id, check_in_ymd, check_out_ymd = '' ){ // FixIn: 10.0.0.47.
2119
2120 console.log( 'WPBC_AUTO_SELECT_DATES_IN_CALENDAR( RESOURCE_ID, CHECK_IN_YMD, CHECK_OUT_YMD )', resource_id, check_in_ymd, check_out_ymd );
2121
2122 if (
2123 ( '2100-01-01' == check_in_ymd )
2124 || ( '2100-01-01' == check_out_ymd )
2125 || ( ( '' == check_in_ymd ) && ( '' == check_out_ymd ) )
2126 ){
2127 return 0;
2128 }
2129
2130 // -----------------------------------------------------------------------------------------------------------------
2131 // If check_in_ymd = [ '2024-05-09','2024-05-19','2024-05-30' ] ARRAY of DATES // FixIn: 10.0.0.50.
2132 // -----------------------------------------------------------------------------------------------------------------
2133 var dates_to_select_arr = [];
2134 if ( Array.isArray( check_in_ymd ) ){
2135 dates_to_select_arr = wpbc_clone_obj( check_in_ymd );
2136
2137 // -------------------------------------------------------------------------------------------------------------
2138 // Exceptions to set MULTIPLE DAYS mode
2139 // -------------------------------------------------------------------------------------------------------------
2140 // if dates as NOT CONSECUTIVE: ['2024-05-09','2024-05-19','2024-05-30'], -> set MULTIPLE DAYS mode
2141 if (
2142 ( dates_to_select_arr.length > 0 )
2143 && ( '' == check_out_ymd )
2144 && ( ! wpbc_dates__is_consecutive_dates_arr_range( dates_to_select_arr ) )
2145 ){
2146 wpbc_cal_days_select__multiple( resource_id );
2147 }
2148 // if multiple days to select, but enabled SINGLE day mode, -> set MULTIPLE DAYS mode
2149 if (
2150 ( dates_to_select_arr.length > 1 )
2151 && ( '' == check_out_ymd )
2152 && ( 'single' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) )
2153 ){
2154 wpbc_cal_days_select__multiple( resource_id );
2155 }
2156 // -------------------------------------------------------------------------------------------------------------
2157 check_in_ymd = dates_to_select_arr[ 0 ];
2158 if ( '' == check_out_ymd ){
2159 check_out_ymd = dates_to_select_arr[ (dates_to_select_arr.length-1) ];
2160 }
2161 }
2162 // -----------------------------------------------------------------------------------------------------------------
2163
2164
2165 if ( '' == check_in_ymd ){
2166 check_in_ymd = check_out_ymd;
2167 }
2168 if ( '' == check_out_ymd ){
2169 check_out_ymd = check_in_ymd;
2170 }
2171
2172 if ( 'undefined' === typeof (resource_id) ){
2173 resource_id = '1';
2174 }
2175
2176
2177 var inst = wpbc_calendar__get_inst( resource_id );
2178
2179 if ( null !== inst ){
2180
2181 // Unselect all dates and set properties of Datepick
2182 jQuery( '#date_booking' + resource_id ).val( '' ); //FixIn: 5.4.3
2183 inst.stayOpen = false;
2184 inst.dates = [];
2185 var check_in_js = wpbc__get__js_date( check_in_ymd );
2186 var td_cell = wpbc_get_clicked_td( inst.id, check_in_js );
2187
2188 // Is ome type of error, then select multiple days selection mode.
2189 if ( '' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ) {
2190 _wpbc.calendar__set_param_value( resource_id, 'days_select_mode', 'multiple' );
2191 }
2192
2193
2194 // ---------------------------------------------------------------------------------------------------------
2195 // == DYNAMIC ==
2196 if ( 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ){
2197 // 1-st click
2198 inst.stayOpen = false;
2199 jQuery.datepick._selectDay( td_cell, '#' + inst.id, check_in_js.getTime() );
2200 if ( 0 === inst.dates.length ){
2201 return 0; // First click was unsuccessful, so we must not make other click
2202 }
2203
2204 // 2-nd click
2205 var check_out_js = wpbc__get__js_date( check_out_ymd );
2206 var td_cell_out = wpbc_get_clicked_td( inst.id, check_out_js );
2207 inst.stayOpen = true;
2208 jQuery.datepick._selectDay( td_cell_out, '#' + inst.id, check_out_js.getTime() );
2209 }
2210
2211 // ---------------------------------------------------------------------------------------------------------
2212 // == FIXED ==
2213 if ( 'fixed' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' )) {
2214 jQuery.datepick._selectDay( td_cell, '#' + inst.id, check_in_js.getTime() );
2215 }
2216
2217 // ---------------------------------------------------------------------------------------------------------
2218 // == SINGLE ==
2219 if ( 'single' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ){
2220 //jQuery.datepick._restrictMinMax( inst, jQuery.datepick._determineDate( inst, check_in_js, null ) ); // Do we need to run this ? Please note, check_in_js must have time, min, sec defined to 0!
2221 jQuery.datepick._selectDay( td_cell, '#' + inst.id, check_in_js.getTime() );
2222 }
2223
2224 // ---------------------------------------------------------------------------------------------------------
2225 // == MULTIPLE ==
2226 if ( 'multiple' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ){
2227
2228 var dates_arr;
2229
2230 if ( dates_to_select_arr.length > 0 ){
2231 // Situation, when we have dates array: ['2024-05-09','2024-05-19','2024-05-30']. and not the Check In / Check out dates as parameter in this function
2232 dates_arr = wpbc_get_selection_dates_js_str_arr__from_arr( dates_to_select_arr );
2233 } else {
2234 dates_arr = wpbc_get_selection_dates_js_str_arr__from_check_in_out( check_in_ymd, check_out_ymd, inst );
2235 }
2236
2237 if ( 0 === dates_arr.dates_js.length ){
2238 return 0;
2239 }
2240
2241 // For Calendar Days selection
2242 for ( var j = 0; j < dates_arr.dates_js.length; j++ ){ // Loop array of dates
2243
2244 var str_date = wpbc__get__sql_class_date( dates_arr.dates_js[ j ] );
2245
2246 // Date unavailable !
2247 if ( 0 == _wpbc.bookings_in_calendar__get_for_date( resource_id, str_date ).day_availability ){
2248 return 0;
2249 }
2250
2251 if ( dates_arr.dates_js[ j ] != -1 ) {
2252 inst.dates.push( dates_arr.dates_js[ j ] );
2253 }
2254 }
2255
2256 var check_out_date = dates_arr.dates_js[ (dates_arr.dates_js.length - 1) ];
2257
2258 inst.dates.push( check_out_date ); // Need add one additional SAME date for correct works of dates selection !!!!!
2259
2260 var checkout_timestamp = check_out_date.getTime();
2261 var td_cell = wpbc_get_clicked_td( inst.id, check_out_date );
2262
2263 jQuery.datepick._selectDay( td_cell, '#' + inst.id, checkout_timestamp );
2264 }
2265
2266
2267 if ( 0 !== inst.dates.length ){
2268 // Scroll to specific month, if we set dates in some future months
2269 wpbc_calendar__scroll_to( resource_id, inst.dates[ 0 ].getFullYear(), inst.dates[ 0 ].getMonth()+1 );
2270 }
2271
2272 return inst.dates.length;
2273 }
2274
2275 return 0;
2276 }
2277
2278 /**
2279 * Get HTML td element (where was click in calendar day cell)
2280 *
2281 * @param calendar_html_id 'calendar_booking1'
2282 * @param date_js JS Date
2283 * @returns {*|jQuery} Dom HTML td element
2284 */
2285 function wpbc_get_clicked_td( calendar_html_id, date_js ){
2286
2287 var td_cell = jQuery( '#' + calendar_html_id + ' .sql_date_' + wpbc__get__sql_class_date( date_js ) ).get( 0 );
2288
2289 return td_cell;
2290 }
2291
2292 /**
2293 * Get arrays of JS and SQL dates as dates array
2294 *
2295 * @param check_in_ymd '2024-05-15'
2296 * @param check_out_ymd '2024-05-25'
2297 * @param inst Datepick Inst. Use wpbc_calendar__get_inst( resource_id );
2298 * @returns {{dates_js: *[], dates_str: *[]}}
2299 */
2300 function wpbc_get_selection_dates_js_str_arr__from_check_in_out( check_in_ymd, check_out_ymd , inst ){
2301
2302 var original_array = [];
2303 var date;
2304 var bk_distinct_dates = [];
2305
2306 var check_in_date = check_in_ymd.split( '-' );
2307 var check_out_date = check_out_ymd.split( '-' );
2308
2309 date = new Date();
2310 date.setFullYear( check_in_date[ 0 ], (check_in_date[ 1 ] - 1), check_in_date[ 2 ] ); // year, month, date
2311 var original_check_in_date = date;
2312 original_array.push( jQuery.datepick._restrictMinMax( inst, jQuery.datepick._determineDate( inst, date, null ) ) ); //add date
2313 if ( ! wpbc_in_array( bk_distinct_dates, (check_in_date[ 2 ] + '.' + check_in_date[ 1 ] + '.' + check_in_date[ 0 ]) ) ){
2314 bk_distinct_dates.push( parseInt(check_in_date[ 2 ]) + '.' + parseInt(check_in_date[ 1 ]) + '.' + check_in_date[ 0 ] );
2315 }
2316
2317 var date_out = new Date();
2318 date_out.setFullYear( check_out_date[ 0 ], (check_out_date[ 1 ] - 1), check_out_date[ 2 ] ); // year, month, date
2319 var original_check_out_date = date_out;
2320
2321 var mewDate = new Date( original_check_in_date.getFullYear(), original_check_in_date.getMonth(), original_check_in_date.getDate() );
2322 mewDate.setDate( original_check_in_date.getDate() + 1 );
2323
2324 while (
2325 (original_check_out_date > date) &&
2326 (original_check_in_date != original_check_out_date) ){
2327 date = new Date( mewDate.getFullYear(), mewDate.getMonth(), mewDate.getDate() );
2328
2329 original_array.push( jQuery.datepick._restrictMinMax( inst, jQuery.datepick._determineDate( inst, date, null ) ) ); //add date
2330 if ( !wpbc_in_array( bk_distinct_dates, (date.getDate() + '.' + parseInt( date.getMonth() + 1 ) + '.' + date.getFullYear()) ) ){
2331 bk_distinct_dates.push( (parseInt(date.getDate()) + '.' + parseInt( date.getMonth() + 1 ) + '.' + date.getFullYear()) );
2332 }
2333
2334 mewDate = new Date( date.getFullYear(), date.getMonth(), date.getDate() );
2335 mewDate.setDate( mewDate.getDate() + 1 );
2336 }
2337 original_array.pop();
2338 bk_distinct_dates.pop();
2339
2340 return {'dates_js': original_array, 'dates_str': bk_distinct_dates};
2341 }
2342
2343 /**
2344 * Get arrays of JS and SQL dates as dates array
2345 *
2346 * @param dates_to_select_arr = ['2024-05-09','2024-05-19','2024-05-30']
2347 *
2348 * @returns {{dates_js: *[], dates_str: *[]}}
2349 */
2350 function wpbc_get_selection_dates_js_str_arr__from_arr( dates_to_select_arr ){ // FixIn: 10.0.0.50.
2351
2352 var original_array = [];
2353 var bk_distinct_dates = [];
2354 var one_date_str;
2355
2356 for ( var d = 0; d < dates_to_select_arr.length; d++ ){
2357
2358 original_array.push( wpbc__get__js_date( dates_to_select_arr[ d ] ) );
2359
2360 one_date_str = dates_to_select_arr[ d ].split('-')
2361 if ( ! wpbc_in_array( bk_distinct_dates, (one_date_str[ 2 ] + '.' + one_date_str[ 1 ] + '.' + one_date_str[ 0 ]) ) ){
2362 bk_distinct_dates.push( parseInt(one_date_str[ 2 ]) + '.' + parseInt(one_date_str[ 1 ]) + '.' + one_date_str[ 0 ] );
2363 }
2364 }
2365
2366 return {'dates_js': original_array, 'dates_str': original_array};
2367 }
2368
2369 // =====================================================================================================================
2370 /* == Auto Fill Fields / Auto Select Dates ==
2371 // ===================================================================================================================== */
2372
2373 jQuery( document ).ready( function (){
2374
2375 var url_params = new URLSearchParams( window.location.search );
2376
2377 // Disable days selection in calendar, after redirection from the "Search results page, after search availability" // FixIn: 8.8.2.3.
2378 if ( 'On' != _wpbc.get_other_param( 'is_enabled_booking_search_results_days_select' ) ) {
2379 if (
2380 ( url_params.has( 'wpbc_select_check_in' ) ) &&
2381 ( url_params.has( 'wpbc_select_check_out' ) ) &&
2382 ( url_params.has( 'wpbc_select_calendar_id' ) )
2383 ){
2384
2385 var select_dates_in_calendar_id = parseInt( url_params.get( 'wpbc_select_calendar_id' ) );
2386
2387 // Fire on all booking dates loaded
2388 jQuery( 'body' ).on( 'wpbc_calendar_ajx__loaded_data', function ( event, loaded_resource_id ){
2389
2390 if ( loaded_resource_id == select_dates_in_calendar_id ){
2391 wpbc_auto_select_dates_in_calendar( select_dates_in_calendar_id, url_params.get( 'wpbc_select_check_in' ), url_params.get( 'wpbc_select_check_out' ) );
2392 }
2393 } );
2394 }
2395 }
2396
2397 if (
2398 url_params.has( 'wpbc_auto_fill' )
2399 && ! jQuery( 'body' ).hasClass( 'wp-admin' )
2400 ){
2401
2402 var wpbc_auto_fill_value = url_params.get( 'wpbc_auto_fill' );
2403
2404 // Convert back. Some systems do not like symbol '~' in URL, so we need to replace to some other symbols
2405 wpbc_auto_fill_value = wpbc_auto_fill_value.replaceAll( '_^_', '~' );
2406
2407 wpbc_auto_fill_booking_fields( wpbc_auto_fill_value );
2408 }
2409
2410 } );
2411
2412 /**
2413 * Autofill / select booking form fields by values from the GET request parameter: ?wpbc_auto_fill=
2414 *
2415 * URL-provided field names are resolved through the exact-name DOM API so
2416 * they can never be interpreted as CSS selector syntax.
2417 *
2418 * @param {string} auto_fill_str Serialized field-name and value pairs.
2419 * @return {void}
2420 */
2421 function wpbc_auto_fill_booking_fields( auto_fill_str ){ // FixIn: 10.0.0.48.
2422
2423 if ( '' == auto_fill_str ){
2424 return;
2425 }
2426
2427 // console.log( 'WPBC_AUTO_FILL_BOOKING_FIELDS( AUTO_FILL_STR )', auto_fill_str);
2428
2429 var fields_arr = wpbc_auto_fill_booking_fields__parse( auto_fill_str );
2430
2431 for ( let i = 0; i < fields_arr.length; i++ ){
2432 var field_name = String( fields_arr[ i ][ 'name' ] || '' );
2433 if ( '' === field_name ) {
2434 continue;
2435 }
2436
2437 var field_elements = document.getElementsByName( field_name );
2438 if ( 0 < field_elements.length ) {
2439 jQuery( field_elements ).val( fields_arr[ i ][ 'value' ] );
2440 }
2441 }
2442 }
2443
2444 /**
2445 * Parse data from get parameter: ?wpbc_auto_fill=visitors231^2~max_capacity231^2
2446 *
2447 * @param data_str = 'visitors231^2~max_capacity231^2';
2448 * @returns {*}
2449 */
2450 function wpbc_auto_fill_booking_fields__parse( data_str ){
2451
2452 var filter_options_arr = [];
2453
2454 var data_arr = data_str.split( '~' );
2455
2456 for ( var j = 0; j < data_arr.length; j++ ){
2457
2458 var my_form_field = data_arr[ j ].split( '^' );
2459
2460 var filter_name = ('undefined' !== typeof (my_form_field[ 0 ])) ? my_form_field[ 0 ] : '';
2461 var filter_value = ('undefined' !== typeof (my_form_field[ 1 ])) ? my_form_field[ 1 ] : '';
2462
2463 filter_options_arr.push(
2464 {
2465 'name' : filter_name,
2466 'value' : filter_value
2467 }
2468 );
2469 }
2470 return filter_options_arr;
2471 }
2472
2473 /**
2474 * Parse data from get parameter: ?search_get__custom_params=...
2475 *
2476 * @param data_str = 'text^search_field__display_check_in^23.05.2024~text^search_field__display_check_out^26.05.2024~selectbox-one^search_quantity^2~selectbox-one^location^Spain~selectbox-one^max_capacity^2~selectbox-one^amenity^parking~checkbox^search_field__extend_search_days^5~submit^^Search~hidden^search_get__check_in_ymd^2024-05-23~hidden^search_get__check_out_ymd^2024-05-26~hidden^search_get__time^~hidden^search_get__quantity^2~hidden^search_get__extend^5~hidden^search_get__users_id^~hidden^search_get__custom_params^~';
2477 * @returns {*}
2478 */
2479 function wpbc_auto_fill_search_fields__parse( data_str ){
2480
2481 var filter_options_arr = [];
2482
2483 var data_arr = data_str.split( '~' );
2484
2485 for ( var j = 0; j < data_arr.length; j++ ){
2486
2487 var my_form_field = data_arr[ j ].split( '^' );
2488
2489 var filter_type = ('undefined' !== typeof (my_form_field[ 0 ])) ? my_form_field[ 0 ] : '';
2490 var filter_name = ('undefined' !== typeof (my_form_field[ 1 ])) ? my_form_field[ 1 ] : '';
2491 var filter_value = ('undefined' !== typeof (my_form_field[ 2 ])) ? my_form_field[ 2 ] : '';
2492
2493 filter_options_arr.push(
2494 {
2495 'type' : filter_type,
2496 'name' : filter_name,
2497 'value' : filter_value
2498 }
2499 );
2500 }
2501 return filter_options_arr;
2502 }
2503
2504
2505 // ---------------------------------------------------------------------------------------------------------------------
2506 /* == Auto Update number of months in calendars ON screen size changed ==
2507 // --------------------------------------------------------------------------------------------------------------------- */
2508
2509 /**
2510 * Auto Update Number of Months in Calendar, e.g.: if ( WINDOW_WIDTH <= 782px ) >>> MONTHS_NUMBER = 1
2511 * ELSE: number of months defined in shortcode.
2512 * @param resource_id int
2513 *
2514 */
2515 function wpbc_calendar__auto_update_months_number__on_resize( resource_id ){
2516
2517 if ( true === _wpbc.get_other_param( 'is_allow_several_months_on_mobile' ) ) {
2518 return false;
2519 }
2520
2521 var local__number_of_months = parseInt( _wpbc.calendar__get_param_value( resource_id, 'calendar_number_of_months' ) );
2522
2523 if ( local__number_of_months > 1 ){
2524
2525 if ( jQuery( window ).width() <= 782 ){
2526 wpbc_calendar__update_months_number( resource_id, 1 );
2527 } else {
2528 wpbc_calendar__update_months_number( resource_id, local__number_of_months );
2529 }
2530
2531 }
2532 }
2533
2534 /**
2535 * Auto Update Number of Months in ALL Calendars
2536 *
2537 */
2538 function wpbc_calendars__auto_update_months_number(){
2539
2540 var all_calendars_arr = _wpbc.calendars_all__get();
2541
2542 // This LOOP "for in" is GOOD, because we check here keys 'calendar_' === calendar_id.slice( 0, 9 )
2543 for ( var calendar_id in all_calendars_arr ){
2544 if ( 'calendar_' === calendar_id.slice( 0, 9 ) ){
2545 var resource_id = parseInt( calendar_id.slice( 9 ) ); // 'calendar_3' -> 3
2546 if ( resource_id > 0 ){
2547 wpbc_calendar__auto_update_months_number__on_resize( resource_id );
2548 }
2549 }
2550 }
2551 }
2552
2553 /**
2554 * If browser window changed, then update number of months.
2555 */
2556 jQuery( window ).on( 'resize', function (){
2557 wpbc_calendars__auto_update_months_number();
2558 } );
2559
2560 /**
2561 * Auto update calendar number of months on initial page load
2562 */
2563 jQuery( document ).ready( function (){
2564 var closed_timer = setTimeout( function (){
2565 wpbc_calendars__auto_update_months_number();
2566 }, 100 );
2567 });
2568
2569 // ---------------------------------------------------------------------------------------------------------------------
2570 /* == Check: calendar_dates_start: "2026-01-01", calendar_dates_end: "2026-12-31" == // FixIn: 10.13.1.4.
2571 // --------------------------------------------------------------------------------------------------------------------- */
2572 /**
2573 * Get Start JS Date of starting dates in calendar, from the _wpbc object.
2574 *
2575 * @param integer resource_id - resource ID, e.g.: 1.
2576 */
2577 function wpbc_calendar__get_dates_start( resource_id ) {
2578 return wpbc_calendar__get_date_parameter( resource_id, 'calendar_dates_start' );
2579 }
2580
2581 /**
2582 * Get End JS Date of ending dates in calendar, from the _wpbc object.
2583 *
2584 * @param integer resource_id - resource ID, e.g.: 1.
2585 */
2586 function wpbc_calendar__get_dates_end(resource_id) {
2587 return wpbc_calendar__get_date_parameter( resource_id, 'calendar_dates_end' );
2588 }
2589
2590 /**
2591 * Get validates date parameter.
2592 *
2593 * @param resource_id - 1
2594 * @param parameter_str - 'calendar_dates_start' | 'calendar_dates_end' | ...
2595 */
2596 function wpbc_calendar__get_date_parameter(resource_id, parameter_str) {
2597
2598 var date_expected_ymd = _wpbc.calendar__get_param_value( resource_id, parameter_str );
2599
2600 if ( ! date_expected_ymd ) {
2601 return false; // '' | 0 | null | undefined -> false.
2602 }
2603
2604 if ( -1 !== date_expected_ymd.indexOf( '-' ) ) {
2605
2606 var date_expected_ymd_arr = date_expected_ymd.split( '-' ); // '2025-07-26' -> ['2025', '07', '26']
2607
2608 if ( date_expected_ymd_arr.length > 0 ) {
2609 var year = (date_expected_ymd_arr.length > 0) ? parseInt( date_expected_ymd_arr[0] ) : new Date().getFullYear(); // Year.
2610 var month = (date_expected_ymd_arr.length > 1) ? (parseInt( date_expected_ymd_arr[1] ) - 1) : 0; // (month - 1) or 0 - Jan.
2611 var day = (date_expected_ymd_arr.length > 2) ? parseInt( date_expected_ymd_arr[2] ) : 1; // date or Otherwise 1st of month
2612
2613 var date_js = new Date( year, month, day, 0, 0, 0, 0 );
2614
2615 return date_js;
2616 }
2617 }
2618
2619 return false; // Fallback, if we not parsed this parameter 'calendar_dates_start' = '2025-07-26', for example because of 'calendar_dates_start' = 'sfsdf'.
2620 }
2621