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 +241 -69 10.15.6 → 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,62 +148,125 @@
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 ) {
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 +}
72 245
73 - var select_div = '';
74 - var css_class='';
75 246
76 - $.each( el_arr, function (index, el_item){
247 +/**
248 + * Initialize the visual time-slot selectors when the WPBC runtime is ready.
249 + *
250 + * The readiness check prevents a JavaScript error when a third-party optimizer
251 + * delays wpbc_all.js, which creates window._wpbc, beyond document ready.
252 + *
253 + * @return {boolean} True when initialization is complete or not required;
254 + * otherwise false when the WPBC runtime is not ready yet.
255 + */
256 +function wpbc_hook__init_timeselector(){
257 +
258 + if (
259 + ( 'object' !== typeof window._wpbc )
260 + || ( 'function' !== typeof window._wpbc.get_other_param )
261 + ) {
262 + return false;
263 + }
264 +
265 + if ( true !== window._wpbc.get_other_param( 'is_enabled_booking_timeslot_picker' ) ) {
266 + return true;
267 + }
77 268
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 );
110 -
111 -
112 -/**
113 - * Init time selector
114 - */
115 -function wpbc_hook__init_timeselector(){
116 -
117 - if ( true !== _wpbc.get_other_param( 'is_enabled_booking_timeslot_picker' ) ) {
118 - return false;
119 - }
120 -
121 269 // Load after page loaded
122 270 jQuery( 'select[name^="rangetime"]' ).wpbc_timeselector();
123 271 jQuery( 'select[name^="starttime"]' ).wpbc_timeselector();
124 272 jQuery( 'select[name^="endtime"]' ).wpbc_timeselector();
@@ -128,13 +276,37 @@
128 276 jQuery( ".booking_form_div" ).on( 'wpbc_hook_timeslots_disabled', function ( event, bk_type, all_dates ){
129 277 jQuery( '#booking_form_div' + bk_type + ' select[name^="rangetime"]' ).wpbc_timeselector();
130 278 jQuery( '#booking_form_div' + bk_type + ' select[name^="starttime"]' ).wpbc_timeselector();
131 279 jQuery( '#booking_form_div' + bk_type + ' select[name^="endtime"]' ).wpbc_timeselector();
132 - jQuery( '#booking_form_div' + bk_type + ' select[name^="durationtime"]' ).wpbc_timeselector();
133 - } );
134 -}
135 -
136 -jQuery(document).ready(function(){
137 -// setTimeout( function ( ) { // Need to have some delay for loading of all times in Garbage
138 - wpbc_hook__init_timeselector();
139 -// }, 1000 );
140 -});
280 + jQuery( '#booking_form_div' + bk_type + ' select[name^="durationtime"]' ).wpbc_timeselector();
281 + } );
282 +
283 + return true;
284 +}
285 +
286 +
287 +/**
288 + * Initialize the time selector now or after the WPBC core signals readiness.
289 + *
290 + * @return {void}
291 + */
292 +function wpbc_init_timeselector_when_wpbc_ready(){
293 +
294 + if ( wpbc_hook__init_timeselector() ) {
295 + return;
296 + }
297 +
298 + var wpbc_timeselector_ready_handler = function (){
299 + if ( wpbc_hook__init_timeselector() ) {
300 + document.removeEventListener( 'wpbc-ready', wpbc_timeselector_ready_handler );
301 + }
302 + };
303 +
304 + document.addEventListener( 'wpbc-ready', wpbc_timeselector_ready_handler );
305 +}
306 +
307 +
308 +jQuery(document).ready(function(){
309 +// setTimeout( function ( ) { // Need to have some delay for loading of all times in Garbage
310 + wpbc_init_timeselector_when_wpbc_ready();
311 +// }, 1000 );
312 +});