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.