PluginProbe
Booking Calendar / 11.6.1
Booking Calendar v11.6.1
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 10.11.2 All 203 releases
booking / includes / __js / client / cal / wpbc_cal.js

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

2,553 lines 97.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__hide_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 * Remove the range-selection guidance belonging to one calendar.
709 *
710 * @param {number|string} resource_id Booking resource ID.
711 */
712 function wpbc_range_selection__hide_guidance( resource_id ){
713 jQuery( '#wpbc_range_selection_guidance' + resource_id ).remove();
714 }
715
716
717 /**
718 * Show a persistent informational note after the first range click.
719 * The calendar parameter can be set to false by PHP or JavaScript to suppress this UI per resource.
720 *
721 * @param {number|string} resource_id Booking resource ID.
722 * @returns {boolean} True when the note was inserted.
723 */
724 function wpbc_range_selection__show_guidance( resource_id ){
725
726 var is_enabled = _wpbc.calendar__get_param_value( resource_id, 'range_selection_guidance_is_enabled' );
727 var $booking_form = jQuery( '#booking_form_div' + resource_id );
728 var $calendar_frame;
729 var $note;
730
731 wpbc_range_selection__hide_guidance( resource_id );
732
733 if (
734 false === is_enabled
735 || 0 === is_enabled
736 || '0' === is_enabled
737 || 'off' === String( is_enabled ).toLowerCase()
738 || 'false' === String( is_enabled ).toLowerCase()
739 || 'dynamic' !== _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' )
740 || 0 === $booking_form.length
741 ) {
742 return false;
743 }
744
745 $calendar_frame = $booking_form.find( '.block_hints.datepick' ).first();
746 if ( 0 === $calendar_frame.length ) {
747 $calendar_frame = $booking_form.find( '.wpbc_cal_powered_by' ).first();
748 if ( 0 === $calendar_frame.length ) {
749 $calendar_frame = $booking_form.find( '.bk_calendar_frame' ).first();
750 if ( 0 === $calendar_frame.length ) {
751 return false;
752 }
753 }
754 }
755
756 $note = jQuery( '<div>', {
757 'id': 'wpbc_range_selection_guidance' + resource_id,
758 'class': 'wpbc_range_selection_guidance wpbc_front_end__message wpbc_fe_message_info',
759 'role': 'status',
760 'aria-live': 'polite'
761 } );
762 $note.append( jQuery( '<i>', {
763 'class': 'menu_icon icon-1x wpbc_icn_info_outline',
764 'aria-hidden': 'true'
765 } ) );
766 $note.append( jQuery( '<span>' ).text( _wpbc.get_message( 'message_range_selection_click_last_date' ) ) );
767 $calendar_frame.after( $note );
768
769 return true;
770 }
771
772
773 /**
774 * Synchronize the guidance with Datepick's two-click range state.
775 *
776 * @param {number|string} resource_id Booking resource ID.
777 */
778 function wpbc_range_selection__sync_guidance( resource_id ){
779
780 var inst = wpbc_calendar__get_inst( resource_id );
781
782 if (
783 null !== inst
784 && 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' )
785 && true === inst.stayOpen
786 && 1 === inst.dates.length
787 && inst.dates[ 0 ]
788 ) {
789 wpbc_range_selection__show_guidance( resource_id );
790 return;
791 }
792
793 wpbc_range_selection__hide_guidance( resource_id );
794 }
795
796
797 /**
798 * Select calendar date cells
799 *
800 * @param date - JavaScript Date Obj: Mon Dec 11 2023 00:00:00
801 * GMT+0200 (Eastern European Standard Time)
802 * @param calendar_params_arr - Calendar Settings Object: {
803 * "resource_id": 4
804 * }
805 * @param datepick_this - this of datepick Obj
806 *
807 */
808 function wpbc__calendar__on_select_days( date, calendar_params_arr, datepick_this ){
809
810 var resource_id = ( 'undefined' !== typeof(calendar_params_arr[ 'resource_id' ]) ) ? calendar_params_arr[ 'resource_id' ] : '1'; // '1'
811
812 // Set unselectable, if only Availability Calendar here (and we do not insert Booking form by mistake).
813 var is_unselectable_calendar = ( jQuery( '#calendar_booking_unselectable' + resource_id ).length > 0); // FixIn: 8.0.1.2.
814 var is_booking_form_exist = ( jQuery( '#booking_form_div' + resource_id ).length > 0 );
815 if ( ( is_unselectable_calendar ) && ( ! is_booking_form_exist ) ){
816 wpbc_calendar__unselect_all_dates( resource_id ); // Unselect Dates
817 jQuery('.wpbc_only_calendar .popover_calendar_hover').remove(); // Hide all opened popovers
818 return false;
819 }
820
821 jQuery( '#date_booking' + resource_id ).val( date ); // Add selected dates to hidden textarea
822
823
824 if ( 'function' === typeof (wpbc__calendar__do_days_select__bs) ) {
825 wpbc__calendar__do_days_select__bs( date, resource_id );
826 } else if ( 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ) {
827 wpbc__calendar__do_days_select__unrestricted_range( date, resource_id );
828 }
829
830 wpbc_range_selection__sync_guidance( resource_id );
831
832 wpbc_disable_time_fields_in_booking_form( resource_id );
833
834 // Hook -- trigger day selection -----------------------------------------------------------------------------------
835 var mouse_clicked_dates = date; // Can be: "05.10.2023 - 07.10.2023" | "10.10.2023 - 10.10.2023" |
836 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", … ]
837 jQuery( ".booking_form_div" ).trigger( "date_selected", [ resource_id, mouse_clicked_dates, all_selected_dates_arr ] );
838 }
839
840 // Mark middle selected dates with 0.5 opacity // FixIn: 10.3.0.9.
841 jQuery( document ).ready( function (){
842 jQuery( ".booking_form_div" ).on( 'date_selected', function ( event, resource_id, date ){
843 if (
844 ( 'fixed' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ))
845 || ('dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ))
846 ){
847 var closed_timer = setTimeout( function (){
848 var middle_days_opacity = _wpbc.get_other_param( 'calendars__days_selection__middle_days_opacity' );
849 jQuery( '#calendar_booking' + resource_id + ' .datepick-current-day' ).not( ".selected_check_in_out" ).css( 'opacity', middle_days_opacity );
850 }, 10 );
851 }
852 } );
853 } );
854
855
856 /**
857 * -- T i m e F i e l d s start --------------------------------------------------------------------------
858 */
859
860 /**
861 * Disable time slots in booking form depend on selected dates and booked dates/times
862 *
863 * @param resource_id
864 */
865 function wpbc_disable_time_fields_in_booking_form( resource_id ){
866
867 /**
868 * 1. Get all time fields in the booking form as array of objects
869 * [
870 * { jquery_option: jQuery_Object {}
871 * name: 'rangetime2[]'
872 * times_as_seconds: [ 21600, 23400 ]
873 * value_option_24h: '06:00 - 06:30'
874 * }
875 * ...
876 * { jquery_option: jQuery_Object {}
877 * name: 'starttime2[]'
878 * times_as_seconds: [ 21600 ]
879 * value_option_24h: '06:00'
880 * }
881 * ]
882 */
883 var time_fields_obj_arr = wpbc_get__time_fields__in_booking_form__as_arr( resource_id );
884
885 // 2. Get all selected dates in SQL format like this [ "2023-08-23", "2023-08-24", "2023-08-25", ... ]
886 var selected_dates_arr = wpbc_get__selected_dates_sql__as_arr( resource_id );
887
888 // 3. Get child booking resources or single booking resource that exist in dates
889 var child_resources_arr = wpbc_clone_obj( _wpbc.booking__get_param_value( resource_id, 'resources_id_arr__in_dates' ) );
890
891 var sql_date;
892 var child_resource_id;
893 var merged_seconds;
894 var time_fields_obj;
895 var is_intersect;
896 var is_check_in;
897
898 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 );
899 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 );
900 var allow_past_context = _wpbc.get_other_param( 'this_page_allow_past' );
901 var edit_booking_hash_context = _wpbc.get_other_param( 'this_page_booking_hash' );
902 var is_allow_past_context =
903 ( '1' === String( allow_past_context ) )
904 || ( 1 === allow_past_context )
905 || ( true === allow_past_context )
906 || ( '' !== String( edit_booking_hash_context || '' ) )
907 || ( location.href.indexOf( 'booking_hash' ) > -1 )
908 || ( location.href.indexOf( 'allow_past' ) > -1 );
909
910 // 4. Loop all time Fields options // FixIn: 10.3.0.2.
911 for ( let field_key = 0; field_key < time_fields_obj_arr.length; field_key++ ){
912
913 time_fields_obj_arr[ field_key ].disabled = 0; // By default, this time field is not disabled.
914
915 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 {}}
916
917 // Loop all selected dates.
918 for ( var i = 0; i < selected_dates_arr.length; i++ ) {
919
920 // Get Date: '2023-08-18'.
921 sql_date = selected_dates_arr[i];
922
923 var is_time_in_past = is_allow_past_context ? false : wpbc_check_is_time_in_past( today_time__shift, sql_date, time_fields_obj );
924 // Exception for 'End Time' field, when selected several dates. // FixIn: 10.14.1.5.
925 if ( ( ! is_allow_past_context ) &&
926 ('On' !== _wpbc.calendar__get_param_value( resource_id, 'booking_recurrent_time' )) &&
927 (-1 !== time_fields_obj.name.indexOf( 'endtime' )) &&
928 (selected_dates_arr.length > 1)
929 ) {
930 is_time_in_past = wpbc_check_is_time_in_past( today_time__shift, selected_dates_arr[(selected_dates_arr.length - 1)], time_fields_obj );
931 }
932 if ( is_time_in_past ) {
933 // This time for selected date already in the past.
934 time_fields_obj_arr[field_key].disabled = 1;
935 break; // exist from Dates LOOP.
936 }
937 // FixIn: 9.9.0.31.
938 if (
939 ( 'Off' === _wpbc.calendar__get_param_value( resource_id, 'booking_recurrent_time' ) )
940 && ( selected_dates_arr.length>1 )
941 ){
942 //TODO: skip some fields checking if it's start / end time for mulple dates selection mode.
943 //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
944
945 if ( (0 == i) && (time_fields_obj[ 'name' ].indexOf( 'endtime' ) >= 0) ){
946 break;
947 }
948 if ( ( (selected_dates_arr.length-1) == i ) && (time_fields_obj[ 'name' ].indexOf( 'starttime' ) >= 0) ){
949 break;
950 }
951 }
952
953
954
955 var how_many_resources_intersected = 0;
956 // Loop all resources ID
957 // for ( var res_key in child_resources_arr ){ // FixIn: 10.3.0.2.
958 if ( null === child_resources_arr ) {
959 child_resources_arr = [];
960 }
961 for ( let res_key = 0; res_key < child_resources_arr.length; res_key++ ){
962
963 child_resource_id = child_resources_arr[ res_key ];
964
965 // _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" ]
966 // _wpbc.bookings_in_calendar__get_for_date(2,'2023-08-21')[2].booked_time_slots.merged_seconds = [ [ 25211, 27002 ], [ 36011, 86400 ] ]
967
968 if ( false !== _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_date ) ){
969 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 ] ]
970 } else {
971 merged_seconds = [];
972 }
973 if ( time_fields_obj.times_as_seconds.length > 1 ){
974 is_intersect = wpbc_is_intersect__range_time_interval( [
975 [
976 ( parseInt( time_fields_obj.times_as_seconds[0] ) + 20 ),
977 ( parseInt( time_fields_obj.times_as_seconds[1] ) - 20 )
978 ]
979 ]
980 , merged_seconds );
981 } else {
982 is_check_in = (-1 !== time_fields_obj.name.indexOf( 'start' ));
983 is_intersect = wpbc_is_intersect__one_time_interval(
984 ( ( is_check_in )
985 ? parseInt( time_fields_obj.times_as_seconds ) + 20
986 : parseInt( time_fields_obj.times_as_seconds ) - 20
987 )
988 , merged_seconds );
989 }
990 if (is_intersect){
991 how_many_resources_intersected++; // Increase
992 }
993
994 }
995
996 if ( child_resources_arr.length == how_many_resources_intersected ) {
997 // 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
998
999 time_fields_obj_arr[ field_key ].disabled = 1;
1000 break; // exist from Dates LOOP
1001 }
1002 }
1003 }
1004
1005
1006 // 5. Now we can disable time slot in HTML by using ( field.disabled == 1 ) property
1007 wpbc__html__time_field_options__set_disabled( time_fields_obj_arr );
1008
1009 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.
1010 }
1011
1012
1013 /**
1014 * Check if specific time(-slot) already in the past for selected date
1015 *
1016 * @param js_current_time_to_check - JS Date
1017 * @param sql_date - '2025-01-26'
1018 * @param time_fields_obj - Object
1019 * @returns {boolean}
1020 */
1021 function wpbc_check_is_time_in_past( js_current_time_to_check, sql_date, time_fields_obj ) {
1022
1023 // FixIn: 10.9.6.4
1024 var sql_date_arr = sql_date.split( '-' );
1025 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 );
1026 var sql_date__midnight_miliseconds = sql_date__midnight.getTime();
1027
1028 var is_intersect = false;
1029
1030 if ( time_fields_obj.times_as_seconds.length > 1 ) {
1031
1032 if ( js_current_time_to_check.getTime() > (sql_date__midnight_miliseconds + (parseInt( time_fields_obj.times_as_seconds[0] ) + 20) * 1000) ) {
1033 is_intersect = true;
1034 }
1035 if ( js_current_time_to_check.getTime() > (sql_date__midnight_miliseconds + (parseInt( time_fields_obj.times_as_seconds[1] ) - 20) * 1000) ) {
1036 is_intersect = true;
1037 }
1038
1039 } else {
1040 var is_check_in = (-1 !== time_fields_obj.name.indexOf( 'start' ));
1041
1042 var times_as_seconds_check = (is_check_in) ? parseInt( time_fields_obj.times_as_seconds ) + 20 : parseInt( time_fields_obj.times_as_seconds ) - 20;
1043
1044 times_as_seconds_check = sql_date__midnight_miliseconds + times_as_seconds_check * 1000;
1045
1046 if ( js_current_time_to_check.getTime() > times_as_seconds_check ) {
1047 is_intersect = true;
1048 }
1049 }
1050
1051 return is_intersect;
1052 }
1053
1054 /**
1055 * Is number inside /intersect of array of intervals ?
1056 *
1057 * @param time_A - 25800
1058 * @param time_interval_B - [ [ 25211, 27002 ], [ 36011, 86400 ] ]
1059 * @returns {boolean}
1060 */
1061 function wpbc_is_intersect__one_time_interval( time_A, time_interval_B ){
1062
1063 for ( var j = 0; j < time_interval_B.length; j++ ){
1064
1065 if ( (parseInt( time_A ) > parseInt( time_interval_B[ j ][ 0 ] )) && (parseInt( time_A ) < parseInt( time_interval_B[ j ][ 1 ] )) ){
1066 return true
1067 }
1068
1069 // if ( ( parseInt( time_A ) == parseInt( time_interval_B[ j ][ 0 ] ) ) || ( parseInt( time_A ) == parseInt( time_interval_B[ j ][ 1 ] ) ) ) {
1070 // // Time A just at the border of interval
1071 // }
1072 }
1073
1074 return false;
1075 }
1076
1077 /**
1078 * Is these array of intervals intersected ?
1079 *
1080 * @param time_interval_A - [ [ 21600, 23400 ] ]
1081 * @param time_interval_B - [ [ 25211, 27002 ], [ 36011, 86400 ] ]
1082 * @returns {boolean}
1083 */
1084 function wpbc_is_intersect__range_time_interval( time_interval_A, time_interval_B ){
1085
1086 var is_intersect;
1087
1088 for ( var i = 0; i < time_interval_A.length; i++ ){
1089
1090 for ( var j = 0; j < time_interval_B.length; j++ ){
1091
1092 is_intersect = wpbc_intervals__is_intersected( time_interval_A[ i ], time_interval_B[ j ] );
1093
1094 if ( is_intersect ){
1095 return true;
1096 }
1097 }
1098 }
1099
1100 return false;
1101 }
1102
1103 /**
1104 * Get all time fields in the booking form as array of objects
1105 *
1106 * @param resource_id
1107 * @returns []
1108 *
1109 * Example:
1110 * [
1111 * {
1112 * value_option_24h: '06:00 - 06:30'
1113 * times_as_seconds: [ 21600, 23400 ]
1114 * jquery_option: jQuery_Object {}
1115 * name: 'rangetime2[]'
1116 * }
1117 * ...
1118 * {
1119 * value_option_24h: '06:00'
1120 * times_as_seconds: [ 21600 ]
1121 * jquery_option: jQuery_Object {}
1122 * name: 'starttime2[]'
1123 * }
1124 * ]
1125 */
1126 function wpbc_get__time_fields__in_booking_form__as_arr( resource_id ){
1127 /**
1128 * Fields with [] like this select[name="rangetime1[]"]
1129 * it's when we have 'multiple' in shortcode: [select* rangetime multiple "06:00 - 06:30" ... ]
1130 */
1131 var time_fields_arr=[
1132 'select[name="rangetime' + resource_id + '"]',
1133 'select[name="rangetime' + resource_id + '[]"]',
1134 'select[name="starttime' + resource_id + '"]',
1135 'select[name="starttime' + resource_id + '[]"]',
1136 'select[name="endtime' + resource_id + '"]',
1137 'select[name="endtime' + resource_id + '[]"]'
1138 ];
1139
1140 var time_fields_obj_arr = [];
1141
1142 // Loop all Time Fields
1143 for ( var ctf= 0; ctf < time_fields_arr.length; ctf++ ){
1144
1145 var time_field = time_fields_arr[ ctf ];
1146 var time_option = jQuery( time_field + ' option' );
1147
1148 // Loop all options in time field
1149 for ( var j = 0; j < time_option.length; j++ ){
1150
1151 var jquery_option = jQuery( time_field + ' option:eq(' + j + ')' );
1152 var value_option_seconds_arr = jquery_option.val().split( '-' );
1153 var times_as_seconds = [];
1154
1155 // Get time as seconds
1156 if ( value_option_seconds_arr.length ){ // FixIn: 9.8.10.1.
1157 for ( let i = 0; i < value_option_seconds_arr.length; i++ ){ // FixIn: 10.0.0.56.
1158 // value_option_seconds_arr[i] = '14:00 ' | ' 16:00' (if from 'rangetime') and '16:00' if (start/end time)
1159
1160 var start_end_times_arr = value_option_seconds_arr[ i ].trim().split( ':' );
1161
1162 var time_in_seconds = parseInt( start_end_times_arr[ 0 ] ) * 60 * 60 + parseInt( start_end_times_arr[ 1 ] ) * 60;
1163
1164 times_as_seconds.push( time_in_seconds );
1165 }
1166 }
1167
1168 time_fields_obj_arr.push( {
1169 'name' : jQuery( time_field ).attr( 'name' ),
1170 'value_option_24h': jquery_option.val(),
1171 'jquery_option' : jquery_option,
1172 'times_as_seconds': times_as_seconds
1173 } );
1174 }
1175 }
1176
1177 return time_fields_obj_arr;
1178 }
1179
1180 /**
1181 * Disable HTML options and add booked CSS class
1182 *
1183 * @param time_fields_obj_arr - this value is from the func:
1184 * wpbc_get__time_fields__in_booking_form__as_arr( resource_id )
1185 * [
1186 * { jquery_option: jQuery_Object {}
1187 * name: 'rangetime2[]'
1188 * times_as_seconds: [ 21600, 23400 ]
1189 * value_option_24h: '06:00 - 06:30'
1190 * disabled = 1
1191 * }
1192 * ...
1193 * { jquery_option: jQuery_Object {}
1194 * name: 'starttime2[]'
1195 * times_as_seconds: [ 21600 ]
1196 * value_option_24h: '06:00'
1197 * disabled = 0
1198 * }
1199 * ]
1200 *
1201 */
1202 function wpbc__html__time_field_options__set_disabled( time_fields_obj_arr ){
1203
1204 var jquery_option;
1205
1206 for ( var i = 0; i < time_fields_obj_arr.length; i++ ){
1207
1208 var jquery_option = time_fields_obj_arr[ i ].jquery_option;
1209
1210 if ( 1 == time_fields_obj_arr[ i ].disabled ){
1211 jquery_option.prop( 'disabled', true ); // Make disable some options
1212 jquery_option.addClass( 'booked' ); // Add "booked" CSS class
1213
1214 // if this booked element selected --> then deselect it
1215 if ( jquery_option.prop( 'selected' ) ){
1216 jquery_option.prop( 'selected', false );
1217
1218 jquery_option.parent().find( 'option:not([disabled]):first' ).prop( 'selected', true ).trigger( "change" );
1219 }
1220
1221 } else {
1222 jquery_option.prop( 'disabled', false ); // Make active all times
1223 jquery_option.removeClass( 'booked' ); // Remove class "booked"
1224 }
1225 }
1226
1227 }
1228
1229 /**
1230 * Check if this time_range | Time_Slot is Full Day booked
1231 *
1232 * @param timeslot_arr_in_seconds - [ 36011, 86400 ]
1233 * @returns {boolean}
1234 */
1235 function wpbc_is_this_timeslot__full_day_booked( timeslot_arr_in_seconds ){
1236
1237 if (
1238 ( timeslot_arr_in_seconds.length > 1 )
1239 && ( parseInt( timeslot_arr_in_seconds[ 0 ] ) < 30 )
1240 && ( parseInt( timeslot_arr_in_seconds[ 1 ] ) > ( (24 * 60 * 60) - 30) )
1241 ){
1242 return true;
1243 }
1244
1245 return false;
1246 }
1247
1248
1249 // -----------------------------------------------------------------------------------------------------------------
1250 /* == S e l e c t e d D a t e s / T i m e - F i e l d s ==
1251 // ----------------------------------------------------------------------------------------------------------------- */
1252
1253 /**
1254 * Get all selected dates in SQL format like this [ "2023-08-23", "2023-08-24" , ... ]
1255 *
1256 * @param resource_id
1257 * @returns {[]} [ "2023-08-23", "2023-08-24", "2023-08-25", "2023-08-26", "2023-08-27", "2023-08-28",
1258 * "2023-08-29" ]
1259 */
1260 function wpbc_get__selected_dates_sql__as_arr( resource_id ){
1261
1262 var selected_dates_arr = [];
1263 selected_dates_arr = jQuery( '#date_booking' + resource_id ).val().split(',');
1264
1265 if ( selected_dates_arr.length ){ // FixIn: 9.8.10.1.
1266 for ( let i = 0; i < selected_dates_arr.length; i++ ){ // FixIn: 10.0.0.56.
1267 selected_dates_arr[ i ] = selected_dates_arr[ i ].trim();
1268 selected_dates_arr[ i ] = selected_dates_arr[ i ].split( '.' );
1269 if ( selected_dates_arr[ i ].length > 1 ){
1270 selected_dates_arr[ i ] = selected_dates_arr[ i ][ 2 ] + '-' + selected_dates_arr[ i ][ 1 ] + '-' + selected_dates_arr[ i ][ 0 ];
1271 }
1272 }
1273 }
1274
1275 // Remove empty elements from an array
1276 selected_dates_arr = selected_dates_arr.filter( function ( n ){ return parseInt(n); } );
1277
1278 selected_dates_arr.sort();
1279
1280 return selected_dates_arr;
1281 }
1282
1283
1284 /**
1285 * Get all time fields in the booking form as array of objects
1286 *
1287 * @param resource_id
1288 * @param is_only_selected_time
1289 * @returns []
1290 *
1291 * Example:
1292 * [
1293 * {
1294 * value_option_24h: '06:00 - 06:30'
1295 * times_as_seconds: [ 21600, 23400 ]
1296 * jquery_option: jQuery_Object {}
1297 * name: 'rangetime2[]'
1298 * }
1299 * ...
1300 * {
1301 * value_option_24h: '06:00'
1302 * times_as_seconds: [ 21600 ]
1303 * jquery_option: jQuery_Object {}
1304 * name: 'starttime2[]'
1305 * }
1306 * ]
1307 */
1308 function wpbc_get__selected_time_fields__in_booking_form__as_arr( resource_id, is_only_selected_time = true ){
1309 /**
1310 * Fields with [] like this select[name="rangetime1[]"]
1311 * it's when we have 'multiple' in shortcode: [select* rangetime multiple "06:00 - 06:30" ... ]
1312 */
1313 var time_fields_arr=[
1314 'select[name="rangetime' + resource_id + '"]',
1315 'select[name="rangetime' + resource_id + '[]"]',
1316 'select[name="starttime' + resource_id + '"]',
1317 'select[name="starttime' + resource_id + '[]"]',
1318 'select[name="endtime' + resource_id + '"]',
1319 'select[name="endtime' + resource_id + '[]"]',
1320 'select[name="durationtime' + resource_id + '"]',
1321 'select[name="durationtime' + resource_id + '[]"]'
1322 ];
1323
1324 var time_fields_obj_arr = [];
1325
1326 // Loop all Time Fields
1327 for ( var ctf= 0; ctf < time_fields_arr.length; ctf++ ){
1328
1329 var time_field = time_fields_arr[ ctf ];
1330
1331 var time_option;
1332 if ( is_only_selected_time ){
1333 time_option = jQuery( '#booking_form' + resource_id + ' ' + time_field + ' option:selected' ); // Exclude conditional fields, because of using '#booking_form3 ...'
1334 } else {
1335 time_option = jQuery( '#booking_form' + resource_id + ' ' + time_field + ' option' ); // All time fields
1336 }
1337
1338
1339 // Loop all options in time field
1340 for ( var j = 0; j < time_option.length; j++ ){
1341
1342 var jquery_option = jQuery( time_option[ j ] ); // Get only selected options //jQuery( time_field + ' option:eq(' + j + ')' );
1343 var value_option_seconds_arr = jquery_option.val().split( '-' );
1344 var times_as_seconds = [];
1345
1346 // Get time as seconds
1347 if ( value_option_seconds_arr.length ){ // FixIn: 9.8.10.1.
1348 for ( let i = 0; i < value_option_seconds_arr.length; i++ ){ // FixIn: 10.0.0.56.
1349 // value_option_seconds_arr[i] = '14:00 ' | ' 16:00' (if from 'rangetime') and '16:00' if (start/end time)
1350
1351 var start_end_times_arr = value_option_seconds_arr[ i ].trim().split( ':' );
1352
1353 var time_in_seconds = parseInt( start_end_times_arr[ 0 ] ) * 60 * 60 + parseInt( start_end_times_arr[ 1 ] ) * 60;
1354
1355 times_as_seconds.push( time_in_seconds );
1356 }
1357 }
1358
1359 time_fields_obj_arr.push( {
1360 'name' : jQuery( '#booking_form' + resource_id + ' ' + time_field ).attr( 'name' ),
1361 'value_option_24h': jquery_option.val(),
1362 'jquery_option' : jquery_option,
1363 'times_as_seconds': times_as_seconds
1364 } );
1365 }
1366 }
1367
1368 // Text: [starttime] - [endtime] -----------------------------------------------------------------------------
1369
1370 var text_time_fields_arr=[
1371 'input[name="starttime' + resource_id + '"]',
1372 'input[name="endtime' + resource_id + '"]',
1373 ];
1374 for ( var tf= 0; tf < text_time_fields_arr.length; tf++ ){
1375
1376 var text_jquery = jQuery( '#booking_form' + resource_id + ' ' + text_time_fields_arr[ tf ] ); // Exclude conditional fields, because of using '#booking_form3 ...'
1377 if ( text_jquery.length > 0 ){
1378
1379 var time__h_m__arr = text_jquery.val().trim().split( ':' ); // '14:00'
1380 if ( 0 == time__h_m__arr.length ){
1381 continue; // Not entered time value in a field
1382 }
1383 if ( 1 == time__h_m__arr.length ){
1384 if ( '' === time__h_m__arr[ 0 ] ){
1385 continue; // Not entered time value in a field
1386 }
1387 time__h_m__arr[ 1 ] = 0;
1388 }
1389 var text_time_in_seconds = parseInt( time__h_m__arr[ 0 ] ) * 60 * 60 + parseInt( time__h_m__arr[ 1 ] ) * 60;
1390
1391 var text_times_as_seconds = [];
1392 text_times_as_seconds.push( text_time_in_seconds );
1393
1394 time_fields_obj_arr.push( {
1395 'name' : text_jquery.attr( 'name' ),
1396 'value_option_24h': text_jquery.val(),
1397 'jquery_option' : text_jquery,
1398 'times_as_seconds': text_times_as_seconds
1399 } );
1400 }
1401 }
1402
1403 return time_fields_obj_arr;
1404 }
1405
1406
1407
1408 // ---------------------------------------------------------------------------------------------------------------------
1409 /* == S U P P O R T for C A L E N D A R ==
1410 // --------------------------------------------------------------------------------------------------------------------- */
1411
1412 /**
1413 * Get Calendar datepick Instance.
1414 *
1415 * @param {int|string} resource_id
1416 * @returns {*|null}
1417 */
1418 function wpbc_calendar__get_inst(resource_id) {
1419
1420 if ( 'undefined' === typeof (resource_id) ) {
1421 resource_id = '1';
1422 }
1423
1424 if ( jQuery( '#calendar_booking' + resource_id ).length > 0 ) {
1425
1426 try {
1427 var inst = jQuery.datepick._getInst( jQuery( '#calendar_booking' + resource_id ).get( 0 ) );
1428 return inst ? inst : null;
1429 } catch ( e ) {
1430 return null;
1431 }
1432 }
1433
1434 return null;
1435 }
1436
1437
1438 /**
1439 * Unselect all dates in calendar and visually update this calendar
1440 *
1441 * @param resource_id ID of booking resource
1442 * @returns {boolean} true on success | false, if no such calendar
1443 */
1444 function wpbc_calendar__unselect_all_dates( resource_id ){
1445
1446 if ( 'undefined' === typeof (resource_id) ){
1447 resource_id = '1';
1448 }
1449
1450 wpbc_range_selection__hide_guidance( resource_id );
1451
1452 var inst = wpbc_calendar__get_inst( resource_id )
1453
1454 if ( null !== inst ){
1455
1456 // Unselect all dates and set properties of Datepick
1457 jQuery( '#date_booking' + resource_id ).val( '' ); //FixIn: 5.4.3
1458 inst.stayOpen = false;
1459 inst.dates = [];
1460 jQuery.datepick._updateDatepick( inst );
1461
1462 return true
1463 }
1464
1465 return false;
1466
1467 }
1468
1469 /**
1470 * Clear days highlighting in All or specific Calendars
1471 *
1472 * @param resource_id - can be skiped to clear highlighting in all calendars
1473 */
1474 function wpbc_calendars__clear_days_highlighting( resource_id ){
1475
1476 if ( 'undefined' !== typeof ( resource_id ) ){
1477
1478 jQuery( '#calendar_booking' + resource_id + ' .datepick-days-cell-over' ).removeClass( 'datepick-days-cell-over' ); // Clear in specific calendar
1479
1480 } else {
1481 jQuery( '.datepick-days-cell-over' ).removeClass( 'datepick-days-cell-over' ); // Clear in all calendars
1482 }
1483 }
1484
1485 /**
1486 * Scroll to specific month in calendar
1487 *
1488 * @param resource_id ID of resource
1489 * @param year - real year - 2023
1490 * @param month - real month - 12
1491 * @returns {boolean}
1492 */
1493 function wpbc_calendar__scroll_to( resource_id, year, month ){
1494
1495 if ( 'undefined' === typeof (resource_id) ){ resource_id = '1'; }
1496 var inst = wpbc_calendar__get_inst( resource_id )
1497 if ( null !== inst ){
1498
1499 year = parseInt( year );
1500 month = parseInt( month ) - 1; // In JS date, month -1
1501
1502 inst.cursorDate = new Date();
1503 // In some cases, the setFullYear can set only Year, and not the Month and day // FixIn: 6.2.3.5.
1504 inst.cursorDate.setFullYear( year, month, 1 );
1505 inst.cursorDate.setMonth( month );
1506 inst.cursorDate.setDate( 1 );
1507
1508 inst.drawMonth = inst.cursorDate.getMonth();
1509 inst.drawYear = inst.cursorDate.getFullYear();
1510
1511 jQuery.datepick._notifyChange( inst );
1512 jQuery.datepick._adjustInstDate( inst );
1513 jQuery.datepick._showDate( inst );
1514 jQuery.datepick._updateDatepick( inst );
1515
1516 return true;
1517 }
1518 return false;
1519 }
1520
1521 /**
1522 * Is this date selectable in calendar (mainly it's means AVAILABLE date)
1523 *
1524 * @param {int|string} resource_id 1
1525 * @param {string} sql_class_day '2023-08-11'
1526 * @returns {boolean} true | false
1527 */
1528 function wpbc_is_this_day_selectable( resource_id, sql_class_day ){
1529
1530 // Get Data --------------------------------------------------------------------------------------------------------
1531 var date_bookings_obj = _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_class_day );
1532 if ( ! date_bookings_obj || 'undefined' === typeof ( date_bookings_obj[ 'day_availability' ] ) ) {
1533 return false;
1534 }
1535
1536 var is_day_selectable = ( parseInt( date_bookings_obj[ 'day_availability' ] ) > 0 );
1537
1538 if ( typeof (date_bookings_obj[ 'summary' ]) === 'undefined' ){
1539 return is_day_selectable;
1540 }
1541
1542 if ( 'available' != date_bookings_obj[ 'summary']['status_for_day' ] ){
1543
1544 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.
1545 var booking_statuses_arr = wpbc_get_booking_statuses__as_arr( date_bookings_obj );
1546
1547 if (
1548 ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending' ) )
1549 || ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_pending' ) )
1550 || ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_approved' ) )
1551 || ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved_pending' ) )
1552 ){
1553 is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable;
1554 }
1555 }
1556
1557 return is_day_selectable;
1558 }
1559
1560 /**
1561 * Is date to check IN array of selected dates
1562 *
1563 * @param {date}js_date_to_check - JS Date - simple JavaScript Date object
1564 * @param {[]} js_dates_arr - [ JSDate, ... ] - array of JS dates
1565 * @returns {boolean}
1566 */
1567 function wpbc_is_this_day_among_selected_days( js_date_to_check, js_dates_arr ){
1568
1569 for ( var date_index = 0; date_index < js_dates_arr.length ; date_index++ ){ // FixIn: 8.4.5.16.
1570 if ( ( js_dates_arr[ date_index ].getFullYear() === js_date_to_check.getFullYear() ) &&
1571 ( js_dates_arr[ date_index ].getMonth() === js_date_to_check.getMonth() ) &&
1572 ( js_dates_arr[ date_index ].getDate() === js_date_to_check.getDate() ) ) {
1573 return true;
1574 }
1575 }
1576
1577 return false;
1578 }
1579
1580 /**
1581 * Get SQL Class Date '2023-08-01' from JS Date
1582 *
1583 * @param date JS Date
1584 * @returns {string} '2023-08-12'
1585 */
1586 function wpbc__get__sql_class_date( date ){
1587
1588 var sql_class_day = date.getFullYear() + '-';
1589 sql_class_day += ( ( date.getMonth() + 1 ) < 10 ) ? '0' : '';
1590 sql_class_day += ( date.getMonth() + 1 ) + '-'
1591 sql_class_day += ( date.getDate() < 10 ) ? '0' : '';
1592 sql_class_day += date.getDate();
1593
1594 return sql_class_day;
1595 }
1596
1597 /**
1598 * Get JS Date from the SQL date format '2024-05-14'
1599 * @param sql_class_date
1600 * @returns {Date}
1601 */
1602 function wpbc__get__js_date( sql_class_date ){
1603
1604 var sql_class_date_arr = sql_class_date.split( '-' );
1605
1606 var date_js = new Date();
1607
1608 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
1609
1610 // Without this time adjust Dates selection in Datepicker can not work!!!
1611 date_js.setHours(0);
1612 date_js.setMinutes(0);
1613 date_js.setSeconds(0);
1614 date_js.setMilliseconds(0);
1615
1616 return date_js;
1617 }
1618
1619 /**
1620 * Get TD Class Date '1-31-2023' from JS Date
1621 *
1622 * @param date JS Date
1623 * @returns {string} '1-31-2023'
1624 */
1625 function wpbc__get__td_class_date( date ){
1626
1627 var td_class_day = (date.getMonth() + 1) + '-' + date.getDate() + '-' + date.getFullYear(); // '1-9-2023'
1628
1629 return td_class_day;
1630 }
1631
1632 /**
1633 * Get date params from string date
1634 *
1635 * @param date string date like '31.5.2023'
1636 * @param separator default '.' can be skipped.
1637 * @returns { {date: number, month: number, year: number} }
1638 */
1639 function wpbc__get__date_params__from_string_date( date , separator){
1640
1641 separator = ( 'undefined' !== typeof (separator) ) ? separator : '.';
1642
1643 var date_arr = date.split( separator );
1644 var date_obj = {
1645 'year' : parseInt( date_arr[ 2 ] ),
1646 'month': (parseInt( date_arr[ 1 ] ) - 1),
1647 'date' : parseInt( date_arr[ 0 ] )
1648 };
1649 return date_obj; // for = new Date( date_obj.year , date_obj.month , date_obj.date );
1650 }
1651
1652 /**
1653 * Add Spin Loader to calendar
1654 * @param resource_id
1655 */
1656 function wpbc_calendar__loading__start( resource_id ){
1657 if ( ! jQuery( '#calendar_booking' + resource_id ).next().hasClass( 'wpbc_spins_loader_wrapper' ) ){
1658 jQuery( '#calendar_booking' + resource_id ).after( '<div class="wpbc_spins_loader_wrapper"><div class="wpbc_spin_loader_one_new"></div></div>' );
1659 }
1660 if ( ! jQuery( '#calendar_booking' + resource_id ).hasClass( 'wpbc_calendar_blur_small' ) ){
1661 jQuery( '#calendar_booking' + resource_id ).addClass( 'wpbc_calendar_blur_small' );
1662 }
1663 wpbc_calendar__blur__start( resource_id );
1664 }
1665
1666 /**
1667 * Remove Spin Loader to calendar
1668 * @param resource_id
1669 */
1670 function wpbc_calendar__loading__stop( resource_id ){
1671 jQuery( '#calendar_booking' + resource_id + ' + .wpbc_spins_loader_wrapper' ).remove();
1672 jQuery( '#calendar_booking' + resource_id ).removeClass( 'wpbc_calendar_blur_small' );
1673 wpbc_calendar__blur__stop( resource_id );
1674 }
1675
1676 /**
1677 * Add Blur to calendar
1678 * @param resource_id
1679 */
1680 function wpbc_calendar__blur__start( resource_id ){
1681 if ( ! jQuery( '#calendar_booking' + resource_id ).hasClass( 'wpbc_calendar_blur' ) ){
1682 jQuery( '#calendar_booking' + resource_id ).addClass( 'wpbc_calendar_blur' );
1683 }
1684 }
1685
1686 /**
1687 * Remove Blur in calendar
1688 * @param resource_id
1689 */
1690 function wpbc_calendar__blur__stop( resource_id ){
1691 jQuery( '#calendar_booking' + resource_id ).removeClass( 'wpbc_calendar_blur' );
1692 }
1693
1694
1695 // .................................................................................................................
1696 /* == Calendar Update - View ==
1697 // ................................................................................................................. */
1698
1699 /**
1700 * Update look of calendar (safe).
1701 *
1702 * In Elementor preview the DOM can be re-rendered, so the calendar element may exist
1703 * while the Datepick instance is missing. In that case try to (re)initialize.
1704 *
1705 * @param {int|string} resource_id
1706 * @return {boolean} true if updated, false if not possible
1707 */
1708 function wpbc_calendar__update_look(resource_id) {
1709
1710 var inst = wpbc_calendar__get_inst( resource_id );
1711
1712 // If instance missing, try to re-init calendar once.
1713 if ( null === inst ) {
1714
1715 var jq_cal = jQuery( '#calendar_booking' + resource_id );
1716
1717 if ( jq_cal.length && ('function' === typeof wpbc_calendar_show) ) {
1718
1719 // Elementor sometimes leaves stale class without real instance.
1720 if ( jq_cal.hasClass( 'hasDatepick' ) ) {
1721 jq_cal.removeClass( 'hasDatepick' );
1722 }
1723
1724 // Try to init datepick markup now.
1725 wpbc_calendar_show( resource_id );
1726
1727 // Try again.
1728 inst = wpbc_calendar__get_inst( resource_id );
1729 }
1730 }
1731
1732 // Still no instance -> do not crash the whole ajax flow.
1733 if ( null === inst ) {
1734 return false;
1735 }
1736
1737 jQuery.datepick._updateDatepick( inst );
1738 return true;
1739 }
1740
1741
1742
1743 /**
1744 * Update dynamically Number of Months in calendar
1745 *
1746 * @param resource_id int
1747 * @param months_number int
1748 */
1749 function wpbc_calendar__update_months_number( resource_id, months_number ){
1750 var inst = wpbc_calendar__get_inst( resource_id );
1751 if ( null !== inst ){
1752 inst.settings[ 'numberOfMonths' ] = months_number;
1753 //_wpbc.calendar__set_param_value( resource_id, 'calendar_number_of_months', months_number );
1754 wpbc_calendar__update_look( resource_id );
1755 }
1756 }
1757
1758
1759 /**
1760 * Show calendar in different Skin
1761 *
1762 * @param selected_skin_url
1763 */
1764 function wpbc__calendar__change_skin( selected_skin_url ){
1765
1766 //console.log( 'SKIN SELECTION ::', selected_skin_url );
1767
1768 // Remove CSS skin
1769 var stylesheet = document.getElementById( 'wpbc-calendar-skin-css' );
1770 stylesheet.parentNode.removeChild( stylesheet );
1771
1772
1773 // Add new CSS skin
1774 var headID = document.getElementsByTagName( "head" )[ 0 ];
1775 var cssNode = document.createElement( 'link' );
1776 cssNode.type = 'text/css';
1777 cssNode.setAttribute( "id", "wpbc-calendar-skin-css" );
1778 cssNode.rel = 'stylesheet';
1779 cssNode.media = 'screen';
1780 cssNode.href = selected_skin_url; //"http://beta/wp-content/plugins/booking/css/skins/green-01.css";
1781 headID.appendChild( cssNode );
1782 }
1783
1784
1785 function wpbc__css__change_skin( selected_skin_url, stylesheet_id = 'wpbc-time_picker-skin-css' ){
1786
1787 // Remove CSS skin
1788 var stylesheet = document.getElementById( stylesheet_id );
1789 stylesheet.parentNode.removeChild( stylesheet );
1790
1791
1792 // Add new CSS skin
1793 var headID = document.getElementsByTagName( "head" )[ 0 ];
1794 var cssNode = document.createElement( 'link' );
1795 cssNode.type = 'text/css';
1796 cssNode.setAttribute( "id", stylesheet_id );
1797 cssNode.rel = 'stylesheet';
1798 cssNode.media = 'screen';
1799 cssNode.href = selected_skin_url; //"http://beta/wp-content/plugins/booking/css/skins/green-01.css";
1800 headID.appendChild( cssNode );
1801 }
1802
1803
1804 // ---------------------------------------------------------------------------------------------------------------------
1805 /* == S U P P O R T M A T H ==
1806 // --------------------------------------------------------------------------------------------------------------------- */
1807
1808 /**
1809 * Merge several intersected intervals or return not intersected:
1810 * [[1,3],[2,6],[8,10],[15,18]] -> [[1,6],[8,10],[15,18]]
1811 *
1812 * @param [] intervals [ [1,3],[2,4],[6,8],[9,10],[3,7] ]
1813 * @returns [] [ [1,8],[9,10] ]
1814 *
1815 * Exmample: wpbc_intervals__merge_inersected( [ [1,3],[2,4],[6,8],[9,10],[3,7] ] );
1816 */
1817 function wpbc_intervals__merge_inersected( intervals ){
1818
1819 if ( ! intervals || intervals.length === 0 ){
1820 return [];
1821 }
1822
1823 var merged = [];
1824 intervals.sort( function ( a, b ){
1825 return a[ 0 ] - b[ 0 ];
1826 } );
1827
1828 var mergedInterval = intervals[ 0 ];
1829
1830 for ( var i = 1; i < intervals.length; i++ ){
1831 var interval = intervals[ i ];
1832
1833 if ( interval[ 0 ] <= mergedInterval[ 1 ] ){
1834 mergedInterval[ 1 ] = Math.max( mergedInterval[ 1 ], interval[ 1 ] );
1835 } else {
1836 merged.push( mergedInterval );
1837 mergedInterval = interval;
1838 }
1839 }
1840
1841 merged.push( mergedInterval );
1842 return merged;
1843 }
1844
1845
1846 /**
1847 * Is 2 intervals intersected: [36011, 86392] <=> [1, 43192] => true ( intersected )
1848 *
1849 * Good explanation here
1850 * https://stackoverflow.com/questions/3269434/whats-the-most-efficient-way-to-test-if-two-ranges-overlap
1851 *
1852 * @param interval_A - [ 36011, 86392 ]
1853 * @param interval_B - [ 1, 43192 ]
1854 *
1855 * @return bool
1856 */
1857 function wpbc_intervals__is_intersected( interval_A, interval_B ) {
1858
1859 if (
1860 ( 0 == interval_A.length )
1861 || ( 0 == interval_B.length )
1862 ){
1863 return false;
1864 }
1865
1866 interval_A[ 0 ] = parseInt( interval_A[ 0 ] );
1867 interval_A[ 1 ] = parseInt( interval_A[ 1 ] );
1868 interval_B[ 0 ] = parseInt( interval_B[ 0 ] );
1869 interval_B[ 1 ] = parseInt( interval_B[ 1 ] );
1870
1871 var is_intersected = Math.max( interval_A[ 0 ], interval_B[ 0 ] ) - Math.min( interval_A[ 1 ], interval_B[ 1 ] );
1872
1873 // if ( 0 == is_intersected ) {
1874 // // Such ranges going one after other, e.g.: [ 12, 15 ] and [ 15, 21 ]
1875 // }
1876
1877 if ( is_intersected < 0 ) {
1878 return true; // INTERSECTED
1879 }
1880
1881 return false; // Not intersected
1882 }
1883
1884
1885 /**
1886 * Get the closets ABS value of element in array to the current myValue
1887 *
1888 * @param myValue - int element to search closet 4
1889 * @param myArray - array of elements where to search [5,8,1,7]
1890 * @returns int 5
1891 */
1892 function wpbc_get_abs_closest_value_in_arr( myValue, myArray ){
1893
1894 if ( myArray.length == 0 ){ // If the array is empty -> return the myValue
1895 return myValue;
1896 }
1897
1898 var obj = myArray[ 0 ];
1899 var diff = Math.abs( myValue - obj ); // Get distance between 1st element
1900 var closetValue = myArray[ 0 ]; // Save 1st element
1901
1902 for ( var i = 1; i < myArray.length; i++ ){
1903 obj = myArray[ i ];
1904
1905 if ( Math.abs( myValue - obj ) < diff ){ // we found closer value -> save it
1906 diff = Math.abs( myValue - obj );
1907 closetValue = obj;
1908 }
1909 }
1910
1911 return closetValue;
1912 }
1913
1914
1915 // ---------------------------------------------------------------------------------------------------------------------
1916 /* == T O O L T I P S ==
1917 // --------------------------------------------------------------------------------------------------------------------- */
1918
1919 /**
1920 * Define tooltip to show, when mouse over Date in Calendar
1921 *
1922 * @param tooltip_text - Text to show 'Booked time: 12:00 - 13:00<br>Cost: $20.00'
1923 * @param resource_id - ID of booking resource '1'
1924 * @param td_class - SQL class '1-9-2023'
1925 * @returns {boolean} - defined to show or not
1926 */
1927 function wpbc_set_tooltip___for__calendar_date( tooltip_text, resource_id, td_class ){
1928
1929 //TODO: make escaping of text for quot symbols, and JS/HTML...
1930
1931 jQuery( '#calendar_booking' + resource_id + ' td.cal4date-' + td_class ).attr( 'data-content', tooltip_text );
1932
1933 var td_el = jQuery( '#calendar_booking' + resource_id + ' td.cal4date-' + td_class ).get( 0 ); // FixIn: 9.0.1.1.
1934
1935 if (
1936 ( 'undefined' !== typeof(td_el) )
1937 && ( undefined == td_el._tippy )
1938 && ( '' !== tooltip_text )
1939 ){
1940
1941 wpbc_tippy( td_el , {
1942 content( reference ){
1943
1944 var popover_content = reference.getAttribute( 'data-content' );
1945
1946 return '<div class="popover popover_tippy">'
1947 + '<div class="popover-content">'
1948 + popover_content
1949 + '</div>'
1950 + '</div>';
1951 },
1952 allowHTML : true,
1953 trigger : 'mouseenter focus',
1954 interactive : false,
1955 hideOnClick : true,
1956 interactiveBorder: 10,
1957 maxWidth : 550,
1958 theme : 'wpbc-tippy-times',
1959 placement : 'top',
1960 delay : [400, 0], // FixIn: 9.4.2.2.
1961 //delay : [0, 9999999999], // Debuge tooltip
1962 ignoreAttributes : true,
1963 touch : true, //['hold', 500], // 500ms delay // FixIn: 9.2.1.5.
1964 appendTo: () => document.body,
1965 });
1966
1967 return true;
1968 }
1969
1970 return false;
1971 }
1972
1973
1974 // ---------------------------------------------------------------------------------------------------------------------
1975 /* == Dates Functions ==
1976 // --------------------------------------------------------------------------------------------------------------------- */
1977
1978 /**
1979 * Get number of dates between 2 JS Dates
1980 *
1981 * @param date1 JS Date
1982 * @param date2 JS Date
1983 * @returns {number}
1984 */
1985 function wpbc_dates__days_between(date1, date2) {
1986
1987 // The number of milliseconds in one day
1988 var ONE_DAY = 1000 * 60 * 60 * 24;
1989
1990 // Convert both dates to milliseconds
1991 var date1_ms = date1.getTime();
1992 var date2_ms = date2.getTime();
1993
1994 // Calculate the difference in milliseconds
1995 var difference_ms = date1_ms - date2_ms;
1996
1997 // Convert back to days and return
1998 return Math.round(difference_ms/ONE_DAY);
1999 }
2000
2001
2002 /**
2003 * Check if this array of dates is consecutive array of dates or not.
2004 * e.g. ['2024-05-09','2024-05-19','2024-05-30'] -> false
2005 * e.g. ['2024-05-09','2024-05-10','2024-05-11'] -> true
2006 * @param sql_dates_arr array e.g.: ['2024-05-09','2024-05-19','2024-05-30']
2007 * @returns {boolean}
2008 */
2009 function wpbc_dates__is_consecutive_dates_arr_range( sql_dates_arr ){ // FixIn: 10.0.0.50.
2010
2011 if ( sql_dates_arr.length > 1 ){
2012 var previos_date = wpbc__get__js_date( sql_dates_arr[ 0 ] );
2013 var current_date;
2014
2015 for ( var i = 1; i < sql_dates_arr.length; i++ ){
2016 current_date = wpbc__get__js_date( sql_dates_arr[i] );
2017
2018 if ( wpbc_dates__days_between( current_date, previos_date ) != 1 ){
2019 return false;
2020 }
2021
2022 previos_date = current_date;
2023 }
2024 }
2025
2026 return true;
2027 }
2028
2029
2030 // ---------------------------------------------------------------------------------------------------------------------
2031 /* == Auto Dates Selection ==
2032 // --------------------------------------------------------------------------------------------------------------------- */
2033
2034 /**
2035 * == How to use ? ==
2036 *
2037 * For Dates selection, we need to use this logic! We need select the dates only after booking data loaded!
2038 *
2039 * Check example bellow.
2040 *
2041 * // Fire on all booking dates loaded
2042 * jQuery( 'body' ).on( 'wpbc_calendar_ajx__loaded_data', function ( event, loaded_resource_id ){
2043 *
2044 * if ( loaded_resource_id == select_dates_in_calendar_id ){
2045 * wpbc_auto_select_dates_in_calendar( select_dates_in_calendar_id, '2024-05-15', '2024-05-25' );
2046 * }
2047 * } );
2048 *
2049 */
2050
2051
2052 /**
2053 * Try to Auto select dates in specific calendar by simulated clicks in datepicker
2054 *
2055 * @param resource_id 1
2056 * @param check_in_ymd '2024-05-09' OR ['2024-05-09','2024-05-19','2024-05-20']
2057 * @param check_out_ymd '2024-05-15' Optional
2058 *
2059 * @returns {number} number of selected dates
2060 *
2061 * Example 1: var num_selected_days = wpbc_auto_select_dates_in_calendar( 1, '2024-05-15',
2062 * '2024-05-25' ); Example 2: var num_selected_days = wpbc_auto_select_dates_in_calendar( 1,
2063 * ['2024-05-09','2024-05-19','2024-05-20'] );
2064 */
2065 function wpbc_auto_select_dates_in_calendar( resource_id, check_in_ymd, check_out_ymd = '' ){ // FixIn: 10.0.0.47.
2066
2067 console.log( 'WPBC_AUTO_SELECT_DATES_IN_CALENDAR( RESOURCE_ID, CHECK_IN_YMD, CHECK_OUT_YMD )', resource_id, check_in_ymd, check_out_ymd );
2068
2069 if (
2070 ( '2100-01-01' == check_in_ymd )
2071 || ( '2100-01-01' == check_out_ymd )
2072 || ( ( '' == check_in_ymd ) && ( '' == check_out_ymd ) )
2073 ){
2074 return 0;
2075 }
2076
2077 // -----------------------------------------------------------------------------------------------------------------
2078 // If check_in_ymd = [ '2024-05-09','2024-05-19','2024-05-30' ] ARRAY of DATES // FixIn: 10.0.0.50.
2079 // -----------------------------------------------------------------------------------------------------------------
2080 var dates_to_select_arr = [];
2081 if ( Array.isArray( check_in_ymd ) ){
2082 dates_to_select_arr = wpbc_clone_obj( check_in_ymd );
2083
2084 // -------------------------------------------------------------------------------------------------------------
2085 // Exceptions to set MULTIPLE DAYS mode
2086 // -------------------------------------------------------------------------------------------------------------
2087 // if dates as NOT CONSECUTIVE: ['2024-05-09','2024-05-19','2024-05-30'], -> set MULTIPLE DAYS mode
2088 if (
2089 ( dates_to_select_arr.length > 0 )
2090 && ( '' == check_out_ymd )
2091 && ( ! wpbc_dates__is_consecutive_dates_arr_range( dates_to_select_arr ) )
2092 ){
2093 wpbc_cal_days_select__multiple( resource_id );
2094 }
2095 // if multiple days to select, but enabled SINGLE day mode, -> set MULTIPLE DAYS mode
2096 if (
2097 ( dates_to_select_arr.length > 1 )
2098 && ( '' == check_out_ymd )
2099 && ( 'single' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) )
2100 ){
2101 wpbc_cal_days_select__multiple( resource_id );
2102 }
2103 // -------------------------------------------------------------------------------------------------------------
2104 check_in_ymd = dates_to_select_arr[ 0 ];
2105 if ( '' == check_out_ymd ){
2106 check_out_ymd = dates_to_select_arr[ (dates_to_select_arr.length-1) ];
2107 }
2108 }
2109 // -----------------------------------------------------------------------------------------------------------------
2110
2111
2112 if ( '' == check_in_ymd ){
2113 check_in_ymd = check_out_ymd;
2114 }
2115 if ( '' == check_out_ymd ){
2116 check_out_ymd = check_in_ymd;
2117 }
2118
2119 if ( 'undefined' === typeof (resource_id) ){
2120 resource_id = '1';
2121 }
2122
2123
2124 var inst = wpbc_calendar__get_inst( resource_id );
2125
2126 if ( null !== inst ){
2127
2128 // Unselect all dates and set properties of Datepick
2129 jQuery( '#date_booking' + resource_id ).val( '' ); //FixIn: 5.4.3
2130 inst.stayOpen = false;
2131 inst.dates = [];
2132 var check_in_js = wpbc__get__js_date( check_in_ymd );
2133 var td_cell = wpbc_get_clicked_td( inst.id, check_in_js );
2134
2135 // Is ome type of error, then select multiple days selection mode.
2136 if ( '' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ) {
2137 _wpbc.calendar__set_param_value( resource_id, 'days_select_mode', 'multiple' );
2138 }
2139
2140
2141 // ---------------------------------------------------------------------------------------------------------
2142 // == DYNAMIC ==
2143 if ( 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ){
2144 // 1-st click
2145 inst.stayOpen = false;
2146 jQuery.datepick._selectDay( td_cell, '#' + inst.id, check_in_js.getTime() );
2147 if ( 0 === inst.dates.length ){
2148 return 0; // First click was unsuccessful, so we must not make other click
2149 }
2150
2151 // 2-nd click
2152 var check_out_js = wpbc__get__js_date( check_out_ymd );
2153 var td_cell_out = wpbc_get_clicked_td( inst.id, check_out_js );
2154 inst.stayOpen = true;
2155 jQuery.datepick._selectDay( td_cell_out, '#' + inst.id, check_out_js.getTime() );
2156 }
2157
2158 // ---------------------------------------------------------------------------------------------------------
2159 // == FIXED ==
2160 if ( 'fixed' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' )) {
2161 jQuery.datepick._selectDay( td_cell, '#' + inst.id, check_in_js.getTime() );
2162 }
2163
2164 // ---------------------------------------------------------------------------------------------------------
2165 // == SINGLE ==
2166 if ( 'single' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ){
2167 //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!
2168 jQuery.datepick._selectDay( td_cell, '#' + inst.id, check_in_js.getTime() );
2169 }
2170
2171 // ---------------------------------------------------------------------------------------------------------
2172 // == MULTIPLE ==
2173 if ( 'multiple' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ){
2174
2175 var dates_arr;
2176
2177 if ( dates_to_select_arr.length > 0 ){
2178 // 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
2179 dates_arr = wpbc_get_selection_dates_js_str_arr__from_arr( dates_to_select_arr );
2180 } else {
2181 dates_arr = wpbc_get_selection_dates_js_str_arr__from_check_in_out( check_in_ymd, check_out_ymd, inst );
2182 }
2183
2184 if ( 0 === dates_arr.dates_js.length ){
2185 return 0;
2186 }
2187
2188 // For Calendar Days selection
2189 for ( var j = 0; j < dates_arr.dates_js.length; j++ ){ // Loop array of dates
2190
2191 var str_date = wpbc__get__sql_class_date( dates_arr.dates_js[ j ] );
2192
2193 // Date unavailable !
2194 if ( 0 == _wpbc.bookings_in_calendar__get_for_date( resource_id, str_date ).day_availability ){
2195 return 0;
2196 }
2197
2198 if ( dates_arr.dates_js[ j ] != -1 ) {
2199 inst.dates.push( dates_arr.dates_js[ j ] );
2200 }
2201 }
2202
2203 var check_out_date = dates_arr.dates_js[ (dates_arr.dates_js.length - 1) ];
2204
2205 inst.dates.push( check_out_date ); // Need add one additional SAME date for correct works of dates selection !!!!!
2206
2207 var checkout_timestamp = check_out_date.getTime();
2208 var td_cell = wpbc_get_clicked_td( inst.id, check_out_date );
2209
2210 jQuery.datepick._selectDay( td_cell, '#' + inst.id, checkout_timestamp );
2211 }
2212
2213
2214 if ( 0 !== inst.dates.length ){
2215 // Scroll to specific month, if we set dates in some future months
2216 wpbc_calendar__scroll_to( resource_id, inst.dates[ 0 ].getFullYear(), inst.dates[ 0 ].getMonth()+1 );
2217 }
2218
2219 return inst.dates.length;
2220 }
2221
2222 return 0;
2223 }
2224
2225 /**
2226 * Get HTML td element (where was click in calendar day cell)
2227 *
2228 * @param calendar_html_id 'calendar_booking1'
2229 * @param date_js JS Date
2230 * @returns {*|jQuery} Dom HTML td element
2231 */
2232 function wpbc_get_clicked_td( calendar_html_id, date_js ){
2233
2234 var td_cell = jQuery( '#' + calendar_html_id + ' .sql_date_' + wpbc__get__sql_class_date( date_js ) ).get( 0 );
2235
2236 return td_cell;
2237 }
2238
2239 /**
2240 * Get arrays of JS and SQL dates as dates array
2241 *
2242 * @param check_in_ymd '2024-05-15'
2243 * @param check_out_ymd '2024-05-25'
2244 * @param inst Datepick Inst. Use wpbc_calendar__get_inst( resource_id );
2245 * @returns {{dates_js: *[], dates_str: *[]}}
2246 */
2247 function wpbc_get_selection_dates_js_str_arr__from_check_in_out( check_in_ymd, check_out_ymd , inst ){
2248
2249 var original_array = [];
2250 var date;
2251 var bk_distinct_dates = [];
2252
2253 var check_in_date = check_in_ymd.split( '-' );
2254 var check_out_date = check_out_ymd.split( '-' );
2255
2256 date = new Date();
2257 date.setFullYear( check_in_date[ 0 ], (check_in_date[ 1 ] - 1), check_in_date[ 2 ] ); // year, month, date
2258 var original_check_in_date = date;
2259 original_array.push( jQuery.datepick._restrictMinMax( inst, jQuery.datepick._determineDate( inst, date, null ) ) ); //add date
2260 if ( ! wpbc_in_array( bk_distinct_dates, (check_in_date[ 2 ] + '.' + check_in_date[ 1 ] + '.' + check_in_date[ 0 ]) ) ){
2261 bk_distinct_dates.push( parseInt(check_in_date[ 2 ]) + '.' + parseInt(check_in_date[ 1 ]) + '.' + check_in_date[ 0 ] );
2262 }
2263
2264 var date_out = new Date();
2265 date_out.setFullYear( check_out_date[ 0 ], (check_out_date[ 1 ] - 1), check_out_date[ 2 ] ); // year, month, date
2266 var original_check_out_date = date_out;
2267
2268 var mewDate = new Date( original_check_in_date.getFullYear(), original_check_in_date.getMonth(), original_check_in_date.getDate() );
2269 mewDate.setDate( original_check_in_date.getDate() + 1 );
2270
2271 while (
2272 (original_check_out_date > date) &&
2273 (original_check_in_date != original_check_out_date) ){
2274 date = new Date( mewDate.getFullYear(), mewDate.getMonth(), mewDate.getDate() );
2275
2276 original_array.push( jQuery.datepick._restrictMinMax( inst, jQuery.datepick._determineDate( inst, date, null ) ) ); //add date
2277 if ( !wpbc_in_array( bk_distinct_dates, (date.getDate() + '.' + parseInt( date.getMonth() + 1 ) + '.' + date.getFullYear()) ) ){
2278 bk_distinct_dates.push( (parseInt(date.getDate()) + '.' + parseInt( date.getMonth() + 1 ) + '.' + date.getFullYear()) );
2279 }
2280
2281 mewDate = new Date( date.getFullYear(), date.getMonth(), date.getDate() );
2282 mewDate.setDate( mewDate.getDate() + 1 );
2283 }
2284 original_array.pop();
2285 bk_distinct_dates.pop();
2286
2287 return {'dates_js': original_array, 'dates_str': bk_distinct_dates};
2288 }
2289
2290 /**
2291 * Get arrays of JS and SQL dates as dates array
2292 *
2293 * @param dates_to_select_arr = ['2024-05-09','2024-05-19','2024-05-30']
2294 *
2295 * @returns {{dates_js: *[], dates_str: *[]}}
2296 */
2297 function wpbc_get_selection_dates_js_str_arr__from_arr( dates_to_select_arr ){ // FixIn: 10.0.0.50.
2298
2299 var original_array = [];
2300 var bk_distinct_dates = [];
2301 var one_date_str;
2302
2303 for ( var d = 0; d < dates_to_select_arr.length; d++ ){
2304
2305 original_array.push( wpbc__get__js_date( dates_to_select_arr[ d ] ) );
2306
2307 one_date_str = dates_to_select_arr[ d ].split('-')
2308 if ( ! wpbc_in_array( bk_distinct_dates, (one_date_str[ 2 ] + '.' + one_date_str[ 1 ] + '.' + one_date_str[ 0 ]) ) ){
2309 bk_distinct_dates.push( parseInt(one_date_str[ 2 ]) + '.' + parseInt(one_date_str[ 1 ]) + '.' + one_date_str[ 0 ] );
2310 }
2311 }
2312
2313 return {'dates_js': original_array, 'dates_str': original_array};
2314 }
2315
2316 // =====================================================================================================================
2317 /* == Auto Fill Fields / Auto Select Dates ==
2318 // ===================================================================================================================== */
2319
2320 jQuery( document ).ready( function (){
2321
2322 var url_params = new URLSearchParams( window.location.search );
2323
2324 // Disable days selection in calendar, after redirection from the "Search results page, after search availability" // FixIn: 8.8.2.3.
2325 if ( 'On' != _wpbc.get_other_param( 'is_enabled_booking_search_results_days_select' ) ) {
2326 if (
2327 ( url_params.has( 'wpbc_select_check_in' ) ) &&
2328 ( url_params.has( 'wpbc_select_check_out' ) ) &&
2329 ( url_params.has( 'wpbc_select_calendar_id' ) )
2330 ){
2331
2332 var select_dates_in_calendar_id = parseInt( url_params.get( 'wpbc_select_calendar_id' ) );
2333
2334 // Fire on all booking dates loaded
2335 jQuery( 'body' ).on( 'wpbc_calendar_ajx__loaded_data', function ( event, loaded_resource_id ){
2336
2337 if ( loaded_resource_id == select_dates_in_calendar_id ){
2338 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' ) );
2339 }
2340 } );
2341 }
2342 }
2343
2344 if ( url_params.has( 'wpbc_auto_fill' ) ){
2345
2346 var wpbc_auto_fill_value = url_params.get( 'wpbc_auto_fill' );
2347
2348 // Convert back. Some systems do not like symbol '~' in URL, so we need to replace to some other symbols
2349 wpbc_auto_fill_value = wpbc_auto_fill_value.replaceAll( '_^_', '~' );
2350
2351 wpbc_auto_fill_booking_fields( wpbc_auto_fill_value );
2352 }
2353
2354 } );
2355
2356 /**
2357 * Autofill / select booking form fields by values from the GET request parameter: ?wpbc_auto_fill=
2358 *
2359 * @param auto_fill_str
2360 */
2361 function wpbc_auto_fill_booking_fields( auto_fill_str ){ // FixIn: 10.0.0.48.
2362
2363 if ( '' == auto_fill_str ){
2364 return;
2365 }
2366
2367 // console.log( 'WPBC_AUTO_FILL_BOOKING_FIELDS( AUTO_FILL_STR )', auto_fill_str);
2368
2369 var fields_arr = wpbc_auto_fill_booking_fields__parse( auto_fill_str );
2370
2371 for ( let i = 0; i < fields_arr.length; i++ ){
2372 jQuery( '[name="' + fields_arr[ i ][ 'name' ] + '"]' ).val( fields_arr[ i ][ 'value' ] );
2373 }
2374 }
2375
2376 /**
2377 * Parse data from get parameter: ?wpbc_auto_fill=visitors231^2~max_capacity231^2
2378 *
2379 * @param data_str = 'visitors231^2~max_capacity231^2';
2380 * @returns {*}
2381 */
2382 function wpbc_auto_fill_booking_fields__parse( data_str ){
2383
2384 var filter_options_arr = [];
2385
2386 var data_arr = data_str.split( '~' );
2387
2388 for ( var j = 0; j < data_arr.length; j++ ){
2389
2390 var my_form_field = data_arr[ j ].split( '^' );
2391
2392 var filter_name = ('undefined' !== typeof (my_form_field[ 0 ])) ? my_form_field[ 0 ] : '';
2393 var filter_value = ('undefined' !== typeof (my_form_field[ 1 ])) ? my_form_field[ 1 ] : '';
2394
2395 filter_options_arr.push(
2396 {
2397 'name' : filter_name,
2398 'value' : filter_value
2399 }
2400 );
2401 }
2402 return filter_options_arr;
2403 }
2404
2405 /**
2406 * Parse data from get parameter: ?search_get__custom_params=...
2407 *
2408 * @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^~';
2409 * @returns {*}
2410 */
2411 function wpbc_auto_fill_search_fields__parse( data_str ){
2412
2413 var filter_options_arr = [];
2414
2415 var data_arr = data_str.split( '~' );
2416
2417 for ( var j = 0; j < data_arr.length; j++ ){
2418
2419 var my_form_field = data_arr[ j ].split( '^' );
2420
2421 var filter_type = ('undefined' !== typeof (my_form_field[ 0 ])) ? my_form_field[ 0 ] : '';
2422 var filter_name = ('undefined' !== typeof (my_form_field[ 1 ])) ? my_form_field[ 1 ] : '';
2423 var filter_value = ('undefined' !== typeof (my_form_field[ 2 ])) ? my_form_field[ 2 ] : '';
2424
2425 filter_options_arr.push(
2426 {
2427 'type' : filter_type,
2428 'name' : filter_name,
2429 'value' : filter_value
2430 }
2431 );
2432 }
2433 return filter_options_arr;
2434 }
2435
2436
2437 // ---------------------------------------------------------------------------------------------------------------------
2438 /* == Auto Update number of months in calendars ON screen size changed ==
2439 // --------------------------------------------------------------------------------------------------------------------- */
2440
2441 /**
2442 * Auto Update Number of Months in Calendar, e.g.: if ( WINDOW_WIDTH <= 782px ) >>> MONTHS_NUMBER = 1
2443 * ELSE: number of months defined in shortcode.
2444 * @param resource_id int
2445 *
2446 */
2447 function wpbc_calendar__auto_update_months_number__on_resize( resource_id ){
2448
2449 if ( true === _wpbc.get_other_param( 'is_allow_several_months_on_mobile' ) ) {
2450 return false;
2451 }
2452
2453 var local__number_of_months = parseInt( _wpbc.calendar__get_param_value( resource_id, 'calendar_number_of_months' ) );
2454
2455 if ( local__number_of_months > 1 ){
2456
2457 if ( jQuery( window ).width() <= 782 ){
2458 wpbc_calendar__update_months_number( resource_id, 1 );
2459 } else {
2460 wpbc_calendar__update_months_number( resource_id, local__number_of_months );
2461 }
2462
2463 }
2464 }
2465
2466 /**
2467 * Auto Update Number of Months in ALL Calendars
2468 *
2469 */
2470 function wpbc_calendars__auto_update_months_number(){
2471
2472 var all_calendars_arr = _wpbc.calendars_all__get();
2473
2474 // This LOOP "for in" is GOOD, because we check here keys 'calendar_' === calendar_id.slice( 0, 9 )
2475 for ( var calendar_id in all_calendars_arr ){
2476 if ( 'calendar_' === calendar_id.slice( 0, 9 ) ){
2477 var resource_id = parseInt( calendar_id.slice( 9 ) ); // 'calendar_3' -> 3
2478 if ( resource_id > 0 ){
2479 wpbc_calendar__auto_update_months_number__on_resize( resource_id );
2480 }
2481 }
2482 }
2483 }
2484
2485 /**
2486 * If browser window changed, then update number of months.
2487 */
2488 jQuery( window ).on( 'resize', function (){
2489 wpbc_calendars__auto_update_months_number();
2490 } );
2491
2492 /**
2493 * Auto update calendar number of months on initial page load
2494 */
2495 jQuery( document ).ready( function (){
2496 var closed_timer = setTimeout( function (){
2497 wpbc_calendars__auto_update_months_number();
2498 }, 100 );
2499 });
2500
2501 // ---------------------------------------------------------------------------------------------------------------------
2502 /* == Check: calendar_dates_start: "2026-01-01", calendar_dates_end: "2026-12-31" == // FixIn: 10.13.1.4.
2503 // --------------------------------------------------------------------------------------------------------------------- */
2504 /**
2505 * Get Start JS Date of starting dates in calendar, from the _wpbc object.
2506 *
2507 * @param integer resource_id - resource ID, e.g.: 1.
2508 */
2509 function wpbc_calendar__get_dates_start( resource_id ) {
2510 return wpbc_calendar__get_date_parameter( resource_id, 'calendar_dates_start' );
2511 }
2512
2513 /**
2514 * Get End JS Date of ending dates in calendar, from the _wpbc object.
2515 *
2516 * @param integer resource_id - resource ID, e.g.: 1.
2517 */
2518 function wpbc_calendar__get_dates_end(resource_id) {
2519 return wpbc_calendar__get_date_parameter( resource_id, 'calendar_dates_end' );
2520 }
2521
2522 /**
2523 * Get validates date parameter.
2524 *
2525 * @param resource_id - 1
2526 * @param parameter_str - 'calendar_dates_start' | 'calendar_dates_end' | ...
2527 */
2528 function wpbc_calendar__get_date_parameter(resource_id, parameter_str) {
2529
2530 var date_expected_ymd = _wpbc.calendar__get_param_value( resource_id, parameter_str );
2531
2532 if ( ! date_expected_ymd ) {
2533 return false; // '' | 0 | null | undefined -> false.
2534 }
2535
2536 if ( -1 !== date_expected_ymd.indexOf( '-' ) ) {
2537
2538 var date_expected_ymd_arr = date_expected_ymd.split( '-' ); // '2025-07-26' -> ['2025', '07', '26']
2539
2540 if ( date_expected_ymd_arr.length > 0 ) {
2541 var year = (date_expected_ymd_arr.length > 0) ? parseInt( date_expected_ymd_arr[0] ) : new Date().getFullYear(); // Year.
2542 var month = (date_expected_ymd_arr.length > 1) ? (parseInt( date_expected_ymd_arr[1] ) - 1) : 0; // (month - 1) or 0 - Jan.
2543 var day = (date_expected_ymd_arr.length > 2) ? parseInt( date_expected_ymd_arr[2] ) : 1; // date or Otherwise 1st of month
2544
2545 var date_js = new Date( year, month, day, 0, 0, 0, 0 );
2546
2547 return date_js;
2548 }
2549 }
2550
2551 return false; // Fallback, if we not parsed this parameter 'calendar_dates_start' = '2025-07-26', for example because of 'calendar_dates_start' = 'sfsdf'.
2552 }
2553