| @@ -1,4 +1,4 @@ | ||
| 1 | 1 | /** |
| 2 | 2 | * ===================================================================================================================== |
| 3 | 3 | * includes/__js/cal/wpbc_cal.js |
| 4 | 4 | * ===================================================================================================================== |
| @@ -76,10 +76,12 @@ | ||
| 76 | 76 | // Stale marker: remove and continue with init. |
| 77 | 77 | jQuery( '#calendar_booking' + resource_id ).removeClass( 'hasDatepick' ); |
| 78 | 78 | } |
| 79 | 79 | |
| 80 | + wpbc_range_selection__prepare_guidance( resource_id ); | |
| 80 | 81 | |
| 81 | 82 | |
| 83 | + | |
| 82 | 84 | // ----------------------------------------------------------------------------------------------------------------- |
| 83 | 85 | // Days selection |
| 84 | 86 | // ----------------------------------------------------------------------------------------------------------------- |
| 85 | 87 | var local__is_range_select = false; |
| @@ -115,17 +117,26 @@ | ||
| 115 | 117 | local__max_date = wpbc_calendar__get_dates_end( resource_id ); // E.g. - local__max_date = new Date( 2025, 11, 31 ); |
| 116 | 118 | } |
| 117 | 119 | |
| 118 | 120 | // In case we edit booking in past or have specific parameter in URL. |
| 119 | - if ( ( location.href.indexOf('page=wpbc-new') != -1 ) | |
| 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 ) | |
| 120 | 128 | && ( |
| 121 | - ( location.href.indexOf('booking_hash') != -1 ) // Comment this line for ability to add booking in past days at Booking > Add booking page. | |
| 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. | |
| 122 | 132 | || ( location.href.indexOf('allow_past') != -1 ) // FixIn: 10.7.1.2. |
| 123 | 133 | ) |
| 124 | 134 | ){ |
| 125 | 135 | // local__min_date = null; |
| 126 | 136 | // FixIn: 10.14.1.4. |
| 127 | - local__min_date = new Date( _wpbc.get_other_param( 'time_local_arr' )[0], ( parseInt( _wpbc.get_other_param( 'time_local_arr' )[1] ) - 1), _wpbc.get_other_param( 'time_local_arr' )[2], _wpbc.get_other_param( 'time_local_arr' )[3], _wpbc.get_other_param( 'time_local_arr' )[4], 0 ); | |
| 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 ); | |
| 128 | 139 | local__max_date = null; |
| 129 | 140 | } |
| 130 | 141 | |
| 131 | 142 | var local__start_weekday = _wpbc.calendar__get_param_value( resource_id, 'booking_start_day_weeek' ); |
| @@ -135,10 +146,14 @@ | ||
| 135 | 146 | // ----------------------------------------------------------------------------------------------------------------- |
| 136 | 147 | // Show calendar |
| 137 | 148 | // ----------------------------------------------------------------------------------------------------------------- |
| 138 | 149 | jQuery('#calendar_booking'+ resource_id).datepick( |
| 139 | - { | |
| 140 | - beforeShowDay: function ( js_date ){ | |
| 150 | + { | |
| 151 | + beforeSelectDay: function ( js_date, inst ) { | |
| 152 | + return 'function' === typeof wpbc__calendar__before_select_days__bs | |
| 153 | + ? wpbc__calendar__before_select_days__bs( js_date, resource_id, inst ) : true; | |
| 154 | + }, | |
| 155 | + beforeShowDay: function ( js_date ){ | |
| 141 | 156 | return wpbc__calendar__apply_css_to_days( js_date, {'resource_id': resource_id}, this ); |
| 142 | 157 | }, |
| 143 | 158 | onSelect: function ( string_dates, js_dates_arr ){ /** |
| 144 | 159 | * string_dates = '23.08.2023 - 26.08.2023' |
| @@ -181,14 +196,14 @@ | ||
| 181 | 196 | } |
| 182 | 197 | ); |
| 183 | 198 | |
| 184 | 199 | |
| 185 | - | |
| 200 | + | |
| 186 | 201 | // ----------------------------------------------------------------------------------------------------------------- |
| 187 | 202 | // Clear today date highlighting |
| 188 | 203 | // ----------------------------------------------------------------------------------------------------------------- |
| 189 | 204 | setTimeout( function (){ wpbc_calendars__clear_days_highlighting( resource_id ); }, 500 ); // FixIn: 7.1.2.8. |
| 190 | - | |
| 205 | + | |
| 191 | 206 | // ----------------------------------------------------------------------------------------------------------------- |
| 192 | 207 | // Scroll calendar to specific month |
| 193 | 208 | // ----------------------------------------------------------------------------------------------------------------- |
| 194 | 209 | var start_bk_month = _wpbc.calendar__get_param_value( resource_id, 'calendar_scroll_to' ); |
| @@ -194,14 +209,159 @@ | ||
| 194 | 209 | var start_bk_month = _wpbc.calendar__get_param_value( resource_id, 'calendar_scroll_to' ); |
| 195 | 210 | if ( false !== start_bk_month ){ |
| 196 | 211 | wpbc_calendar__scroll_to( resource_id, start_bk_month[ 0 ], start_bk_month[ 1 ] ); |
| 197 | 212 | } |
| 198 | -} | |
| 213 | + } | |
| 199 | 214 | |
| 200 | 215 | |
| 201 | 216 | /** |
| 202 | - * Apply CSS to calendar date cells | |
| 217 | + * Get booking statuses as array from the structured summary field, with fallback to the legacy string field. | |
| 203 | 218 | * |
| 219 | + * @param date_bookings_obj | |
| 220 | + * @returns {[]} | |
| 221 | + */ | |
| 222 | + function wpbc_get_booking_statuses__as_arr( date_bookings_obj ){ | |
| 223 | + | |
| 224 | + if ( | |
| 225 | + ( ! date_bookings_obj ) | |
| 226 | + || ( 'undefined' === typeof (date_bookings_obj[ 'summary' ]) ) | |
| 227 | + ){ | |
| 228 | + return []; | |
| 229 | + } | |
| 230 | + | |
| 231 | + if ( Array.isArray( date_bookings_obj[ 'summary' ][ 'status_for_bookings_arr' ] ) ){ | |
| 232 | + return date_bookings_obj[ 'summary' ][ 'status_for_bookings_arr' ].filter( function ( booking_status ){ | |
| 233 | + return '' !== booking_status; | |
| 234 | + } ); | |
| 235 | + } | |
| 236 | + | |
| 237 | + if ( ! date_bookings_obj[ 'summary' ][ 'status_for_bookings' ] ){ | |
| 238 | + return []; | |
| 239 | + } | |
| 240 | + | |
| 241 | + return date_bookings_obj[ 'summary' ][ 'status_for_bookings' ].toString().trim().split( /\s+/ ).filter( function ( booking_status ){ | |
| 242 | + return '' !== booking_status; | |
| 243 | + } ); | |
| 244 | + } | |
| 245 | + | |
| 246 | + | |
| 247 | + /** | |
| 248 | + * Check exact booking status in statuses array. | |
| 249 | + * | |
| 250 | + * @param {[]} booking_statuses_arr | |
| 251 | + * @param {string} booking_status | |
| 252 | + * @returns {boolean} | |
| 253 | + */ | |
| 254 | + function wpbc_booking_statuses__has( booking_statuses_arr, booking_status ){ | |
| 255 | + return booking_statuses_arr.indexOf( booking_status ) > -1; | |
| 256 | + } | |
| 257 | + | |
| 258 | + | |
| 259 | + /** | |
| 260 | + * Check booking status part, e.g. "pending" in "approved_pending". | |
| 261 | + * | |
| 262 | + * @param {[]} booking_statuses_arr | |
| 263 | + * @param {string} booking_status_part | |
| 264 | + * @returns {boolean} | |
| 265 | + */ | |
| 266 | + function wpbc_booking_statuses__has_part( booking_statuses_arr, booking_status_part ){ | |
| 267 | + | |
| 268 | + for ( var i = 0; i < booking_statuses_arr.length; i++ ){ | |
| 269 | + if ( booking_statuses_arr[ i ].split( '_' ).indexOf( booking_status_part ) > -1 ){ | |
| 270 | + return true; | |
| 271 | + } | |
| 272 | + } | |
| 273 | + | |
| 274 | + return false; | |
| 275 | + } | |
| 276 | + | |
| 277 | + | |
| 278 | + /** | |
| 279 | + * Apply optional, one-way date constraints to a native availability result. | |
| 280 | + * | |
| 281 | + * Listeners receive only a new constraint object. No booking, availability, | |
| 282 | + * season, or calendar parameter objects are exposed. Core availability stays | |
| 283 | + * authoritative because the returned state can only change from selectable to | |
| 284 | + * blocked; a listener cannot reopen a natively unavailable date. | |
| 285 | + * | |
| 286 | + * Event: wpbc_calendar__apply_date_constraints | |
| 287 | + * | |
| 288 | + * Constraint object: | |
| 289 | + * { | |
| 290 | + * resource_id: resource_id, | |
| 291 | + * sql_date: sql_date, | |
| 292 | + * is_blocked: false, | |
| 293 | + * css_classes: [] | |
| 294 | + * } | |
| 295 | + * | |
| 296 | + * Listeners may set is_blocked to the boolean true and append individual, | |
| 297 | + * valid CSS class names to css_classes. Core-owned availability classes are | |
| 298 | + * ignored when supplied by listeners. | |
| 299 | + * | |
| 300 | + * @param {number|string} resource_id Booking resource ID. | |
| 301 | + * @param {string} sql_date Calendar date in YYYY-MM-DD format. | |
| 302 | + * @param {boolean} is_natively_selectable Whether core marked the date selectable. | |
| 303 | + * @param {string[]} native_css_classes Core-generated CSS classes. | |
| 304 | + * @returns {{is_selectable: boolean, css_classes: string[]}} Constrained result. | |
| 305 | + */ | |
| 306 | + function wpbc_calendar__evaluate_date_constraints( resource_id, sql_date, is_natively_selectable, native_css_classes ) { | |
| 307 | + | |
| 308 | + var date_constraint = { | |
| 309 | + 'resource_id': resource_id, | |
| 310 | + 'sql_date': sql_date, | |
| 311 | + 'is_blocked': false, | |
| 312 | + 'css_classes': [] | |
| 313 | + }; | |
| 314 | + var result_css_classes = Array.isArray( native_css_classes ) ? native_css_classes.slice( 0 ) : []; | |
| 315 | + var constraint_class_index; | |
| 316 | + var constraint_css_class; | |
| 317 | + | |
| 318 | + jQuery( 'body' ).trigger( 'wpbc_calendar__apply_date_constraints', [ date_constraint ] ); | |
| 319 | + | |
| 320 | + if ( true === date_constraint.is_blocked ) { | |
| 321 | + for ( constraint_class_index = result_css_classes.length - 1; constraint_class_index >= 0; constraint_class_index-- ) { | |
| 322 | + if ( 'date_available' === result_css_classes[ constraint_class_index ] ) { | |
| 323 | + result_css_classes.splice( constraint_class_index, 1 ); | |
| 324 | + } | |
| 325 | + } | |
| 326 | + | |
| 327 | + if ( -1 === result_css_classes.indexOf( 'date_user_unavailable' ) ) { | |
| 328 | + result_css_classes.push( 'date_user_unavailable' ); | |
| 329 | + } | |
| 330 | + } | |
| 331 | + | |
| 332 | + if ( Array.isArray( date_constraint.css_classes ) ) { | |
| 333 | + for ( constraint_class_index = 0; constraint_class_index < date_constraint.css_classes.length; constraint_class_index++ ) { | |
| 334 | + if ( 'string' !== typeof date_constraint.css_classes[ constraint_class_index ] ) { | |
| 335 | + continue; | |
| 336 | + } | |
| 337 | + | |
| 338 | + constraint_css_class = date_constraint.css_classes[ constraint_class_index ].trim(); | |
| 339 | + if ( | |
| 340 | + '' === constraint_css_class | |
| 341 | + || 'date_available' === constraint_css_class | |
| 342 | + || 'date_user_unavailable' === constraint_css_class | |
| 343 | + || ! /^-?[_a-zA-Z]+[_a-zA-Z0-9-]*$/.test( constraint_css_class ) | |
| 344 | + ) { | |
| 345 | + continue; | |
| 346 | + } | |
| 347 | + | |
| 348 | + if ( -1 === result_css_classes.indexOf( constraint_css_class ) ) { | |
| 349 | + result_css_classes.push( constraint_css_class ); | |
| 350 | + } | |
| 351 | + } | |
| 352 | + } | |
| 353 | + | |
| 354 | + return { | |
| 355 | + 'is_selectable': Boolean( is_natively_selectable ) && true !== date_constraint.is_blocked, | |
| 356 | + 'css_classes': result_css_classes | |
| 357 | + }; | |
| 358 | + } | |
| 359 | + | |
| 360 | + | |
| 361 | + /** | |
| 362 | + * Apply CSS to calendar date cells | |
| 363 | + * | |
| 204 | 364 | * @param date - JavaScript Date Obj: Mon Dec 11 2023 00:00:00 |
| 205 | 365 | * GMT+0200 (Eastern European Standard Time) |
| 206 | 366 | * @param calendar_params_arr - Calendar Settings Object: { |
| 207 | 367 | * "resource_id": 4 |
| @@ -245,16 +405,18 @@ | ||
| 245 | 405 | } |
| 246 | 406 | } |
| 247 | 407 | |
| 248 | 408 | |
| 249 | - var is_day_selectable = false; | |
| 409 | + var is_day_selectable = false; | |
| 410 | + var date_constraint_result; | |
| 250 | 411 | |
| 251 | 412 | // If something not defined, then this date closed --------------------------------------------------------------- // FixIn: 10.12.4.6. |
| 252 | - if ( (false === date_bookings_obj) || ('undefined' === typeof (date_bookings_obj[resource_id])) ) { | |
| 253 | - | |
| 254 | - css_classes__for_date.push( 'date_user_unavailable' ); | |
| 255 | - | |
| 256 | - return [ is_day_selectable, css_classes__for_date.join(' ') ]; | |
| 413 | + if ( (false === date_bookings_obj) || ('undefined' === typeof (date_bookings_obj[resource_id])) ) { | |
| 414 | + | |
| 415 | + css_classes__for_date.push( 'date_user_unavailable' ); | |
| 416 | + date_constraint_result = wpbc_calendar__evaluate_date_constraints( resource_id, sql_class_day, is_day_selectable, css_classes__for_date ); | |
| 417 | + | |
| 418 | + return [ date_constraint_result.is_selectable, date_constraint_result.css_classes.join( ' ' ) ]; | |
| 257 | 419 | } |
| 258 | 420 | |
| 259 | 421 | |
| 260 | 422 | // ----------------------------------------------------------------------------------------------------------------- |
| @@ -259,8 +421,9 @@ | ||
| 259 | 421 | |
| 260 | 422 | // ----------------------------------------------------------------------------------------------------------------- |
| 261 | 423 | // date_bookings_obj - Defined. Dates can be selectable. |
| 262 | 424 | // ----------------------------------------------------------------------------------------------------------------- |
| 425 | + var booking_statuses_arr = wpbc_get_booking_statuses__as_arr( date_bookings_obj ); | |
| 263 | 426 | |
| 264 | 427 | // ----------------------------------------------------------------------------------------------------------------- |
| 265 | 428 | // Add season names to the day CSS classes -- it is required for correct work of conditional fields -------------- |
| 266 | 429 | var season_names_arr = _wpbc.seasons__get_for_date( resource_id, sql_class_day ); |
| @@ -301,28 +464,38 @@ | ||
| 301 | 464 | |
| 302 | 465 | case 'season_filter': |
| 303 | 466 | css_classes__for_date.push( 'date_user_unavailable', 'season_unavailable' ); |
| 304 | 467 | date_bookings_obj[ 'summary']['status_for_bookings' ] = ''; // Reset booking status color for possible old bookings on this date |
| 468 | + date_bookings_obj[ 'summary']['status_for_bookings_arr' ] = []; | |
| 469 | + booking_statuses_arr = []; | |
| 305 | 470 | break; |
| 306 | 471 | |
| 307 | 472 | case 'resource_availability': |
| 308 | 473 | css_classes__for_date.push( 'date_user_unavailable', 'resource_unavailable' ); |
| 309 | 474 | date_bookings_obj[ 'summary']['status_for_bookings' ] = ''; // Reset booking status color for possible old bookings on this date |
| 475 | + date_bookings_obj[ 'summary']['status_for_bookings_arr' ] = []; | |
| 476 | + booking_statuses_arr = []; | |
| 310 | 477 | break; |
| 311 | 478 | |
| 312 | 479 | case 'weekday_unavailable': |
| 313 | 480 | css_classes__for_date.push( 'date_user_unavailable', 'weekday_unavailable' ); |
| 314 | 481 | date_bookings_obj[ 'summary']['status_for_bookings' ] = ''; // Reset booking status color for possible old bookings on this date |
| 482 | + date_bookings_obj[ 'summary']['status_for_bookings_arr' ] = []; | |
| 483 | + booking_statuses_arr = []; | |
| 315 | 484 | break; |
| 316 | 485 | |
| 317 | 486 | case 'from_today_unavailable': |
| 318 | 487 | css_classes__for_date.push( 'date_user_unavailable', 'from_today_unavailable' ); |
| 319 | 488 | date_bookings_obj[ 'summary']['status_for_bookings' ] = ''; // Reset booking status color for possible old bookings on this date |
| 489 | + date_bookings_obj[ 'summary']['status_for_bookings_arr' ] = []; | |
| 490 | + booking_statuses_arr = []; | |
| 320 | 491 | break; |
| 321 | 492 | |
| 322 | 493 | case 'limit_available_from_today': |
| 323 | 494 | css_classes__for_date.push( 'date_user_unavailable', 'limit_available_from_today' ); |
| 324 | 495 | date_bookings_obj[ 'summary']['status_for_bookings' ] = ''; // Reset booking status color for possible old bookings on this date |
| 496 | + date_bookings_obj[ 'summary']['status_for_bookings_arr' ] = []; | |
| 497 | + booking_statuses_arr = []; | |
| 325 | 498 | break; |
| 326 | 499 | |
| 327 | 500 | case 'change_over': |
| 328 | 501 | /* |
| @@ -334,12 +507,12 @@ | ||
| 334 | 507 | */ |
| 335 | 508 | |
| 336 | 509 | css_classes__for_date.push( 'timespartly', 'check_in_time', 'check_out_time' ); |
| 337 | 510 | // FixIn: 10.0.0.2. |
| 338 | - if ( date_bookings_obj[ 'summary' ][ 'status_for_bookings' ].indexOf( 'approved_pending' ) > -1 ){ | |
| 511 | + if ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved_pending' ) ){ | |
| 339 | 512 | css_classes__for_date.push( 'check_out_time_date_approved', 'check_in_time_date2approve' ); |
| 340 | 513 | } |
| 341 | - if ( date_bookings_obj[ 'summary' ][ 'status_for_bookings' ].indexOf( 'pending_approved' ) > -1 ){ | |
| 514 | + if ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_approved' ) ){ | |
| 342 | 515 | css_classes__for_date.push( 'check_out_time_date2approve', 'check_in_time_date_approved' ); |
| 343 | 516 | } |
| 344 | 517 | break; |
| 345 | 518 | |
| @@ -346,11 +519,11 @@ | ||
| 346 | 519 | case 'check_in': |
| 347 | 520 | css_classes__for_date.push( 'timespartly', 'check_in_time' ); |
| 348 | 521 | |
| 349 | 522 | // FixIn: 9.9.0.33. |
| 350 | - if ( date_bookings_obj[ 'summary' ][ 'status_for_bookings' ].indexOf( 'pending' ) > -1 ){ | |
| 523 | + if ( wpbc_booking_statuses__has_part( booking_statuses_arr, 'pending' ) ){ | |
| 351 | 524 | css_classes__for_date.push( 'check_in_time_date2approve' ); |
| 352 | - } else if ( date_bookings_obj[ 'summary' ][ 'status_for_bookings' ].indexOf( 'approved' ) > -1 ){ | |
| 525 | + } else if ( wpbc_booking_statuses__has_part( booking_statuses_arr, 'approved' ) ){ | |
| 353 | 526 | css_classes__for_date.push( 'check_in_time_date_approved' ); |
| 354 | 527 | } |
| 355 | 528 | break; |
| 356 | 529 | |
| @@ -357,11 +530,11 @@ | ||
| 357 | 530 | case 'check_out': |
| 358 | 531 | css_classes__for_date.push( 'timespartly', 'check_out_time' ); |
| 359 | 532 | |
| 360 | 533 | // FixIn: 9.9.0.33. |
| 361 | - if ( date_bookings_obj[ 'summary' ][ 'status_for_bookings' ].indexOf( 'pending' ) > -1 ){ | |
| 534 | + if ( wpbc_booking_statuses__has_part( booking_statuses_arr, 'pending' ) ){ | |
| 362 | 535 | css_classes__for_date.push( 'check_out_time_date2approve' ); |
| 363 | - } else if ( date_bookings_obj[ 'summary' ][ 'status_for_bookings' ].indexOf( 'approved' ) > -1 ){ | |
| 536 | + } else if ( wpbc_booking_statuses__has_part( booking_statuses_arr, 'approved' ) ){ | |
| 364 | 537 | css_classes__for_date.push( 'check_out_time_date_approved' ); |
| 365 | 538 | } |
| 366 | 539 | break; |
| 367 | 540 | |
| @@ -375,49 +548,35 @@ | ||
| 375 | 548 | if ( 'available' != date_bookings_obj[ 'summary']['status_for_day' ] ){ |
| 376 | 549 | |
| 377 | 550 | var is_set_pending_days_selectable = _wpbc.calendar__get_param_value( resource_id, 'pending_days_selectable' ); // set pending days selectable // FixIn: 8.6.1.18. |
| 378 | 551 | |
| 379 | - switch ( date_bookings_obj[ 'summary']['status_for_bookings' ] ){ | |
| 380 | - | |
| 381 | - case '': | |
| 382 | - // Usually it's means that day is available or unavailable without the bookings | |
| 383 | - break; | |
| 384 | - | |
| 385 | - case 'pending': | |
| 386 | - css_classes__for_date.push( 'date2approve' ); | |
| 387 | - is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable; | |
| 388 | - break; | |
| 389 | - | |
| 390 | - case 'approved': | |
| 391 | - css_classes__for_date.push( 'date_approved' ); | |
| 392 | - break; | |
| 393 | - | |
| 394 | - // Situations for "change-over" days: ---------------------------------------------------------------------- | |
| 395 | - case 'pending_pending': | |
| 396 | - css_classes__for_date.push( 'check_out_time_date2approve', 'check_in_time_date2approve' ); | |
| 397 | - is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable; | |
| 398 | - break; | |
| 399 | - | |
| 400 | - case 'pending_approved': | |
| 401 | - css_classes__for_date.push( 'check_out_time_date2approve', 'check_in_time_date_approved' ); | |
| 402 | - is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable; | |
| 403 | - break; | |
| 404 | - | |
| 405 | - case 'approved_pending': | |
| 406 | - css_classes__for_date.push( 'check_out_time_date_approved', 'check_in_time_date2approve' ); | |
| 407 | - is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable; | |
| 408 | - break; | |
| 409 | - | |
| 410 | - case 'approved_approved': | |
| 411 | - css_classes__for_date.push( 'check_out_time_date_approved', 'check_in_time_date_approved' ); | |
| 412 | - break; | |
| 413 | - | |
| 414 | - default: | |
| 415 | - | |
| 552 | + if ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending' ) ){ | |
| 553 | + css_classes__for_date.push( 'date2approve' ); | |
| 554 | + is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable; | |
| 416 | 555 | } |
| 556 | + if ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved' ) ){ | |
| 557 | + css_classes__for_date.push( 'date_approved' ); | |
| 558 | + } | |
| 559 | + if ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_pending' ) ){ | |
| 560 | + css_classes__for_date.push( 'check_out_time_date2approve', 'check_in_time_date2approve' ); | |
| 561 | + is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable; | |
| 562 | + } | |
| 563 | + if ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_approved' ) ){ | |
| 564 | + css_classes__for_date.push( 'check_out_time_date2approve', 'check_in_time_date_approved' ); | |
| 565 | + is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable; | |
| 566 | + } | |
| 567 | + if ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved_pending' ) ){ | |
| 568 | + css_classes__for_date.push( 'check_out_time_date_approved', 'check_in_time_date2approve' ); | |
| 569 | + is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable; | |
| 570 | + } | |
| 571 | + if ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved_approved' ) ){ | |
| 572 | + css_classes__for_date.push( 'check_out_time_date_approved', 'check_in_time_date_approved' ); | |
| 573 | + } | |
| 417 | 574 | } |
| 418 | 575 | |
| 419 | - return [ is_day_selectable, css_classes__for_date.join( ' ' ) ]; | |
| 576 | + date_constraint_result = wpbc_calendar__evaluate_date_constraints( resource_id, sql_class_day, is_day_selectable, css_classes__for_date ); | |
| 577 | + | |
| 578 | + return [ date_constraint_result.is_selectable, date_constraint_result.css_classes.join( ' ' ) ]; | |
| 420 | 579 | } |
| 421 | 580 | |
| 422 | 581 | |
| 423 | 582 | /** |
| @@ -469,8 +628,10 @@ | ||
| 469 | 628 | |
| 470 | 629 | // U n h o v e r i n g in UNSELECTABLE_CALENDAR ------------------------------------------------------------ |
| 471 | 630 | var is_unselectable_calendar = ( jQuery( '#calendar_booking_unselectable' + resource_id ).length > 0); // FixIn: 8.0.1.2. |
| 472 | 631 | var is_booking_form_exist = ( jQuery( '#booking_form_div' + resource_id ).length > 0 ); |
| 632 | + var is_add_booking_modal_calendar = ( jQuery( '#calendar_booking' + resource_id ).closest( '#wpbc_modal__add_booking__section' ).length > 0 ); | |
| 633 | + var is_admin_calendar_preview = ( jQuery( '#calendar_booking' + resource_id ).closest( '[data-wpbc-admin-calendar-preview="1"]' ).length > 0 ); | |
| 473 | 634 | |
| 474 | 635 | if ( ( is_unselectable_calendar ) && ( ! is_booking_form_exist ) ){ |
| 475 | 636 | |
| 476 | 637 | /** |
| @@ -490,13 +651,15 @@ | ||
| 490 | 651 | |
| 491 | 652 | // D a y s H o v e r i n g ------------------------------------------------------------------------------------ |
| 492 | 653 | if ( |
| 493 | 654 | ( location.href.indexOf( 'page=wpbc' ) == -1 ) |
| 494 | - || ( location.href.indexOf( 'page=wpbc-new' ) > 0 ) | |
| 655 | + || ( ( location.href.indexOf( 'page=wpbc' ) > 0 ) && ( location.href.indexOf( 'tab=add-booking' ) > 0 ) ) | |
| 656 | + || ( is_add_booking_modal_calendar ) | |
| 657 | + || ( is_admin_calendar_preview ) | |
| 495 | 658 | || ( location.href.indexOf( 'page=wpbc-setup' ) > 0 ) |
| 496 | 659 | || ( location.href.indexOf( 'page=wpbc-availability' ) > 0 ) |
| 497 | 660 | || ( ( location.href.indexOf( 'page=wpbc-settings' ) > 0 ) && |
| 498 | - ( location.href.indexOf( '&tab=form' ) > 0 ) | |
| 661 | + ( location.href.indexOf( 'tab=builder_booking_form' ) > 0 ) | |
| 499 | 662 | ) |
| 500 | 663 | ){ |
| 501 | 664 | // The same as dates selection, but for days hovering |
| 502 | 665 | |
| @@ -501,8 +664,10 @@ | ||
| 501 | 664 | // The same as dates selection, but for days hovering |
| 502 | 665 | |
| 503 | 666 | if ( 'function' == typeof( wpbc__calendar__do_days_highlight__bs ) ){ |
| 504 | 667 | wpbc__calendar__do_days_highlight__bs( sql_class_day, date, resource_id ); |
| 668 | + } else if ( 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ) { | |
| 669 | + wpbc__calendar__do_days_highlight__unrestricted_range( sql_class_day, date, resource_id ); | |
| 505 | 670 | } |
| 506 | 671 | } |
| 507 | 672 | |
| 508 | 673 | } |
| @@ -508,8 +673,273 @@ | ||
| 508 | 673 | } |
| 509 | 674 | |
| 510 | 675 | |
| 511 | 676 | /** |
| 677 | + * Highlight an unrestricted proposed range after the first click when Business Small is not loaded. | |
| 678 | + * | |
| 679 | + * @param {string} sql_class_day Hovered date in SQL format. | |
| 680 | + * @param {Date} date Hovered date. | |
| 681 | + * @param {number|string} resource_id Booking resource ID. | |
| 682 | + * @returns {boolean} True when the complete proposed range is available and highlighted. | |
| 683 | + */ | |
| 684 | + function wpbc__calendar__do_days_highlight__unrestricted_range( sql_class_day, date, resource_id ){ | |
| 685 | + | |
| 686 | + var inst = wpbc_calendar__get_inst( resource_id ); | |
| 687 | + var range_start; | |
| 688 | + var range_end; | |
| 689 | + var working_date; | |
| 690 | + var td_overs = []; | |
| 691 | + | |
| 692 | + wpbc_calendars__clear_days_highlighting( resource_id ); | |
| 693 | + | |
| 694 | + if ( | |
| 695 | + null === inst | |
| 696 | + || 'dynamic' !== _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) | |
| 697 | + ) { | |
| 698 | + return false; | |
| 699 | + } | |
| 700 | + | |
| 701 | + // Before a range starts, or after it is complete, preview the hovered date as the possible first day. | |
| 702 | + if ( 1 !== inst.dates.length || ! inst.dates[ 0 ] ) { | |
| 703 | + if ( ! wpbc_is_this_day_selectable( resource_id, sql_class_day ) ) { | |
| 704 | + return false; | |
| 705 | + } | |
| 706 | + | |
| 707 | + jQuery( '#calendar_booking' + resource_id + ' .cal4date-' + wpbc__get__td_class_date( date ) ) | |
| 708 | + .addClass( 'datepick-days-cell-over' ); | |
| 709 | + return true; | |
| 710 | + } | |
| 711 | + | |
| 712 | + range_start = new Date( inst.dates[ 0 ].getFullYear(), inst.dates[ 0 ].getMonth(), inst.dates[ 0 ].getDate(), 0, 0, 0 ); | |
| 713 | + range_end = new Date( date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0 ); | |
| 714 | + | |
| 715 | + // Datepick only permits a check-out date on/after the first click. Do not preview a backward range. | |
| 716 | + if ( range_end.getTime() < range_start.getTime() ) { | |
| 717 | + return false; | |
| 718 | + } | |
| 719 | + | |
| 720 | + working_date = new Date( range_start.getTime() ); | |
| 721 | + while ( working_date.getTime() <= range_end.getTime() ) { | |
| 722 | + sql_class_day = wpbc__get__sql_class_date( working_date ); | |
| 723 | + if ( ! wpbc_is_this_day_selectable( resource_id, sql_class_day ) ) { | |
| 724 | + return false; | |
| 725 | + } | |
| 726 | + | |
| 727 | + td_overs.push( '#calendar_booking' + resource_id + ' .cal4date-' + wpbc__get__td_class_date( working_date ) ); | |
| 728 | + working_date.setDate( working_date.getDate() + 1 ); | |
| 729 | + } | |
| 730 | + | |
| 731 | + if ( td_overs.length ) { | |
| 732 | + jQuery( td_overs.join( ',' ) ).addClass( 'datepick-days-cell-over' ); | |
| 733 | + } | |
| 734 | + | |
| 735 | + return true; | |
| 736 | + } | |
| 737 | + | |
| 738 | + | |
| 739 | + /** | |
| 740 | + * Complete an unrestricted two-click range when the Business Small range engine is not loaded. | |
| 741 | + * The hidden booking field must contain every date because Free time/hint handlers consume a comma-separated list. | |
| 742 | + * | |
| 743 | + * @param {string} selected_dates Datepick's endpoint string. | |
| 744 | + * @param {number|string} resource_id Booking resource ID. | |
| 745 | + * @returns {boolean} True after a complete valid range, false while incomplete or invalid. | |
| 746 | + */ | |
| 747 | + function wpbc__calendar__do_days_select__unrestricted_range( selected_dates, resource_id ){ | |
| 748 | + | |
| 749 | + var inst = wpbc_calendar__get_inst( resource_id ); | |
| 750 | + var range_start; | |
| 751 | + var range_end; | |
| 752 | + var current_date; | |
| 753 | + var selected_range = []; | |
| 754 | + | |
| 755 | + if ( null === inst ) { | |
| 756 | + return false; | |
| 757 | + } | |
| 758 | + | |
| 759 | + // Datepick writes "start - start" after click one. Keep its internal endpoint, but do not expose a submittable range yet. | |
| 760 | + if ( true === inst.stayOpen || inst.dates.length < 2 || ! inst.dates[ 0 ] || ! inst.dates[ 1 ] ) { | |
| 761 | + jQuery( '#date_booking' + resource_id ).val( '' ); | |
| 762 | + return false; | |
| 763 | + } | |
| 764 | + | |
| 765 | + range_start = new Date( inst.dates[ 0 ].getFullYear(), inst.dates[ 0 ].getMonth(), inst.dates[ 0 ].getDate(), 0, 0, 0 ); | |
| 766 | + range_end = new Date( inst.dates[ 1 ].getFullYear(), inst.dates[ 1 ].getMonth(), inst.dates[ 1 ].getDate(), 0, 0, 0 ); | |
| 767 | + | |
| 768 | + // Defensive normalization. Datepick normally prevents an end date before the first endpoint. | |
| 769 | + if ( range_end.getTime() < range_start.getTime() ) { | |
| 770 | + current_date = range_start; | |
| 771 | + range_start = range_end; | |
| 772 | + range_end = current_date; | |
| 773 | + } | |
| 774 | + | |
| 775 | + current_date = new Date( range_start.getTime() ); | |
| 776 | + while ( current_date.getTime() <= range_end.getTime() ) { | |
| 777 | + if ( ! wpbc_is_this_day_selectable( resource_id, wpbc__get__sql_class_date( current_date ) ) ) { | |
| 778 | + wpbc_calendar__unselect_all_dates( resource_id ); | |
| 779 | + if ( 'function' === typeof ( wpbc_front_end__show_message__warning ) ) { | |
| 780 | + wpbc_front_end__show_message__warning( | |
| 781 | + '#booking_form_div' + resource_id + ' .bk_calendar_frame', | |
| 782 | + _wpbc.get_message( 'message_range_selection_has_unavailable_dates' ), | |
| 783 | + 'text' | |
| 784 | + ); | |
| 785 | + } | |
| 786 | + return false; | |
| 787 | + } | |
| 788 | + | |
| 789 | + selected_range.push( jQuery.datepick._formatDate( inst, current_date ) ); | |
| 790 | + current_date.setDate( current_date.getDate() + 1 ); | |
| 791 | + } | |
| 792 | + | |
| 793 | + jQuery( '#date_booking' + resource_id ).val( selected_range.join( ', ' ) ); | |
| 794 | + return true; | |
| 795 | + } | |
| 796 | + | |
| 797 | + | |
| 798 | + /** | |
| 799 | + * Prepare a collapsed range-selection guidance note for one calendar. | |
| 800 | + * Keeping one reusable node avoids repeated insertion while inactive guidance occupies no layout space. | |
| 801 | + * | |
| 802 | + * @param {number|string} resource_id Booking resource ID. | |
| 803 | + * @returns {jQuery} The prepared note, or an empty collection when guidance is unavailable. | |
| 804 | + */ | |
| 805 | + function wpbc_range_selection__prepare_guidance( resource_id ){ | |
| 806 | + | |
| 807 | + var is_enabled = _wpbc.calendar__get_param_value( resource_id, 'range_selection_guidance_is_enabled' ); | |
| 808 | + var $booking_form = jQuery( '#booking_form_div' + resource_id ); | |
| 809 | + var $calendar_frame; | |
| 810 | + var $note = jQuery( '#wpbc_range_selection_guidance' + resource_id ); | |
| 811 | + | |
| 812 | + if ( | |
| 813 | + false === is_enabled | |
| 814 | + || 0 === is_enabled | |
| 815 | + || '0' === is_enabled | |
| 816 | + || 'off' === String( is_enabled ).toLowerCase() | |
| 817 | + || 'false' === String( is_enabled ).toLowerCase() | |
| 818 | + || 'dynamic' !== _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) | |
| 819 | + || 0 === $booking_form.length | |
| 820 | + ) { | |
| 821 | + $note.remove(); | |
| 822 | + return jQuery(); | |
| 823 | + } | |
| 824 | + | |
| 825 | + if ( 0 < $note.length ) { | |
| 826 | + $note.stop( true, true ).hide().attr( 'aria-hidden', 'true' ); | |
| 827 | + return $note; | |
| 828 | + } | |
| 829 | + | |
| 830 | + $calendar_frame = $booking_form.find( '.block_hints.datepick' ).first(); | |
| 831 | + if ( 0 === $calendar_frame.length ) { | |
| 832 | + $calendar_frame = $booking_form.find( '.wpbc_cal_powered_by' ).first(); | |
| 833 | + if ( 0 === $calendar_frame.length ) { | |
| 834 | + $calendar_frame = $booking_form.find( '.bk_calendar_frame' ).first(); | |
| 835 | + if ( 0 === $calendar_frame.length ) { | |
| 836 | + return jQuery(); | |
| 837 | + } | |
| 838 | + } | |
| 839 | + } | |
| 840 | + | |
| 841 | + $note = jQuery( '<div>', { | |
| 842 | + 'id': 'wpbc_range_selection_guidance' + resource_id, | |
| 843 | + 'class': 'wpbc_range_selection_guidance wpbc_front_end__message wpbc_fe_message_info', | |
| 844 | + 'role': 'status', | |
| 845 | + 'aria-live': 'polite', | |
| 846 | + 'aria-hidden': 'true', | |
| 847 | + 'style': 'display: none;' | |
| 848 | + } ); | |
| 849 | + $note.append( jQuery( '<i>', { | |
| 850 | + 'class': 'menu_icon icon-1x wpbc_icn_info_outline', | |
| 851 | + 'aria-hidden': 'true' | |
| 852 | + } ) ); | |
| 853 | + $note.append( jQuery( '<span>' ).text( _wpbc.get_message( 'message_range_selection_click_last_date' ) ) ); | |
| 854 | + $calendar_frame.after( $note ); | |
| 855 | + | |
| 856 | + return $note; | |
| 857 | + } | |
| 858 | + | |
| 859 | + | |
| 860 | + /** | |
| 861 | + * Get the range-guidance animation duration for the current motion preference. | |
| 862 | + * | |
| 863 | + * @returns {number} Animation duration in milliseconds, or zero when reduced motion is preferred. | |
| 864 | + */ | |
| 865 | + function wpbc_range_selection__get_guidance_animation_duration(){ | |
| 866 | + | |
| 867 | + if ( | |
| 868 | + 'function' === typeof window.matchMedia | |
| 869 | + && window.matchMedia( '(prefers-reduced-motion: reduce)' ).matches | |
| 870 | + ) { | |
| 871 | + return 0; | |
| 872 | + } | |
| 873 | + | |
| 874 | + return 160; | |
| 875 | + } | |
| 876 | + | |
| 877 | + | |
| 878 | + /** | |
| 879 | + * Collapse the range-selection guidance so it does not reserve empty layout space. | |
| 880 | + * | |
| 881 | + * @param {number|string} resource_id Booking resource ID. | |
| 882 | + */ | |
| 883 | + function wpbc_range_selection__hide_guidance( resource_id ){ | |
| 884 | + | |
| 885 | + jQuery( '#wpbc_range_selection_guidance' + resource_id ) | |
| 886 | + .attr( 'aria-hidden', 'true' ) | |
| 887 | + .stop( true, true ) | |
| 888 | + .slideUp( wpbc_range_selection__get_guidance_animation_duration() ); | |
| 889 | + } | |
| 890 | + | |
| 891 | + | |
| 892 | + /** | |
| 893 | + * Show the prepared informational note after the first range click. | |
| 894 | + * The calendar parameter can be set to false by PHP or JavaScript to suppress this UI per resource. | |
| 895 | + * | |
| 896 | + * @param {number|string} resource_id Booking resource ID. | |
| 897 | + * @returns {boolean} True when the note was shown. | |
| 898 | + */ | |
| 899 | + function wpbc_range_selection__show_guidance( resource_id ){ | |
| 900 | + | |
| 901 | + var $note = wpbc_range_selection__prepare_guidance( resource_id ); | |
| 902 | + | |
| 903 | + if ( 0 === $note.length ) { | |
| 904 | + return false; | |
| 905 | + } | |
| 906 | + | |
| 907 | + $note | |
| 908 | + .stop( true, true ) | |
| 909 | + .slideDown( wpbc_range_selection__get_guidance_animation_duration() ) | |
| 910 | + .removeAttr( 'aria-hidden' ); | |
| 911 | + $note.find( 'span' ).text( _wpbc.get_message( 'message_range_selection_click_last_date' ) ); | |
| 912 | + | |
| 913 | + return true; | |
| 914 | + } | |
| 915 | + | |
| 916 | + | |
| 917 | + /** | |
| 918 | + * Synchronize the guidance with Datepick's two-click range state. | |
| 919 | + * | |
| 920 | + * @param {number|string} resource_id Booking resource ID. | |
| 921 | + */ | |
| 922 | + function wpbc_range_selection__sync_guidance( resource_id ){ | |
| 923 | + | |
| 924 | + var inst = wpbc_calendar__get_inst( resource_id ); | |
| 925 | + | |
| 926 | + if ( | |
| 927 | + null !== inst | |
| 928 | + && 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) | |
| 929 | + && true === inst.stayOpen | |
| 930 | + && 1 === inst.dates.length | |
| 931 | + && inst.dates[ 0 ] | |
| 932 | + ) { | |
| 933 | + wpbc_range_selection__show_guidance( resource_id ); | |
| 934 | + return; | |
| 935 | + } | |
| 936 | + | |
| 937 | + wpbc_range_selection__hide_guidance( resource_id ); | |
| 938 | + } | |
| 939 | + | |
| 940 | + | |
| 941 | + /** | |
| 512 | 942 | * Select calendar date cells |
| 513 | 943 | * |
| 514 | 944 | * @param date - JavaScript Date Obj: Mon Dec 11 2023 00:00:00 |
| 515 | 945 | * GMT+0200 (Eastern European Standard Time) |
| @@ -534,10 +964,16 @@ | ||
| 534 | 964 | |
| 535 | 965 | jQuery( '#date_booking' + resource_id ).val( date ); // Add selected dates to hidden textarea |
| 536 | 966 | |
| 537 | 967 | |
| 538 | - if ( 'function' === typeof (wpbc__calendar__do_days_select__bs) ){ wpbc__calendar__do_days_select__bs( date, resource_id ); } | |
| 968 | + if ( 'function' === typeof (wpbc__calendar__do_days_select__bs) ) { | |
| 969 | + wpbc__calendar__do_days_select__bs( date, resource_id ); | |
| 970 | + } else if ( 'dynamic' === _wpbc.calendar__get_param_value( resource_id, 'days_select_mode' ) ) { | |
| 971 | + wpbc__calendar__do_days_select__unrestricted_range( date, resource_id ); | |
| 972 | + } | |
| 539 | 973 | |
| 974 | + wpbc_range_selection__sync_guidance( resource_id ); | |
| 975 | + | |
| 540 | 976 | wpbc_disable_time_fields_in_booking_form( resource_id ); |
| 541 | 977 | |
| 542 | 978 | // Hook -- trigger day selection ----------------------------------------------------------------------------------- |
| 543 | 979 | var mouse_clicked_dates = date; // Can be: "05.10.2023 - 07.10.2023" | "10.10.2023 - 10.10.2023" | |
| @@ -604,8 +1040,17 @@ | ||
| 604 | 1040 | var is_check_in; |
| 605 | 1041 | |
| 606 | 1042 | var today_time__real = new Date( _wpbc.get_other_param( 'time_local_arr' )[0], ( parseInt( _wpbc.get_other_param( 'time_local_arr' )[1] ) - 1), _wpbc.get_other_param( 'time_local_arr' )[2], _wpbc.get_other_param( 'time_local_arr' )[3], _wpbc.get_other_param( 'time_local_arr' )[4], 0 ); |
| 607 | 1043 | 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 ); |
| 1044 | + var allow_past_context = _wpbc.get_other_param( 'this_page_allow_past' ); | |
| 1045 | + var edit_booking_hash_context = _wpbc.get_other_param( 'this_page_booking_hash' ); | |
| 1046 | + var is_allow_past_context = | |
| 1047 | + ( '1' === String( allow_past_context ) ) | |
| 1048 | + || ( 1 === allow_past_context ) | |
| 1049 | + || ( true === allow_past_context ) | |
| 1050 | + || ( '' !== String( edit_booking_hash_context || '' ) ) | |
| 1051 | + || ( location.href.indexOf( 'booking_hash' ) > -1 ) | |
| 1052 | + || ( location.href.indexOf( 'allow_past' ) > -1 ); | |
| 608 | 1053 | |
| 609 | 1054 | // 4. Loop all time Fields options // FixIn: 10.3.0.2. |
| 610 | 1055 | for ( let field_key = 0; field_key < time_fields_obj_arr.length; field_key++ ){ |
| 611 | 1056 | |
| @@ -618,11 +1063,12 @@ | ||
| 618 | 1063 | |
| 619 | 1064 | // Get Date: '2023-08-18'. |
| 620 | 1065 | sql_date = selected_dates_arr[i]; |
| 621 | 1066 | |
| 622 | - var is_time_in_past = wpbc_check_is_time_in_past( today_time__shift, sql_date, time_fields_obj ); | |
| 1067 | + var is_time_in_past = is_allow_past_context ? false : wpbc_check_is_time_in_past( today_time__shift, sql_date, time_fields_obj ); | |
| 623 | 1068 | // Exception for 'End Time' field, when selected several dates. // FixIn: 10.14.1.5. |
| 624 | - if ( ('On' !== _wpbc.calendar__get_param_value( resource_id, 'booking_recurrent_time' )) && | |
| 1069 | + if ( ( ! is_allow_past_context ) && | |
| 1070 | + ('On' !== _wpbc.calendar__get_param_value( resource_id, 'booking_recurrent_time' )) && | |
| 625 | 1071 | (-1 !== time_fields_obj.name.indexOf( 'endtime' )) && |
| 626 | 1072 | (selected_dates_arr.length > 1) |
| 627 | 1073 | ) { |
| 628 | 1074 | is_time_in_past = wpbc_check_is_time_in_past( today_time__shift, selected_dates_arr[(selected_dates_arr.length - 1)], time_fields_obj ); |
| @@ -1144,8 +1590,10 @@ | ||
| 1144 | 1590 | if ( 'undefined' === typeof (resource_id) ){ |
| 1145 | 1591 | resource_id = '1'; |
| 1146 | 1592 | } |
| 1147 | 1593 | |
| 1594 | + wpbc_range_selection__hide_guidance( resource_id ); | |
| 1595 | + | |
| 1148 | 1596 | var inst = wpbc_calendar__get_inst( resource_id ) |
| 1149 | 1597 | |
| 1150 | 1598 | if ( null !== inst ){ |
| 1151 | 1599 | |
| @@ -1213,45 +1661,70 @@ | ||
| 1213 | 1661 | } |
| 1214 | 1662 | return false; |
| 1215 | 1663 | } |
| 1216 | 1664 | |
| 1217 | - /** | |
| 1218 | - * Is this date selectable in calendar (mainly it's means AVAILABLE date) | |
| 1219 | - * | |
| 1220 | - * @param {int|string} resource_id 1 | |
| 1221 | - * @param {string} sql_class_day '2023-08-11' | |
| 1222 | - * @returns {boolean} true | false | |
| 1223 | - */ | |
| 1224 | - function wpbc_is_this_day_selectable( resource_id, sql_class_day ){ | |
| 1665 | + /** | |
| 1666 | + * Determine native date selectability without optional date constraints. | |
| 1667 | + * | |
| 1668 | + * This helper exposes the canonical calendar result for algorithms, such as | |
| 1669 | + * gap discovery, that must not consume their own derived constraints. It does | |
| 1670 | + * not trigger wpbc_calendar__apply_date_constraints and must remain free of | |
| 1671 | + * optional-module state. | |
| 1672 | + * | |
| 1673 | + * @param {number|string} resource_id Booking resource ID. | |
| 1674 | + * @param {string} sql_date Calendar date in YYYY-MM-DD format. | |
| 1675 | + * @returns {boolean} True when core calendar data marks the date selectable. | |
| 1676 | + */ | |
| 1677 | + function wpbc_is_this_day_selectable__raw( resource_id, sql_date ){ | |
| 1678 | + | |
| 1679 | + // Get Data -------------------------------------------------------------------------------------------------------- | |
| 1680 | + var date_bookings_obj = _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_date ); | |
| 1681 | + var is_day_selectable = false; | |
| 1682 | + | |
| 1683 | + if ( date_bookings_obj && 'undefined' !== typeof ( date_bookings_obj[ 'day_availability' ] ) ) { | |
| 1684 | + is_day_selectable = ( parseInt( date_bookings_obj[ 'day_availability' ] ) > 0 ); | |
| 1685 | + | |
| 1686 | + if ( 'undefined' !== typeof ( date_bookings_obj[ 'summary' ] ) && 'available' != date_bookings_obj[ 'summary']['status_for_day' ] ){ | |
| 1687 | + | |
| 1688 | + 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. | |
| 1689 | + var booking_statuses_arr = wpbc_get_booking_statuses__as_arr( date_bookings_obj ); | |
| 1690 | + | |
| 1691 | + if ( | |
| 1692 | + ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending' ) ) | |
| 1693 | + || ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_pending' ) ) | |
| 1694 | + || ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_approved' ) ) | |
| 1695 | + || ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved_pending' ) ) | |
| 1696 | + ){ | |
| 1697 | + is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable; | |
| 1698 | + } | |
| 1699 | + } | |
| 1700 | + } | |
| 1701 | + | |
| 1702 | + return Boolean( is_day_selectable ); | |
| 1703 | + } | |
| 1704 | + | |
| 1705 | + /** | |
| 1706 | + * Determine final date selectability after native and optional constraints. | |
| 1707 | + * | |
| 1708 | + * Native unavailability returns immediately. Optional modules may therefore | |
| 1709 | + * only block a natively selectable date and can never reopen a core-unavailable | |
| 1710 | + * date. | |
| 1711 | + * | |
| 1712 | + * @param {number|string} resource_id Booking resource ID. | |
| 1713 | + * @param {string} sql_date Calendar date in YYYY-MM-DD format. | |
| 1714 | + * @returns {boolean} True when the date remains selectable after constraints. | |
| 1715 | + */ | |
| 1716 | + function wpbc_is_this_day_selectable( resource_id, sql_date ){ | |
| 1717 | + | |
| 1718 | + var is_natively_selectable = wpbc_is_this_day_selectable__raw( resource_id, sql_date ); | |
| 1719 | + | |
| 1720 | + if ( ! is_natively_selectable ) { | |
| 1721 | + return false; | |
| 1722 | + } | |
| 1723 | + | |
| 1724 | + return wpbc_calendar__evaluate_date_constraints( resource_id, sql_date, is_natively_selectable, [] ).is_selectable; | |
| 1725 | + } | |
| 1225 | 1726 | |
| 1226 | - // Get Data -------------------------------------------------------------------------------------------------------- | |
| 1227 | - var date_bookings_obj = _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_class_day ); | |
| 1228 | - | |
| 1229 | - var is_day_selectable = ( parseInt( date_bookings_obj[ 'day_availability' ] ) > 0 ); | |
| 1230 | - | |
| 1231 | - if ( typeof (date_bookings_obj[ 'summary' ]) === 'undefined' ){ | |
| 1232 | - return is_day_selectable; | |
| 1233 | - } | |
| 1234 | - | |
| 1235 | - if ( 'available' != date_bookings_obj[ 'summary']['status_for_day' ] ){ | |
| 1236 | - | |
| 1237 | - var is_set_pending_days_selectable = _wpbc.calendar__get_param_value( resource_id, 'pending_days_selectable' ); // set pending days selectable // FixIn: 8.6.1.18. | |
| 1238 | - | |
| 1239 | - switch ( date_bookings_obj[ 'summary']['status_for_bookings' ] ){ | |
| 1240 | - case 'pending': | |
| 1241 | - // Situations for "change-over" days: | |
| 1242 | - case 'pending_pending': | |
| 1243 | - case 'pending_approved': | |
| 1244 | - case 'approved_pending': | |
| 1245 | - is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable; | |
| 1246 | - break; | |
| 1247 | - default: | |
| 1248 | - } | |
| 1249 | - } | |
| 1250 | - | |
| 1251 | - return is_day_selectable; | |
| 1252 | - } | |
| 1253 | - | |
| 1254 | 1727 | /** |
| 1255 | 1728 | * Is date to check IN array of selected dates |
| 1256 | 1729 | * |
| 1257 | 1730 | * @param {date}js_date_to_check - JS Date - simple JavaScript Date object |
| @@ -1909,9 +2382,9 @@ | ||
| 1909 | 2382 | // Scroll to specific month, if we set dates in some future months |
| 1910 | 2383 | wpbc_calendar__scroll_to( resource_id, inst.dates[ 0 ].getFullYear(), inst.dates[ 0 ].getMonth()+1 ); |
| 1911 | 2384 | } |
| 1912 | 2385 | |
| 1913 | - return inst.dates.length; | |
| 2386 | + return wpbc_get__selected_dates_sql__as_arr( resource_id ).length; | |
| 1914 | 2387 | } |
| 1915 | 2388 | |
| 1916 | 2389 | return 0; |
| 1917 | 2390 | } |
| @@ -2034,9 +2507,12 @@ | ||
| 2034 | 2507 | } ); |
| 2035 | 2508 | } |
| 2036 | 2509 | } |
| 2037 | 2510 | |
| 2038 | - if ( url_params.has( 'wpbc_auto_fill' ) ){ | |
| 2511 | + if ( | |
| 2512 | + url_params.has( 'wpbc_auto_fill' ) | |
| 2513 | + && ! jQuery( 'body' ).hasClass( 'wp-admin' ) | |
| 2514 | + ){ | |
| 2039 | 2515 | |
| 2040 | 2516 | var wpbc_auto_fill_value = url_params.get( 'wpbc_auto_fill' ); |
| 2041 | 2517 | |
| 2042 | 2518 | // Convert back. Some systems do not like symbol '~' in URL, so we need to replace to some other symbols |
| @@ -2049,9 +2525,13 @@ | ||
| 2049 | 2525 | |
| 2050 | 2526 | /** |
| 2051 | 2527 | * Autofill / select booking form fields by values from the GET request parameter: ?wpbc_auto_fill= |
| 2052 | 2528 | * |
| 2053 | - * @param auto_fill_str | |
| 2529 | + * URL-provided field names are resolved through the exact-name DOM API so | |
| 2530 | + * they can never be interpreted as CSS selector syntax. | |
| 2531 | + * | |
| 2532 | + * @param {string} auto_fill_str Serialized field-name and value pairs. | |
| 2533 | + * @return {void} | |
| 2054 | 2534 | */ |
| 2055 | 2535 | function wpbc_auto_fill_booking_fields( auto_fill_str ){ // FixIn: 10.0.0.48. |
| 2056 | 2536 | |
| 2057 | 2537 | if ( '' == auto_fill_str ){ |
| @@ -2059,15 +2539,23 @@ | ||
| 2059 | 2539 | } |
| 2060 | 2540 | |
| 2061 | 2541 | // console.log( 'WPBC_AUTO_FILL_BOOKING_FIELDS( AUTO_FILL_STR )', auto_fill_str); |
| 2062 | 2542 | |
| 2063 | - var fields_arr = wpbc_auto_fill_booking_fields__parse( auto_fill_str ); | |
| 2543 | + var fields_arr = wpbc_auto_fill_booking_fields__parse( auto_fill_str ); | |
| 2544 | + | |
| 2545 | + for ( let i = 0; i < fields_arr.length; i++ ){ | |
| 2546 | + var field_name = String( fields_arr[ i ][ 'name' ] || '' ); | |
| 2547 | + if ( '' === field_name ) { | |
| 2548 | + continue; | |
| 2549 | + } | |
| 2550 | + | |
| 2551 | + var field_elements = document.getElementsByName( field_name ); | |
| 2552 | + if ( 0 < field_elements.length ) { | |
| 2553 | + jQuery( field_elements ).val( fields_arr[ i ][ 'value' ] ); | |
| 2554 | + } | |
| 2555 | + } | |
| 2556 | +} | |
| 2064 | 2557 | |
| 2065 | - for ( let i = 0; i < fields_arr.length; i++ ){ | |
| 2066 | - jQuery( '[name="' + fields_arr[ i ][ 'name' ] + '"]' ).val( fields_arr[ i ][ 'value' ] ); | |
| 2067 | - } | |
| 2068 | -} | |
| 2069 | - | |
| 2070 | 2558 | /** |
| 2071 | 2559 | * Parse data from get parameter: ?wpbc_auto_fill=visitors231^2~max_capacity231^2 |
| 2072 | 2560 | * |
| 2073 | 2561 | * @param data_str = 'visitors231^2~max_capacity231^2'; |
| @@ -2242,5 +2730,5 @@ | ||
| 2242 | 2730 | } |
| 2243 | 2731 | } |
| 2244 | 2732 | |
| 2245 | 2733 | return false; // Fallback, if we not parsed this parameter 'calendar_dates_start' = '2025-07-26', for example because of 'calendar_dates_start' = 'sfsdf'. |
| 2246 | -} | |
| 2734 | +} | |