PluginProbe
Booking Calendar / 10.15.6
Booking Calendar v10.15.6
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 10.15.6, at includes/__js/client/cal/wpbc_cal.js

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