PluginProbe
Booking Calendar / 11.9
Booking Calendar v11.9
11.9 11.8.4 11.8.3 11.8.2 11.8.1 11.8 11.7 11.6.1 11.6 11.5 11.4.3 11.4.2 11.4.1 11.4 11.3 11.2.1 11.2 11.1 11.0 10.15.7 10.15.6 10.1.3 10.10 10.10.1 10.10.2 All 205 releases
← All changes | includes/__js/client/cal/wpbc_cal.js +471 -60 11.2.1 → 11.9 View file →
@@ -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;
@@ -144,10 +146,14 @@
144 146 // -----------------------------------------------------------------------------------------------------------------
145 147 // Show calendar
146 148 // -----------------------------------------------------------------------------------------------------------------
147 149 jQuery('#calendar_booking'+ resource_id).datepick(
148 - {
149 - 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 ){
150 156 return wpbc__calendar__apply_css_to_days( js_date, {'resource_id': resource_id}, this );
151 157 },
152 158 onSelect: function ( string_dates, js_dates_arr ){ /**
153 159 * string_dates = '23.08.2023 - 26.08.2023'
@@ -265,13 +271,96 @@
265 271 }
266 272 }
267 273
268 274 return false;
269 - }
270 -
271 -
272 - /**
273 - * Apply CSS to calendar date cells
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
274 363 *
275 364 * @param date - JavaScript Date Obj: Mon Dec 11 2023 00:00:00
276 365 * GMT+0200 (Eastern European Standard Time)
277 366 * @param calendar_params_arr - Calendar Settings Object: {
@@ -316,16 +405,18 @@
316 405 }
317 406 }
318 407
319 408
320 - var is_day_selectable = false;
409 + var is_day_selectable = false;
410 + var date_constraint_result;
321 411
322 412 // If something not defined, then this date closed --------------------------------------------------------------- // FixIn: 10.12.4.6.
323 - if ( (false === date_bookings_obj) || ('undefined' === typeof (date_bookings_obj[resource_id])) ) {
324 -
325 - css_classes__for_date.push( 'date_user_unavailable' );
326 -
327 - 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( ' ' ) ];
328 419 }
329 420
330 421
331 422 // -----------------------------------------------------------------------------------------------------------------
@@ -481,9 +572,11 @@
481 572 css_classes__for_date.push( 'check_out_time_date_approved', 'check_in_time_date_approved' );
482 573 }
483 574 }
484 575
485 - 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( ' ' ) ];
486 579 }
487 580
488 581
489 582 /**
@@ -536,8 +629,9 @@
536 629 // U n h o v e r i n g in UNSELECTABLE_CALENDAR ------------------------------------------------------------
537 630 var is_unselectable_calendar = ( jQuery( '#calendar_booking_unselectable' + resource_id ).length > 0); // FixIn: 8.0.1.2.
538 631 var is_booking_form_exist = ( jQuery( '#booking_form_div' + resource_id ).length > 0 );
539 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 );
540 634
541 635 if ( ( is_unselectable_calendar ) && ( ! is_booking_form_exist ) ){
542 636
543 637 /**
@@ -559,12 +653,13 @@
559 653 if (
560 654 ( location.href.indexOf( 'page=wpbc' ) == -1 )
561 655 || ( ( location.href.indexOf( 'page=wpbc' ) > 0 ) && ( location.href.indexOf( 'tab=add-booking' ) > 0 ) )
562 656 || ( is_add_booking_modal_calendar )
657 + || ( is_admin_calendar_preview )
563 658 || ( location.href.indexOf( 'page=wpbc-setup' ) > 0 )
564 659 || ( location.href.indexOf( 'page=wpbc-availability' ) > 0 )
565 660 || ( ( location.href.indexOf( 'page=wpbc-settings' ) > 0 ) &&
566 - ( location.href.indexOf( '&tab=form' ) > 0 )
661 + ( location.href.indexOf( 'tab=builder_booking_form' ) > 0 )
567 662 )
568 663 ){
569 664 // The same as dates selection, but for days hovering
570 665
@@ -569,8 +664,10 @@
569 664 // The same as dates selection, but for days hovering
570 665
571 666 if ( 'function' == typeof( wpbc__calendar__do_days_highlight__bs ) ){
572 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 );
573 670 }
574 671 }
575 672
576 673 }
@@ -576,8 +673,273 @@
576 673 }
577 674
578 675
579 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 + /**
580 942 * Select calendar date cells
581 943 *
582 944 * @param date - JavaScript Date Obj: Mon Dec 11 2023 00:00:00
583 945 * GMT+0200 (Eastern European Standard Time)
@@ -602,10 +964,16 @@
602 964
603 965 jQuery( '#date_booking' + resource_id ).val( date ); // Add selected dates to hidden textarea
604 966
605 967
606 - 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 + }
607 973
974 + wpbc_range_selection__sync_guidance( resource_id );
975 +
608 976 wpbc_disable_time_fields_in_booking_form( resource_id );
609 977
610 978 // Hook -- trigger day selection -----------------------------------------------------------------------------------
611 979 var mouse_clicked_dates = date; // Can be: "05.10.2023 - 07.10.2023" | "10.10.2023 - 10.10.2023" |
@@ -1222,8 +1590,10 @@
1222 1590 if ( 'undefined' === typeof (resource_id) ){
1223 1591 resource_id = '1';
1224 1592 }
1225 1593
1594 + wpbc_range_selection__hide_guidance( resource_id );
1595 +
1226 1596 var inst = wpbc_calendar__get_inst( resource_id )
1227 1597
1228 1598 if ( null !== inst ){
1229 1599
@@ -1291,44 +1661,70 @@
1291 1661 }
1292 1662 return false;
1293 1663 }
1294 1664
1295 - /**
1296 - * Is this date selectable in calendar (mainly it's means AVAILABLE date)
1297 - *
1298 - * @param {int|string} resource_id 1
1299 - * @param {string} sql_class_day '2023-08-11'
1300 - * @returns {boolean} true | false
1301 - */
1302 - 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 + }
1303 1726
1304 - // Get Data --------------------------------------------------------------------------------------------------------
1305 - var date_bookings_obj = _wpbc.bookings_in_calendar__get_for_date( resource_id, sql_class_day );
1306 -
1307 - var is_day_selectable = ( parseInt( date_bookings_obj[ 'day_availability' ] ) > 0 );
1308 -
1309 - if ( typeof (date_bookings_obj[ 'summary' ]) === 'undefined' ){
1310 - return is_day_selectable;
1311 - }
1312 -
1313 - if ( 'available' != date_bookings_obj[ 'summary']['status_for_day' ] ){
1314 -
1315 - 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.
1316 - var booking_statuses_arr = wpbc_get_booking_statuses__as_arr( date_bookings_obj );
1317 -
1318 - if (
1319 - ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending' ) )
1320 - || ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_pending' ) )
1321 - || ( wpbc_booking_statuses__has( booking_statuses_arr, 'pending_approved' ) )
1322 - || ( wpbc_booking_statuses__has( booking_statuses_arr, 'approved_pending' ) )
1323 - ){
1324 - is_day_selectable = (is_day_selectable) ? true : is_set_pending_days_selectable;
1325 - }
1326 - }
1327 -
1328 - return is_day_selectable;
1329 - }
1330 -
1331 1727 /**
1332 1728 * Is date to check IN array of selected dates
1333 1729 *
1334 1730 * @param {date}js_date_to_check - JS Date - simple JavaScript Date object
@@ -1986,9 +2382,9 @@
1986 2382 // Scroll to specific month, if we set dates in some future months
1987 2383 wpbc_calendar__scroll_to( resource_id, inst.dates[ 0 ].getFullYear(), inst.dates[ 0 ].getMonth()+1 );
1988 2384 }
1989 2385
1990 - return inst.dates.length;
2386 + return wpbc_get__selected_dates_sql__as_arr( resource_id ).length;
1991 2387 }
1992 2388
1993 2389 return 0;
1994 2390 }
@@ -2111,9 +2507,12 @@
2111 2507 } );
2112 2508 }
2113 2509 }
2114 2510
2115 - if ( url_params.has( 'wpbc_auto_fill' ) ){
2511 + if (
2512 + url_params.has( 'wpbc_auto_fill' )
2513 + && ! jQuery( 'body' ).hasClass( 'wp-admin' )
2514 + ){
2116 2515
2117 2516 var wpbc_auto_fill_value = url_params.get( 'wpbc_auto_fill' );
2118 2517
2119 2518 // Convert back. Some systems do not like symbol '~' in URL, so we need to replace to some other symbols
@@ -2126,9 +2525,13 @@
2126 2525
2127 2526 /**
2128 2527 * Autofill / select booking form fields by values from the GET request parameter: ?wpbc_auto_fill=
2129 2528 *
2130 - * @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}
2131 2534 */
2132 2535 function wpbc_auto_fill_booking_fields( auto_fill_str ){ // FixIn: 10.0.0.48.
2133 2536
2134 2537 if ( '' == auto_fill_str ){
@@ -2136,14 +2539,22 @@
2136 2539 }
2137 2540
2138 2541 // console.log( 'WPBC_AUTO_FILL_BOOKING_FIELDS( AUTO_FILL_STR )', auto_fill_str);
2139 2542
2140 - var fields_arr = wpbc_auto_fill_booking_fields__parse( auto_fill_str );
2141 -
2142 - for ( let i = 0; i < fields_arr.length; i++ ){
2143 - jQuery( '[name="' + fields_arr[ i ][ 'name' ] + '"]' ).val( fields_arr[ i ][ 'value' ] );
2144 - }
2145 -}
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 +}
2146 2557
2147 2558 /**
2148 2559 * Parse data from get parameter: ?wpbc_auto_fill=visitors231^2~max_capacity231^2
2149 2560 *