PageSourceSearch

https://hoteldeville-suisse.ch/wp-content/plugins/restaurant-reser…/js/booking-form.js?ver=2.8.0.1791198689

js hoteldeville-suisse.ch collected 2026-10-10 02:26:24 UTC 39,815 bytes, 1,167 lines download raw bytes

1/* Javascript for Restaurant Reservations booking form */
2
3var rtb_booking_form = rtb_booking_form || {};
4
5jQuery(document).ready(function ($) {
6	rtb_booking_form.time_request = null;
7	rtb_booking_form.time_request_token = 0;
8	rtb_booking_form.table_request = null;
9	rtb_booking_form.table_request_token = 0;
10
11	rtb_booking_form.invalidate_time_request = function() {
12		rtb_booking_form.time_request_token++;
13		if ( rtb_booking_form.time_request ) { rtb_booking_form.time_request.abort(); }
14		rtb_booking_form.time_request = null;
15	}
16
17	rtb_booking_form.invalidate_table_request = function() {
18		rtb_booking_form.table_request_token++;
19		if ( rtb_booking_form.table_request ) { rtb_booking_form.table_request.abort(); }
20		rtb_booking_form.table_request = null;
21	}
22
23	rtb_booking_form.clear_availability_errors = function( fields, include_submission ) {
24		jQuery.each( fields, function( index, field ) {
25			clearPrevFieldError( field, include_submission );
26		} );
27	}
28
29	/**
30	 * Initialize the booking form when loaded
31	 */
32	rtb_booking_form.init = function() {
33
34		// Scroll to the first error message on the booking form
35		if ( $( '.rtb-booking-form .rtb-error' ).length ) {
36			$('html, body').animate({
37				scrollTop: $( '.rtb-booking-form .rtb-error' ).first().offset().top + -40
38			}, 500);
39		}
40
41		// Show the message field on the booking form
42		$( '.rtb-booking-form .add-message a' ).click( function() {
43			$(this).hide();
44			$(this).parent().siblings( '.message' ).addClass( 'message-open' )
45				.find( 'label' ).focus();
46
47			return false;
48		});
49
50		// Show the message field on load if not empty
51		if ( $.trim( $( '.rtb-booking-form .message textarea' ).val() ) ) {
52			$( '.rtb-booking-form .add-message a' ).trigger( 'click' );
53		}
54
55		// Disable the submit button when the booking form is submitted
56		$( '.rtb-booking-form form' ).submit( function() {
57			$(this).find( 'button[type="submit"]' ).prop( 'disabled', 'disabled' );
58			return true;
59		} );
60
61		// Remove required attribute from all checkboxes in a group if one is checked
62		$( '.rtb-booking-form form button[type="submit"]' ).on( 'click', function() {
63
64			$( '.rtb-booking-form form input[required]:checkbox' ).each( function() {
65				 var checkbox_name = $( this ).attr('name');
66
67				 var checkbox_group = $( '.rtb-booking-form form input[name="' + checkbox_name + '"]' );
68
69				 if ( checkbox_group.is( ':checked' ) ) {
70
71				 	checkbox_group.prop( 'required', false );
72				 }
73			});
74		});
75
76		// Enable datepickers on load
77		if ( typeof rtb_pickadate !== 'undefined' ) {
78
79			rtb_pickadate.init_complete = false;
80
81			// A failed submission restores the location select without firing change.
82			// Apply its rules before either picker reads the initial settings.
83			if ( rtb_pickadate.multiple_locations_enabled && $( '#rtb-location' ).length ) {
84				rtb_booking_form.update_base_data_for_selected_location();
85				rtb_booking_form.update_party_options_for_location( true );
86			}
87
88			// Declare datepicker
89			var $date_input = $( '#rtb-date' );
90			if ( $date_input.length ) {
91				var date_input = $date_input.pickadate({
92					format: rtb_pickadate.date_format,
93					formatSubmit: 'yyyy/mm/dd',
94					hiddenName: true,
95					min: !rtb_pickadate.allow_past,
96					container: 'body',
97					firstDay: rtb_pickadate.first_day,
98					today: rtb_pickadate.date_today_label,
99					clear: rtb_pickadate.date_clear_label,
100					close: rtb_pickadate.date_close_label,
101
102					onStart: function() {
103
104						// Block dates beyond early bookings window
105						if ( rtb_pickadate.early_bookings !== '' ) {
106							this.set( 'max', parseInt( rtb_pickadate.early_bookings, 10 ) );
107						}
108
109						// Select the value when loaded if a value has been set
110						if ( $date_input.val()	!== '' ) {
111							var date = new Date( $date_input.val() );
112							if ( Object.prototype.toString.call( date ) === "[object Date]" ) {
113								this.set( 'select', date );
114							}
115						}
116						else if ( jQuery( '.rtb-selected-date' ).length ) {
117							var date = new Date( jQuery( '.rtb-selected-date' ).val() );
118							if ( Object.prototype.toString.call( date ) === "[object Date]" ) {
119								this.set( 'select', date );
120							}
121						}
122					}
123				});
124
125				rtb_booking_form.datepicker = date_input.pickadate( 'picker' );
126			}
127
128			// Declare timepicker
129			var $time_input = $( '#rtb-time' );
130			if ( $time_input.length ) {
131				var time_input = $time_input.pickatime({
132					format: rtb_pickadate.time_format,
133					formatSubmit: 'h:i A',
134					hiddenName: true,
135					interval: parseInt( rtb_pickadate.time_interval, 10 ),
136					container: 'body',
137					clear: rtb_pickadate.time_clear_label,
138
139					// Select the value when loaded if a value has been set
140					onStart: function() {
141						if ( $time_input.val()	!== '' ) {
142							var today = new Date();
143							var today_date = today.getFullYear() + '/' + ( today.getMonth() + 1 ) + '/' + today.getDate();
144							var time = new Date( today_date + ' ' + $time_input.val() );
145							if ( Object.prototype.toString.call( time ) === "[object Date]" ) {
146								this.set( 'select', time );
147							}
148
149						}
150					}
151				});
152
153				rtb_booking_form.timepicker = time_input.pickatime( 'picker' );
154			}
155
156			// We need to check both to support different jQuery versions loaded
157			// by older versions of WordPress. In jQuery v1.10.2, the property
158			// is undefined. But in v1.11.3 it's set to null.
159			if ( rtb_booking_form.datepicker === null || typeof rtb_booking_form.datepicker == 'undefined' ) {
160				return;
161			}
162
163			// Enforce the party -> date -> time dependency.
164			rtb_booking_form.update_datepicker();
165
166			if ( typeof rtb_pickadate.late_bookings === 'string' ) {
167				if ( rtb_pickadate.late_bookings == 'same_day' ) {
168					rtb_booking_form.datepicker.set( 'min', 1 );
169				} else if ( rtb_pickadate.late_bookings !== '' ) {
170					rtb_pickadate.late_bookings = parseInt( rtb_pickadate.late_bookings, 10 );
171					if ( rtb_pickadate.late_bookings % 1 === 0 && rtb_pickadate.late_bookings >= 1440 ) {
172						var min = Math.floor( rtb_pickadate.late_bookings / 1440 );
173						rtb_booking_form.datepicker.set( 'min', min );
174					}
175				}
176			}
177
178			// If no date has been set, select today's date if it's a valid
179			// date. User may opt not to do this in the settings.
180			if ( $date_input.val() === '' && parseInt( $( '#rtb-party' ).val(), 10 ) > 0 && !$( '.rtb-booking-form .date .rtb-error' ).length ) {
181
182				if ( rtb_pickadate.date_onload == 'soonest' ) {
183					rtb_booking_form.datepicker.set( 'select', new Date() );
184				} else if ( rtb_pickadate.date_onload !== 'empty' ) {
185					var dateToVerify = rtb_booking_form.datepicker.component.create( new Date() );
186					var isDisabled = rtb_booking_form.datepicker.component.disabled( dateToVerify );
187					if ( !isDisabled ) {
188						rtb_booking_form.datepicker.set( 'select', dateToVerify );
189					}
190				}
191			}
192
193			if ( rtb_booking_form.timepicker === null || typeof rtb_booking_form.timepicker == 'undefined' ) {
194				return;
195			}
196
197			// Update timepicker on pageload and whenever the datepicker is closed
198			rtb_booking_form.update_timepicker_range();
199			rtb_booking_form.datepicker.on( {
200				open: function () {
201					
202					rtb_booking_form.before_change_value = rtb_booking_form.datepicker.get();
203				},
204
205				close: function() {
206
207					rtb_booking_form.after_change_value = rtb_booking_form.datepicker.get();
208
209					if(rtb_booking_form.before_change_value != rtb_booking_form.after_change_value) {
210						rtb_booking_form.clear_availability_errors( [ 'date', 'time', 'table' ], true );
211						// clear time value if date changed
212						rtb_booking_form.timepicker.clear();
213					}
214
215					rtb_booking_form.update_timepicker_range();
216					rtb_booking_form.update_possible_tables();
217				}
218			});
219
220			rtb_booking_form.timepicker.on( {
221				open: function() { rtb_booking_form.previous_time = rtb_booking_form.timepicker.get(); },
222				close: function() {
223					if ( rtb_booking_form.previous_time !== rtb_booking_form.timepicker.get() ) {
224						rtb_booking_form.clear_availability_errors( [ 'time', 'table' ], true );
225					}
226					rtb_booking_form.clear_availability_errors( [ 'table' ] );
227					rtb_booking_form.update_possible_tables();
228				}
229			});
230
231			$( '#rtb-party' ).on( 'change', function() {
232				rtb_booking_form.clear_availability_errors( [ 'date', 'time', 'table' ], true );
233				rtb_booking_form.timepicker.clear();
234				rtb_booking_form.datepicker.clear();
235				rtb_booking_form.update_datepicker();
236				rtb_booking_form.update_timepicker_range();
237				rtb_booking_form.update_possible_tables();
238			});
239
240			$( '#rtb-location' ).on( 'change', function() {
241
242				if ( ! rtb_pickadate.multiple_locations_enabled ) { return; }
243
244				rtb_booking_form.clear_availability_errors( [ 'date', 'time', 'table' ], true );
245				rtb_booking_form.timepicker.clear();
246				rtb_booking_form.datepicker.clear();
247
248				rtb_booking_form.update_base_data_for_selected_location();
249				rtb_booking_form.update_party_options_for_location();
250
251				rtb_booking_form.update_datepicker();
252
253				rtb_booking_form.update_timepicker_range();
254
255				rtb_booking_form.update_possible_tables();
256			});
257
258			if ( 'rtb-bookings' != ( new URL( window.location.href ) ).searchParams.get( 'page' ) ) { rtb_booking_form.update_possible_tables(); }
259		}
260	};
261
262	/**
263	 * Update base data for date/time picker as per the selected location
264	 * @return object
265	 */
266	rtb_booking_form.update_base_data_for_selected_location = function () {
267		const selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : '';
268		
269		if( '' == selected_location ) {
270			// Set global settings
271			return Object.assign( rtb_pickadate, rtb_location_data.global );
272		}
273
274		return Object.assign( rtb_pickadate, rtb_location_data[selected_location] );
275	}
276
277	rtb_booking_form.update_party_options_for_location = function ( restore_submitted_party ) {
278		var party_select = jQuery( '#rtb-party' ),
279			selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : 'global',
280			location_data = selected_location && rtb_location_data[selected_location] ? rtb_location_data[selected_location] : rtb_location_data.global,
281			current_party = restore_submitted_party && party_select.data( 'selected' ) ? party_select.data( 'selected' ) : party_select.val();
282
283		if ( ! location_data || ! location_data.party_options ) { return; }
284
285		party_select.empty();
286		jQuery.each( location_data.party_options, function( value, label ) {
287			party_select.append( jQuery( '<option></option>' ).attr( 'value', value ).text( label ) );
288		});
289
290		if ( party_select.find( 'option[value="' + current_party + '"]' ).length ) {
291			party_select.val( current_party );
292		}
293	}
294
295	rtb_booking_form.update_datepicker = function () {
296		var date_input = jQuery( '#rtb-date' );
297		
298		// Reset enabled/disabled rules on this datepicker
299		rtb_booking_form.datepicker.set( 'enable', false );
300		rtb_booking_form.datepicker.set( 'disable', false );
301
302		if ( ! parseInt( jQuery( '#rtb-party' ).val(), 10 ) ) {
303			date_input.prop( 'disabled', true ).attr( 'aria-disabled', 'true' );
304			rtb_booking_form.datepicker.clear();
305			rtb_booking_form.datepicker.set( 'disable', true );
306			return;
307		}
308
309		date_input.prop( 'disabled', false ).removeAttr( 'aria-disabled' );
310
311		rtb_booking_form.update_disabled_dates();
312	}
313
314	/**
315	 * Update datepicker to change the disabled dates based on location
316	 */
317	rtb_booking_form.update_disabled_dates = function() {
318
319		if ( rtb_booking_form_js_localize.admin_ignore_schedule && rtb_booking_form_js_localize.is_admin ) {
320			rtb_booking_form.datepicker.set( 'enable', true );
321			return;
322		}
323
324		// Pass conditional configuration parameters
325		if ( rtb_pickadate.disable_dates.length ) {
326
327			var disable_dates = jQuery.extend( true, [], rtb_pickadate.disable_dates );
328
329			// Update weekday dates if start of the week has been modified
330			if ( typeof rtb_booking_form.datepicker.component.settings.firstDay == 'number' ) {
331				var weekday_num = 0;
332				for ( var disable_key in rtb_pickadate.disable_dates ) {
333					if ( typeof rtb_pickadate.disable_dates[disable_key] == 'number' ) {
334						weekday_num = rtb_pickadate.disable_dates[disable_key] - rtb_booking_form.datepicker.component.settings.firstDay;
335						if ( weekday_num < 1 ) {
336							weekday_num = 7;
337						}
338						disable_dates[disable_key] =  weekday_num;
339					}
340				}
341			}
342
343			rtb_booking_form.datepicker.set( 'disable', disable_dates );
344
345		}
346	}
347
348	/**
349	 * Update the timepicker's range based on the currently selected date
350	 */
351	rtb_booking_form.update_timepicker_range = function() {
352		var time_input = jQuery( '#rtb-time' );
353		rtb_booking_form.invalidate_time_request();
354		rtb_booking_form.invalidate_table_request();
355		rtb_booking_form.clear_availability_errors( [ 'time', 'table' ] );
356		time_input.prop( 'disabled', true ).attr( 'aria-disabled', 'true' );
357
358		// Reset enabled/disabled rules on this timepicker
359		rtb_booking_form.timepicker.set( 'enable', false );
360		rtb_booking_form.timepicker.set( 'disable', false );
361
362		if ( rtb_booking_form_js_localize.admin_ignore_schedule && rtb_booking_form_js_localize.is_admin ) {
363			time_input.prop( 'disabled', false ).removeAttr( 'aria-disabled' );
364			rtb_booking_form.timepicker.set( 'enable', true );
365			return;
366		}
367
368		var selected_party = parseInt( jQuery( '#rtb-party' ).val(), 10 );
369		if ( ! selected_party || rtb_booking_form.datepicker.get() === '' ) {
370			rtb_booking_form.timepicker.set( 'disable', true );
371			return;
372		}
373
374		var selected_date = new Date( rtb_booking_form.datepicker.get( 'select', 'yyyy/mm/dd' ) ),
375			selected_date_year = selected_date.getFullYear(),
376			selected_date_month = selected_date.getMonth(),
377			selected_date_date = selected_date.getDate(),
378			current_date = new Date(),
379			selected_location = '';
380
381		selected_date.setHours(0, 0, 0), selected_date.setMilliseconds(100);
382
383		// Declaring the first element true inverts the timepicker settings. All
384		// times subsequently declared are valid. Any time that doesn't fall
385		// within those declarations is invalid.
386		// See: http://amsul.ca/pickadate.js/time/#disable-times-all
387		var valid_times = [ rtb_booking_form.get_outer_time_range() ];
388
389		if ( selected_party ) {
390			selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : '';
391
392			let hidden_location = jQuery('.rtb-booking-form-form input[name="rtb-location"]');
393			if('' == selected_location && hidden_location.length ) {
394				selected_location = hidden_location.val();
395			}
396
397			selected_date_month = ('0' + (selected_date_month + 1)).slice(-2);
398			selected_date_date = ('0' + selected_date_date).slice(-2);
399
400			var params = {};
401
402			params.action = 'rtb_get_available_time_slots';
403			params.nonce  = rtb_booking_form_js_localize.nonce;
404			params.year   = selected_date_year;
405			params.month  = selected_date_month;
406			params.day    = selected_date_date;
407			params.location = selected_location;
408			params.party = selected_party;
409			params.booking_id = jQuery( '.rtb-booking-form form input[name="ID"]' ).length ? jQuery( '.rtb-booking-form form input[name="ID"]' ).val() : 0;
410
411			var data = jQuery.param( params );
412			var request_token = rtb_booking_form.time_request_token;
413			rtb_booking_form.time_request = jQuery.post( ajaxurl, data, function( response ) {
414				if ( request_token !== rtb_booking_form.time_request_token ) { return; }
415
416				if( rtb_pickadate.init_complete ) {
417					clearPrevFieldError( 'date' );
418					clearPrevFieldError( 'time' );
419				}
420
421				if ( ! response ) {
422					clearPrevFieldError( 'date' );
423					displayFieldError( 'date', rtb_booking_form_js_localize.error['smthng-wrng-cntct-us'] );
424					rtb_booking_form.timepicker.set( 'disable', true );
425
426					return;
427				}
428
429				var additional_valid_times = jQuery.parseJSON( response );
430
431				// If today is all day open, only add one valid date/time rule
432				let outer_range = valid_times[0];
433				if(
434					additional_valid_times.length == 1
435					&& additional_valid_times[0].from[0] == outer_range.from[0]
436					&& additional_valid_times[0].from[1] == outer_range.from[1]
437					&& additional_valid_times[0].to[0] == outer_range.to[0]
438					&& additional_valid_times[0].to[1] == outer_range.to[1] ) {
439					var all_valid_times = [ additional_valid_times[0] ];
440				}
441				else {
442					var all_valid_times = valid_times.concat( additional_valid_times );
443				}
444
445				if( !Array.isArray( additional_valid_times ) || 1 > additional_valid_times.length ) {
446					clearPrevFieldError( 'time' );
447					displayFieldError( 'time', rtb_booking_form_js_localize.error['no-slots-available'] );
448				}
449				else {
450					time_input.prop( 'disabled', false ).removeAttr( 'aria-disabled' );
451				}
452
453				jQuery( all_valid_times ).each( function( index, valid_time ) {
454					valid_time.to = rtb_booking_form.get_latest_viable_time( parseInt( valid_time.to[0] ), parseInt( valid_time.to[1] ) );
455				} );
456
457				rtb_booking_form.timepicker.set( 'disable', all_valid_times );
458				rtb_pickadate.init_complete = true;
459			})
460			.fail(function( args ) {
461				if ( 'abort' === args.statusText || request_token !== rtb_booking_form.time_request_token ) { return; }
462				clearPrevFieldError( 'date' );
463				displayFieldError( 'date', rtb_booking_form_js_localize.error['smthng-wrng-cntct-us'] );
464				rtb_booking_form.timepicker.set( 'disable', true );
465
466				return;
467			})
468			.always(function() {
469				if ( request_token === rtb_booking_form.time_request_token ) {
470					rtb_booking_form.time_request = null;
471				}
472			});
473		}
474
475		else {
476			// Check if this date is an exception to the rules
477			if ( typeof rtb_pickadate.schedule_closed !== 'undefined' ) {
478
479				var excp_date = [];
480				var excp_start_date = [];
481				var excp_start_time = [];
482				var excp_end_date = [];
483				var excp_end_time = [];
484				for ( var closed_key in rtb_pickadate.schedule_closed ) {
485
486					let rule = rtb_pickadate.schedule_closed[closed_key];
487					if( rule.hasOwnProperty('date_range') ) {
488						let start = '' != rule.date_range.start ? new Date( rule.date_range.start ) : new Date();
489						start.setHours(0, 0, 0), start.setMilliseconds(0);
490						start = start.getTime();
491
492						let end = '' != rule.date_range.end ? new Date( rule.date_range.end ) : 9999999999999;
493						'number' != typeof end && end.setHours(23, 59, 58) && end.setMilliseconds(0);
494						end = 'number' != typeof end ? end.getTime() : end;
495
496						if( start < selected_date.getTime() && selected_date.getTime() < end ) {
497							excp_date = selected_date;
498						}
499						else {
500							// Set anything to void this rule
501							// Dates assign with copy, thus creating a new one
502							excp_date = new Date( selected_date.getTime() );
503							excp_date.setDate( selected_date_year + 1 );
504						}
505					}
506					else {
507						excp_date = new Date( rule.date );
508					}
509
510					if ( excp_date.getFullYear() == selected_date_year &&
511							excp_date.getMonth() == selected_date_month &&
512							excp_date.getDate() == selected_date_date
513							) {
514
515						// Closed all day
516						if ( typeof rtb_pickadate.schedule_closed[closed_key].time == 'undefined' ) {
517							rtb_booking_form.timepicker.set( 'disable', [ true ] );
518
519							return;
520						}
521
522						if ( typeof rtb_pickadate.schedule_closed[closed_key].time.start !== 'undefined' ) {
523							excp_start_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_closed[closed_key].time.start );
524							excp_start_time = [ excp_start_date.getHours(), excp_start_date.getMinutes() ];
525						} else {
526							excp_start_time = [ 0, 0 ]; // Start of the day
527						}
528
529						if ( typeof rtb_pickadate.schedule_closed[closed_key].time.end !== 'undefined' ) {
530							excp_end_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_closed[closed_key].time.end );
531							excp_end_time = [ excp_end_date.getHours(), excp_end_date.getMinutes() ];
532						} else {
533							excp_end_time = [ 24, 0 ]; // End of the day
534						}
535
536						excp_start_time = rtb_booking_form.get_earliest_time( excp_start_time, selected_date, current_date );
537
538						valid_times.push( { from: excp_start_time, to: excp_end_time, inverted: true } );
539					}
540				}
541
542				excp_date = excp_start_date = excp_start_time = excp_end_date = excp_end_time = null;
543
544				// Exit early if this date is an exception
545				if ( valid_times.length > 1 ) {
546					rtb_booking_form.timepicker.set( 'disable', valid_times );
547
548					return;
549				}
550			}
551
552			// Get any rules which apply to this weekday
553			if ( typeof rtb_pickadate.schedule_open != 'undefined' ) {
554	
555				var selected_date_weekday = selected_date.getDay();
556	
557				var weekdays = {
558					sunday: 0,
559					monday: 1,
560					tuesday: 2,
561					wednesday: 3,
562					thursday: 4,
563					friday: 5,
564					saturday: 6,
565				};
566	
567				var rule_start_date = [];
568				var rule_start_time = [];
569				var rule_end_date = [];
570				var rule_end_time = [];
571				for ( var open_key in rtb_pickadate.schedule_open ) {
572	
573					if ( typeof rtb_pickadate.schedule_open[open_key].weekdays !== 'undefined' ) {
574						for ( var weekdays_key in rtb_pickadate.schedule_open[open_key].weekdays ) {
575							if ( weekdays[weekdays_key] == selected_date_weekday ) {
576	
577								// Closed all day
578								if ( typeof rtb_pickadate.schedule_open[open_key].time == 'undefined' ) {
579									rtb_booking_form.timepicker.set( 'disable', [ true ] );
580	
581									return;
582								}
583	
584								if ( typeof rtb_pickadate.schedule_open[open_key].time.start !== 'undefined' ) {
585									rule_start_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_open[open_key].time.start );
586									rule_start_time = [ rule_start_date.getHours(), rule_start_date.getMinutes() ];
587								} else {
588									rule_start_time = [ 0, 0 ]; // Start of the day
589								}
590	
591								if ( typeof rtb_pickadate.schedule_open[open_key].time.end !== 'undefined' ) {
592									rule_end_date = new Date( '1 January 2000 ' + rtb_pickadate.schedule_open[open_key].time.end );
593									rule_end_time = rtb_booking_form.get_latest_viable_time( rule_end_date.getHours(), rule_end_date.getMinutes() );
594								} else {
595									rule_end_time = [ 24, 0 ]; // End of the day
596								}
597	
598								rule_start_time = rtb_booking_form.get_earliest_time( rule_start_time, selected_date, current_date );
599	
600								valid_times.push( { from: rule_start_time, to: rule_end_time, inverted: true } );
601	
602							}
603						}
604					}
605				}
606	
607				rule_start_date = rule_start_time = rule_end_date = rule_end_time = null;
608	
609				// Pass any valid times located
610				if ( valid_times.length > 1 ) {
611					rtb_booking_form.timepicker.set( 'disable', valid_times );
612	
613					return;
614				}
615	
616			}
617
618			// Set it to always open if no rules have been defined
619			rtb_booking_form.timepicker.set( 'enable', true );
620			rtb_booking_form.timepicker.set( 'disable', false );
621		}
622
623		return;
624	};
625
626	/**
627	 * Get the outer times to exclude based on the time interval
628	 *
629	 * This is a work-around for a bug in pickadate.js
630	 * See: https://github.com/amsul/pickadate.js/issues/614
631	 */
632	rtb_booking_form.get_outer_time_range = function() {
633
634		var interval = rtb_booking_form.timepicker.get( 'interval' );
635
636		var hour = 0;
637		var minutes = 0;
638
639		while ( ( hour + ( minutes / 60) + ( interval / 60 ) ) < 24 ) {
640			hour += Math.floor( ( minutes + interval ) / 60 );
641			minutes = ( minutes + interval ) % 60;
642		}
643
644		return { from: [0, 0], to: [ hour, minutes ] };
645	};
646
647	/**
648	 * Get the latest working opening hour/minute value
649	 *
650	 * This is a workaround for a bug in pickadate.js. The end time of a valid
651	 * time value must NOT fall within the last timepicker interval and midnight
652	 * See: https://github.com/amsul/pickadate.js/issues/614
653	 */
654	rtb_booking_form.get_latest_viable_time = function( hour, minute ) {
655
656		var interval = rtb_booking_form.timepicker.get( 'interval' );
657
658		var outer_time_range = this.get_outer_time_range(); 
659
660		/* 
661		* Adjust the last time for wide intervals, so that the last time entered
662		* corresponds to an interval time. A pickadate bug causes a later time to
663		* be available for booking otherwise.
664		 */
665		if ( interval > 60) {
666
667			var last_hour = 0;
668			var last_minute = 0;
669			var last_time_minutes = 0;
670
671			var end_time_minutes = 60 * hour + minute;
672
673			while ( ( last_time_minutes + interval ) <= end_time_minutes ) {
674				
675				var remainder = interval + last_minute;
676
677				while ( remainder >= 60 ) {
678					last_hour++;
679					remainder -= 60;
680				}
681
682				last_minute = remainder;
683
684				last_time_minutes = 60 * last_hour + last_minute;
685			}
686
687			var long_interval_viable_time = [ last_hour, last_minute ];
688		}
689
690		if ( interval > 60 ) {
691			
692			return long_interval_viable_time;
693		}
694		else if ( hour > outer_time_range.to[0] && minute > outer_time_range.to[1] ) {
695			
696			return [ outer_time_range.to[0], outer_time_range.to[1] ];
697		} else {
698			
699			return [ hour, minute ];
700		}
701	};
702
703	/**
704	 * Get the earliest valid time
705	 *
706	 * This checks the valid time for the day and, if a current day, applies
707	 * any late booking restrictions. It also ensures that times in the past
708	 * are not availabe.
709	 *
710	 * @param array start_time
711	 * @param array selected_date
712	 * @param array current_date
713	 */
714	rtb_booking_form.get_earliest_time = function( start_time, selected_date, current_date ) {
715
716		// Only make adjustments for current day selections
717		if ( selected_date.toDateString() !== current_date.toDateString() ) {
718			return start_time;
719		}
720
721		// Get the number of minutes after midnight to compare
722		var start_minutes = ( start_time[0] * 60 ) + start_time[1],
723			current_minutes = ( current_date.getHours() * 60 ) + current_date.getMinutes(),
724			late_booking_minutes;
725
726		start_minutes = start_minutes > current_minutes ? start_minutes : current_minutes;
727
728		if ( typeof rtb_pickadate.late_bookings === 'number' && rtb_pickadate.late_bookings % 1 === 0 ) {
729			late_booking_minutes = current_minutes + rtb_pickadate.late_bookings;
730			if ( late_booking_minutes > start_minutes ) {
731				start_minutes = late_booking_minutes;
732			}
733		}
734
735		start_time = [ Math.floor( start_minutes / 60 ), start_minutes % 60 ];
736
737		return start_time;
738	};
739
740	rtb_booking_form.update_party_size_select = function() {
741		
742		if ( ( rtb_pickadate.enable_max_reservations && ( rtb_pickadate.max_people || rtb_pickadate.multiple_locations_enabled ) ) ||
743			 ( rtb_pickadate.location_timeslot_party_rules ) ) {
744			var partySelect = $('#rtb-party'),
745			selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : '',
746			selected_date = new Date( rtb_booking_form.datepicker.get( 'select', 'yyyy/mm/dd' ) ),
747			selected_date_year = selected_date.getFullYear(),
748			selected_date_month = selected_date.getMonth(),
749			selected_date_date = selected_date.getDate(),
750			selected_time = rtb_booking_form.timepicker.get('value');
751
752			if ( ! selected_time ) { return; }
753
754			selected_date_month = ('0' + (selected_date_month + 1)).slice(-2);
755			selected_date_date = ('0' + selected_date_date).slice(-2);
756
757			//reset party size
758			partySelect.prop("selectedIndex", 0).change();
759
760			var params = {};
761
762			params.action = 'rtb_get_available_party_size';
763			params.nonce  = rtb_booking_form_js_localize.nonce;
764			params.year   = selected_date_year;
765			params.month  = selected_date_month;
766			params.day    = selected_date_date;
767			params.time   = selected_time;
768			params.location = selected_location;
769
770			var data = jQuery.param( params );
771			jQuery.post( ajaxurl, data, function( response ) { console.log( response );
772				if ( ! response ) {
773					return;
774				}
775
776				response = jQuery.parseJSON(response);
777
778				var min_party_size = response.min_party_size;
779				var available_spots = response.available_spots;
780
781				partySelect.prop('disabled', false);
782
783				if ( rtb_booking_form_js_localize.is_admin && rtb_booking_form_js_localize.admin_ignore_maximums ) {
784
785					partySelect.find('> option').prop( 'disabled', false );
786				}
787				else {
788
789					partySelect.find('> option').each(function() {
790						var that = $(this); 
791						if (this.value > available_spots || this.value < min_party_size) {
792							that.prop('disabled', true);
793						} else {
794							that.prop('disabled', false);
795						}
796					});
797				}
798			});
799		}
800	}
801
802	rtb_booking_form.update_possible_tables = function() {
803		var table_select = $('#rtb-table');
804		rtb_booking_form.invalidate_table_request();
805		rtb_booking_form.clear_availability_errors( [ 'table' ] );
806
807		if ( table_select.length ) {
808			table_select.prop( 'disabled', true );
809			table_select.prop( 'selectedIndex', 0 );
810		}
811
812		if ( rtb_pickadate.enable_tables ) {
813
814			var party = $('#rtb-party').val(),
815			selected_location = jQuery( '#rtb-location' ).length ? jQuery( '#rtb-location' ).val() : '',
816			selected_date = new Date( rtb_booking_form.datepicker.get( 'select', 'yyyy/mm/dd' ) ),
817			selected_date_year = selected_date.getFullYear(),
818			selected_date_month = selected_date.getMonth(),
819			selected_date_date = selected_date.getDate(),
820			selected_time = rtb_booking_form.timepicker.get('value');
821
822			let hidden_location = jQuery('.rtb-booking-form-form input[name="rtb-location"]');
823			
824			if ( '' == selected_location && hidden_location.length ) {
825
826				selected_location = hidden_location.val();
827			}
828
829			if ( ! selected_time || ! party || party == 0 ) { return; }
830
831			selected_date_month = ('0' + (selected_date_month + 1)).slice(-2);
832			selected_date_date = ('0' + selected_date_date).slice(-2);
833
834			var booking_id = $( '.rtb-booking-form form input[name="ID"]').length ? $( '.rtb-booking-form form input[name="ID"]').val() : 0;
835
836			var params = {};
837
838			params.action = 'rtb_get_available_tables';
839			params.nonce  = rtb_booking_form_js_localize.nonce;
840			params.year   = selected_date_year;
841			params.month  = selected_date_month;
842			params.day    = selected_date_date;
843			params.time   = selected_time;
844			params.party  = party;
845			params.booking_id = booking_id;
846			params.location_id = selected_location;
847
848			var data = jQuery.param( params ),
849				request_token = rtb_booking_form.table_request_token;
850			rtb_booking_form.table_request = jQuery.post( ajaxurl, data, function( response ) {
851				if ( request_token !== rtb_booking_form.table_request_token ) { return; }
852				
853				if ( ! response ) { return; }
854
855				//remove tables
856				table_select.find('> option').remove();
857
858				table_select.prop('disabled', false);
859
860				response = jQuery.parseJSON(response);
861
862				var available_tables = response.available_tables;
863
864				if ( 1 > Object.keys( available_tables ).length ) {
865					displayFieldError( 'table', rtb_booking_form_js_localize.error['no-table-available'] );
866				}
867				else {
868					clearPrevFieldError( 'table' );
869				}
870
871				if ( rtb_booking_form_js_localize.is_admin ) {
872					table_select.append( '<option></option>' );
873				}
874
875				jQuery.each(available_tables, function(index, element) {
876
877					table_select.append( '<option value="' + index + '">' + element + '</option>' );
878				});
879
880				// pre-select table if it was selected before and is available
881				if ( response.selected_table != -1 ) {
882					table_select.val( response.selected_table );
883				}
884				else if( '' != table_select.data('selected') ) {
885					table_select.val( table_select.data('selected') );
886				}			
887			})
888			.always(function() {
889				if ( request_token === rtb_booking_form.table_request_token ) {
890					rtb_booking_form.table_request = null;
891				}
892			});
893		}
894
895	}
896
897	rtb_booking_form.init();
898});
899
900//Handle reservation modification
901jQuery( document ).ready( function() {
902
903	jQuery( '.rtb-pattern-modify-booking .rtb-modification-form' ).removeClass( 'rtb-hidden' );
904	jQuery( '.rtb-pattern-modify-booking .rtb-booking-form-form' ).addClass( 'rtb-hidden' );
905	jQuery( '.rtb-pattern-modify-booking .rtb-modification-toggle' ).html( rtb_booking_form_js_localize.make );
906
907	jQuery( '.rtb-modification-toggle' ).on( 'click', function() {
908		jQuery('.rtb-modification-form, .rtb-booking-form-form' ).toggleClass( 'rtb-hidden' );
909
910		if ( jQuery( '.rtb-modification-form' ).hasClass( 'rtb-hidden' ) ) {
911			jQuery( '.rtb-modification-toggle' ).html( rtb_booking_form_js_localize.want_to_modify );
912		}
913		else {
914			jQuery( '.rtb-modification-toggle' ).html( rtb_booking_form_js_localize.make );
915		}
916	});
917
918	var modify_booking = function(ev) {
919		var booking_email = jQuery('input[name="rtb_modification_email"]').val();
920		var booking_code = jQuery('input[name="rtb_modification_code"]').val();
921
922		var params = {};
923
924		params.action = 'rtb_find_reservations';
925		params.nonce  = rtb_booking_form_js_localize.nonce;
926		params.booking_email   = booking_email;
927		params.booking_code   = booking_code;
928
929		var data = jQuery.param( params );
930		jQuery.post(ajaxurl, data, function(response) {
931
932			if (response.success) {
933				var booking_html = '';
934				var guest_txt = '';
935				var pay_btn = '';
936
937				jQuery(response.data.bookings).each(function( index, val) {
938					pay_btn = '';
939					guest_txt = val.party > 1 ? rtb_booking_form_js_localize.guests : rtb_booking_form_js_localize.guest;
940					
941					if('payment_pending' == val.status || 'payment_failed' == val.status) {
942						pay_btn = `
943							<div class="rtb-deposit-booking" data-bookingid="${val.ID}" data-bookingemail="${val.email}" data-bookingcode="${val.code}">
944								${rtb_booking_form_js_localize.deposit}
945							</div>
946						`;
947					}
948
949					booking_html += '<div class="rtb-cancel-booking-div">';
950
951					// Only show cancellation button if not passed cancellation cutoff
952					if ( val.datetime_u > ( Date.now() / 1000) + rtb_booking_form_js_localize.cancellation_cutoff * 60 ) {
953
954						booking_html += `
955							<div class="rtb-cancel-booking" data-bookingid="${val.ID}" data-bookingemail="${val.email}" data-bookingcode="${val.code}">
956								${rtb_booking_form_js_localize.cancel}
957							</div>
958						`;
959					}
960
961					booking_html += `
962							${pay_btn}
963							<div class="rtb-booking-information">${val.datetime} - ${val.party} ${guest_txt} (${val.status_lbl})</div>
964						</div>
965					`;
966				});
967
968				jQuery('.rtb-bookings-results').html(booking_html);
969
970				cancellationHandler();
971				delayedPaymentHandler();
972			}
973			else {jQuery('.rtb-bookings-results').html(response.data.msg);}
974		});
975	};
976
977	jQuery(document).on('click', '.rtb-find-reservation-button', modify_booking);
978	jQuery(document).on('keypress', '.rtb-modification-form input', function (ev) {
979		// Capture enter key
980		if(13 == ev.which) {
981			ev.preventDefault();
982			modify_booking(ev);
983		}
984	});
985});
986
987function cancellationHandler() {
988	jQuery('.rtb-cancel-booking:not(.cancelled)').off('click');
989	jQuery('.rtb-cancel-booking:not(.cancelled)').on('click', function() {
990		var btn = jQuery(this);
991
992		if(btn.hasClass('processing')) {
993			return;
994		}
995
996		btn.addClass('processing');
997
998		var booking_id = btn.data('bookingid');
999		var booking_email = btn.data('bookingemail');
1000		var booking_code = btn.data('bookingcode');
1001
1002		var params = {};
1003
1004		params.action = 'rtb_cancel_reservations';
1005		params.nonce  = rtb_booking_form_js_localize.nonce;
1006		params.booking_id = booking_id;
1007		params.booking_email = booking_email;
1008		params.booking_code = booking_code;
1009
1010		var data = jQuery.param( params );
1011		jQuery.post(ajaxurl, data, function(response) {
1012			if (response.success) {
1013				if (response.data.hasOwnProperty('cancelled_redirect')) {
1014					window.location.href = response.data.cancelled_redirect;
1015				}
1016				else {
1017					btn.off('click');
1018					btn.addClass('cancelled');
1019					btn.text(rtb_booking_form_js_localize.cancelled);
1020				}
1021			}
1022			else {
1023				btn.parent().after(`<p class="alert error">${response.data.msg}</p>`);
1024			}
1025
1026			btn.removeClass('processing');
1027		});
1028	});
1029}
1030
1031function delayedPaymentHandler() {
1032	jQuery('.rtb-deposit-booking').off('click');
1033	jQuery('.rtb-deposit-booking').on('click', function() {
1034		var btn = jQuery(this);
1035
1036		if(btn.hasClass('processing')) {
1037			return;
1038		}
1039
1040		btn.addClass('processing');
1041
1042		var booking_id = btn.data('bookingid');
1043		var booking_email = btn.data('bookingemail');
1044
1045		var data = {
1046			'booking_id': booking_id,
1047			'booking_email': booking_email,
1048			'payment': 'rtb-delayed-deposit'
1049		};
1050
1051		let current_loc = window.location;
1052		let params = new URLSearchParams();
1053		Object.keys( data ).map( function( param ) { params.append( param, data[ param ] ) } );
1054
1055		window.location = current_loc.origin + current_loc.pathname + '?' + params.toString();
1056
1057	});
1058}
1059
1060function displayFieldError( field, message ) {
1061
1062	const fieldSelector = '.'+field;
1063	var fieldElm = jQuery('form.rtb-booking-form-form '+fieldSelector);
1064
1065	if( fieldElm.length ) {
1066
1067		clearPrevFieldError( field );
1068
1069		fieldElm.prepend(`
1070			<div class="rtb-error">${message}</div>
1071		`);
1072	}
1073}
1074
1075function clearPrevFieldError( field, include_submission ) {
1076	// Availability refreshes (including startup) are not a new submission.
1077	var errors = get_field_with_error( field );
1078	if ( ! include_submission ) { errors = errors.not( '.rtb-submission-error' ); }
1079	errors.each( (idx, x) => x.remove() );
1080}
1081
1082function field_has_error( field ) {
1083	return get_field_with_error( field ).length;
1084}
1085
1086function get_field_with_error( field ) {
1087	const fieldSelector = '.'+field;
1088	var errorElms = jQuery('form.rtb-booking-form-form ' + fieldSelector + ' .rtb-error');
1089
1090	return errorElms;
1091}
1092
1093// Functions for the 'View Bookings' shortcode
1094jQuery(document).ready(function ($) {
1095	jQuery('.rtb-view-bookings-form-date-selector').on('change', function() {
1096		window.location.href = replaceUrlParam(window.location.href, 'date', jQuery(this).val());
1097	});
1098
1099	jQuery('.rtb-view-bookings-form-location-selector').on('change', function() {
1100		window.location.href = replaceUrlParam(window.location.href, 'booking_location', jQuery(this).val());
1101	});
1102
1103	jQuery('.rtb-edit-view-booking').on('click', function() {
1104		jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').removeClass('rtb-hidden');
1105
1106		jQuery('.rtb-view-bookings-form-confirmation-div').data('bookingid', jQuery(this).data('bookingid'));
1107
1108		jQuery(this).prop('checked', false);
1109	});
1110
1111	jQuery('.rtb-view-bookings-form-confirmation-accept').on('click', function() {
1112		var booking_id = jQuery('.rtb-view-bookings-form-confirmation-div').data('bookingid');
1113
1114		var params = {};
1115
1116		params.action = 'rtb_set_reservation_arrived';
1117		params.nonce = rtb_booking_form_js_localize.nonce;
1118		params.booking = {
1119			'ID':	booking_id
1120		};
1121
1122		var data = $.param( params );
1123
1124		jQuery.post(ajaxurl, data, function(response) {
1125
1126			if (response.success) {window.location.href = window.location.href}
1127			else {jQuery('.rtb-view-bookings-form-confirmation-div').html(response.data.msg);}
1128		});
1129	});
1130
1131	jQuery('.rtb-view-bookings-form-confirmation-decline').on('click', function() {
1132		jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').addClass('rtb-hidden');
1133	});
1134	jQuery('.rtb-view-bookings-form-confirmation-background-div').on('click', function() {
1135		jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').addClass('rtb-hidden');
1136	});
1137	jQuery('#rtb-view-bookings-form-close').on('click', function() {
1138		jQuery('.rtb-view-bookings-form-confirmation-div, .rtb-view-bookings-form-confirmation-background-div').addClass('rtb-hidden');
1139	});
1140});
1141
1142function replaceUrlParam(url, paramName, paramValue)
1143{
1144    if (paramValue == null) {
1145        paramValue = '';
1146    }
1147    var pattern = new RegExp('\\b('+paramName+'=).*?(&|#|$)');
1148    if (url.search(pattern)>=0) {
1149        return url.replace(pattern,'$1' + paramValue + '$2');
1150    }
1151    url = url.replace(/[?#]$/,'');
1152    return url + (url.indexOf('?')>0 ? '&' : '?') + paramName + '=' + paramValue;
1153}
1154
1155// TABLES GRAPHIC
1156jQuery( document ).ready( function() {
1157
1158	var tables_graphic_width = rtb_booking_form_js_localize.tables_graphic_width;
1159	var booking_form_width = 95 - rtb_booking_form_js_localize.tables_graphic_width;
1160
1161	if ( tables_graphic_width != '' ) {
1162		
1163		jQuery( '.rtb-booking-form-with-tables-graphic.left .rtb-booking-form-form' ).css( 'width', booking_form_width+'%' );
1164		jQuery( '.rtb-booking-form-with-tables-graphic.right .rtb-booking-form-form' ).css( 'width', booking_form_width+'%' );
1165		jQuery( '.rtb-tables-graphic-container' ).css( 'width', tables_graphic_width+'%' );
1166	}
1167});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.