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 | js/wpbc_time-selector.js +187 -52 11.8.1 → 11.9 View file →
@@ -1,6 +1,63 @@
1 1 // FixIn: 8.7.11.10.
2 -(function ( $ ){
2 +(function ( $ ){
3 +
4 + /**
5 + * Normalize native time options for the visual time-picker presentation.
6 + *
7 + * Empty values are select-box placeholders rather than bookable choices, so
8 + * they must not become visual picker cards. If the native select currently
9 + * points to a placeholder or a disabled option, select the first enabled real
10 + * time to match the browser behavior of a picker-mode form exported without a
11 + * placeholder.
12 + *
13 + * @param {Array<Object>} option_records Native option records.
14 + * @return {Array<Object>} Normalized non-placeholder option records with at
15 + * most one enabled selection.
16 + */
17 + function wpbc_timeselector_normalize_options( option_records ) {
18 +
19 + var normalized_options = [];
20 + var selected_option_index = -1;
21 +
22 + $.each( Array.isArray( option_records ) ? option_records : [], function ( index, option_record ) {
23 + var option_value = ( 'undefined' === typeof option_record.value || null === option_record.value ) ? '' : option_record.value;
24 +
25 + if ( '' === String( option_value ).trim() ) {
26 + return;
27 + }
28 +
29 + normalized_options.push( {
30 + title : ( 'undefined' === typeof option_record.title || null === option_record.title ) ? '' : option_record.title,
31 + value : option_value,
32 + disabled: !! option_record.disabled,
33 + selected: !! option_record.selected
34 + } );
35 +
36 + if (
37 + -1 === selected_option_index
38 + && ! option_record.disabled
39 + && option_record.selected
40 + ) {
41 + selected_option_index = normalized_options.length - 1;
42 + }
43 + } );
44 +
45 + if ( -1 === selected_option_index ) {
46 + $.each( normalized_options, function ( index, option_record ) {
47 + if ( ! option_record.disabled ) {
48 + selected_option_index = index;
49 + return false;
50 + }
51 + } );
52 + }
53 +
54 + $.each( normalized_options, function ( index, option_record ) {
55 + option_record.selected = index === selected_option_index;
56 + } );
57 +
58 + return normalized_options;
59 + }
3 60
4 61 $.fn.extend( {
5 62
6 63 wpbc_timeselector: function (){
@@ -6,12 +63,21 @@
6 63 wpbc_timeselector: function (){
7 64
8 65 var times_options = [];
9 66
10 - this.each( function (){
67 + this.each( function (){
68 +
69 + var el = $( this );
70 + var native_selected_option = el.find( 'option:selected' ).first();
71 + var native_selected_value = native_selected_option.length ? String( native_selected_option.val() ) : '';
72 + var automatic_selection_value = el.attr( 'data-wpbc-time-picker-auto-value' );
73 + var has_explicit_native_selection = (
74 + native_selected_option.length
75 + && ! native_selected_option.is( ':disabled' )
76 + && '' !== native_selected_value.trim()
77 + && automatic_selection_value !== native_selected_value
78 + );
11 79
12 - var el = $( this );
13 -
14 80 // On new days click we are searching for old time items, and remove them from this booking form
15 81 if ( el.parent().find( '.wpbc_times_selector' ).length ) {
16 82 el.parent().find( '.wpbc_times_selector' ).remove();
17 83 }
@@ -26,12 +92,28 @@
26 92 } );
27 93
28 94 } );
29 95
30 - var times_options_html = $.fn.wpbc_timeselector.format( times_options );
96 + times_options = wpbc_timeselector_normalize_options( times_options );
97 +
98 + $.each( times_options, function ( index, option_record ) {
99 + if ( option_record.selected ) {
100 + var selected_time_value = String( option_record.value );
101 +
102 + el.val( selected_time_value );
103 + if ( has_explicit_native_selection ) {
104 + el.removeAttr( 'data-wpbc-time-picker-auto-value' );
105 + } else {
106 + el.attr( 'data-wpbc-time-picker-auto-value', selected_time_value );
107 + }
108 + return false;
109 + }
110 + } );
111 +
112 + var times_options_element = $.fn.wpbc_timeselector.format( times_options );
113 +
114 + el.after( times_options_element );
31 115
32 - el.after( times_options_html );
33 -
34 116 el.next('.wpbc_times_selector').find('div').not('.wpbc_time_picker_disabled').on( "click", function() {
35 117
36 118 // Get data value of clicked DIV time-slot
37 119 var selected_value = jQuery( this ).attr( 'data-value' );
@@ -41,12 +123,15 @@
41 123 // Set time item with selected Class
42 124 jQuery( this ).addClass('wpbc_time_selected');
43 125
44 126 el.find( 'option' ).prop( 'selected', false );
45 - // Find option in selectbox with this value
46 - el.find( 'option[value="' + selected_value + '"]' ).prop( 'selected', true );
47 -
48 - el.trigger( 'change' );
127 + // Match the literal value without interpreting it as selector syntax.
128 + el.find( 'option' ).filter( function (){
129 + return selected_value === jQuery( this ).val();
130 + } ).prop( 'selected', true );
131 + el.removeAttr( 'data-wpbc-time-picker-auto-value' );
132 +
133 + el.trigger( 'change' );
49 134 });
50 135
51 136 // Execute a function when the user presses a key (13 - Enter) on the keyboard. (FixIn: EAA)
52 137 el.next( '.wpbc_times_selector' ).find( 'div' ).not( '.wpbc_time_picker_disabled' ).on( "keypress", function (event) {
@@ -63,51 +148,101 @@
63 148 } );
64 149
65 150 return this; // Chain
66 151 }
67 - } );
152 + } );
153 +
154 + /**
155 + * Build the visual time-slot selector from native option data.
156 + *
157 + * Values and labels can originate in saved form configuration or modified
158 + * DOM state. Creating elements and assigning text/attributes separately
159 + * prevents either value from becoming executable markup.
160 + *
161 + * @param {Array<Object>} el_arr Time-slot option records.
162 + * @return {jQuery} Detached, safely populated time-slot selector.
163 + */
164 + $.fn.wpbc_timeselector.format = function ( el_arr ) {
165 +
166 + var times_selector = jQuery( document.createElement( 'div' ) ).addClass( 'wpbc_times_selector' );
167 + var has_available_times = false;
168 + var normalized_options = wpbc_timeselector_normalize_options( el_arr );
169 +
170 + $.each( normalized_options, function (index, el_item){
171 +
172 + if ( !el_item.disabled ){
173 + var time_option_value = ( 'undefined' === typeof el_item.value || null === el_item.value ) ? '' : el_item.value;
174 + var time_option_title = ( 'undefined' === typeof el_item.title || null === el_item.title ) ? '' : el_item.title;
175 + var time_option = jQuery( document.createElement( 'div' ) )
176 + .attr( 'data-value', String( time_option_value ) )
177 + .attr( 'tabindex', '0' )
178 + .text( String( time_option_title ) );
179 +
180 + if ( el_item.selected ){
181 + time_option.addClass( 'wpbc_time_selected' );
182 + }
183 +
184 + times_selector.append( time_option );
185 + has_available_times = true;
186 + } else {
187 + // Uncomment row bellow to Show booked time slots as unavailable RED slots // FixIn: 9.9.0.2.
188 + // Add a disabled element through the same DOM construction path when this feature is enabled.
189 + }
190 +
191 + } );
192 +
193 + if ( ! has_available_times ){
194 + times_selector.append(
195 + jQuery( document.createElement( 'span' ) )
196 + .addClass( 'wpbc_no_time_pickers' )
197 + .text( 'No available times' )
198 + );
199 + }
200 +
201 + return times_selector;
202 + };
68 203
69 204
70 - // Get HTML structure of times selection
71 - $.fn.wpbc_timeselector.format = function ( el_arr ) {
72 -
73 - var select_div = '';
74 - var css_class='';
75 -
76 - $.each( el_arr, function (index, el_item){
77 -
78 - if ( !el_item.disabled ){
79 -
80 - if (el_item.selected){
81 - css_class = 'wpbc_time_selected';
82 - } else {
83 - css_class = '';
84 - }
85 -
86 - select_div += '<div '
87 - + ' data-value="' + el_item.value + '" '
88 - + ' class="' + css_class + '" '
89 - + ' tabindex="0" '
90 - + '>'
91 - + el_item.title
92 - + '</div>'
93 - } else {
94 - // Uncomment row bellow to Show booked time slots as unavailable RED slots // FixIn: 9.9.0.2.
95 - // select_div += '<div class="wpbc_time_picker_disabled">' + el_item.title + '</div>';
96 - }
97 -
98 - } );
99 -
100 - if ( '' == select_div ){
101 - select_div = '<span class="wpbc_no_time_pickers">'
102 - + 'No available times'
103 - + '</span>'
104 - }
105 - return '<div class="wpbc_times_selector">' + select_div + '</div>';
106 - }
107 -
108 -
109 -})( jQuery );
205 +})( jQuery );
206 +
207 +
208 +/**
209 + * Remove visual time pickers and restore native select-box placeholders.
210 + *
211 + * Only values automatically chosen by the visual picker are restored. An
212 + * explicit customer choice remains selected when a live settings preview is
213 + * switched back to Select box mode.
214 + *
215 + * @param {HTMLElement|Document|jQuery|null} context Optional preview boundary.
216 + * @return {void}
217 + */
218 +function wpbc_hook__destroy_timeselector( context ){
219 +
220 + var selector = 'select[name^="rangetime"], select[name^="starttime"], select[name^="endtime"], select[name^="durationtime"]';
221 + var scope = context ? jQuery( context ) : jQuery( document );
222 +
223 + scope.find( '.wpbc_times_selector' ).remove();
224 + scope.find( selector ).each( function (){
225 + var select_element = jQuery( this );
226 + var current_value = select_element.val();
227 + var automatic_value = select_element.attr( 'data-wpbc-time-picker-auto-value' );
228 +
229 + current_value = ( 'undefined' === typeof current_value || null === current_value ) ? '' : String( current_value );
230 + if ( 'undefined' !== typeof automatic_value && automatic_value === current_value ) {
231 + var placeholder_option = select_element.find( 'option' ).filter( function (){
232 + var option_value = jQuery( this ).val();
233 +
234 + return 'undefined' === typeof option_value || null === option_value || '' === String( option_value ).trim();
235 + } ).first();
236 +
237 + if ( placeholder_option.length ) {
238 + select_element.val( placeholder_option.val() );
239 + }
240 + }
241 +
242 + select_element.removeAttr( 'data-wpbc-time-picker-auto-value' ).show();
243 + } );
244 +}
110 245
111 246
112 247 /**
113 248 * Initialize the visual time-slot selectors when the WPBC runtime is ready.