PluginProbe
Booking Calendar / 11.0
Booking Calendar v11.0
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.0, at includes/__js/client/cal/wpbc_cal.js

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