1var bookneticPaymentStatus; 2 3(function($) 4{ 5 "use strict"; 6 7 if( typeof window.bookneticInitBookingPage !== 'undefined' ) 8 return; 9 10 window.bookneticHooks = { 11 12 hooks: {}, 13 14 addFilter: function ( key, fn, fn_id ) 15 { 16 key = key.toLowerCase(); 17 18 if ( ! this.hooks.hasOwnProperty( key ) ) 19 { 20 this.hooks[ key ] = {}; 21 } 22 23 if (fn === null && this.hooks[key].hasOwnProperty(fn_id)) { 24 delete this.hooks[key][fn_id]; 25 return 0; 26 } 27 28 if (fn_id === undefined || fn_id === null) { 29 while(true) { 30 fn_id = Math.random().toString(36).substring(2, 15); 31 if (!this.hooks[key].hasOwnProperty(fn_id)) 32 break; 33 } 34 } 35 36 this.hooks[ key ][ fn_id ] = fn; 37 return fn_id; 38 }, 39 40 doFilter: function ( key, params, ...extra ) 41 { 42 key = key.toLowerCase(); 43 44 if (!this.hooks.hasOwnProperty( key ) ) { 45 return params; 46 } 47 48 for (let fn_id in this.hooks[key]) { 49 let fn = this.hooks[key][fn_id]; 50 51 if ( typeof params === 'undefined' ) { 52 params = fn( ...extra ); 53 } else { 54 params = fn( params, ...extra ); 55 } 56 } 57 58 return params; 59 }, 60 61 addAction: function ( key, fn, fn_id ) 62 { 63 return this.addFilter( key, fn, fn_id ); 64 }, 65 66 doAction: function ( key, ...params ) 67 { 68 this.doFilter( key, undefined, ...params ); 69 } 70 }; 71 72 function __( key ) 73 { 74 return key in BookneticData.localization ? BookneticData.localization[ key ] : key; 75 } 76 77 $.fn.handleScrollBooknetic = function() 78 { 79 if ( !this.hasClass('nice-scrollbar-primary') && ! window.matchMedia('(max-width: 1000px)').matches ) 80 { 81 this.addClass( 'nice-scrollbar-primary' ); 82 } 83 84 if( window.matchMedia('(max-width: 1000px)').matches && this.hasClass('nice-scrollbar-primary') ) 85 { 86 booking_panel_js.find(".booknetic_appointment_container_body").removeClass('nice-scrollbar-primary') 87 88 if ( $( '#country-listbox' ).length ) 89 { 90 $( '#country-listbox' ).removeClass('nice-scrollbar-primary') 91 } 92 93 // return; 94 } 95 } 96 97 let index = 0; 98 window.bookneticInitBookingPage = function ( booking_panel_JS_element ) 99 { 100 index++; 101 let booking_panel_js = $(booking_panel_JS_element); 102 103 if( booking_panel_js.data('booknetic_has_been_initiated') === true ) 104 return; 105 106 booking_panel_js.data('booknetic_has_been_initiated', true); 107 108 let booknetic = { 109 110 google_recaptcha_token: null, 111 google_recaptcha_action: 'booknetic_booking_panel_' + index, 112 113 cartArr : [], 114 cartHTMLBody : [], 115 cartHTMLSideBar : [], 116 cartCurrentIndex:0, 117 cartErrors : { 118 a:[], 119 callbacks: [(arr)=>{ 120 if( arr.length > 0 ) 121 { 122 let itemIds = []; 123 124 arr.forEach((value)=>{ 125 if( itemIds.indexOf(value['cart_item']) === -1) 126 itemIds.push(value['cart_item']); 127 }); 128 129 130 booking_panel_js.find('.booknetic-cart-item-error .booknetic-cart-item-error-body').remove(); 131 booking_panel_js.find('.booknetic-cart-item-error').removeClass('show'); 132 133 arr.forEach((value)=>{ 134 if(value['cart_item']!==undefined) 135 { 136 booking_panel_js.find('div.booknetic-cart div[data-index='+ value['cart_item'] +'] .booknetic-cart-item-error').addClass('show'); 137 booking_panel_js.find('div.booknetic-cart div[data-index='+ value['cart_item'] +'] .booknetic-cart-item-error').append(` 138 <div class="booknetic-cart-item-error-body">${value['message']}</div> 139 `); 140 } 141 }) 142 143 144 } 145 else 146 { 147 booking_panel_js.find('.booknetic-cart-item-error .booknetic-cart-item-error-body').remove(); 148 booking_panel_js.find('.booknetic-cart-item-error').removeClass('show'); 149 } 150 }], 151 get error() 152 { 153 return this.a; 154 }, 155 set error(arr) 156 { 157 this.a = arr; 158 for (let i = 0; i < this.callbacks.length; i++) { 159 this.callbacks[i](arr ); 160 } 161 } 162 }, 163 __, 164 165 panel_js : booking_panel_js, 166 167 options: { 168 'templates': { 169 'loader': '<div class="booknetic_loading_layout"></div>' 170 } 171 }, 172 173 localization: { 174 month_names: [ __('January'), __('February'), __('March'), __('April'), __('May'), __('June'), __('July'), __('August'), __('September'), __('October'), __('November'), __('December') ], 175 day_of_week: [ __('Sun'), __('Mon'), __('Tue'), __('Wed'), __('Thu'), __('Fri'), __('Sat'), __('Sun') ] , 176 }, 177 178 calendarDateTimes: {}, 179 time_show_format: 1, 180 calendarYear: null, 181 calendarMonth: null, 182 183 paymentWindow: null, 184 paymentStatus: null, 185 appointmentId: null, // doit: bu failed payment olan appointmenti silmek ucundu, bunu payment_id ederik
186 ajaxResultConfirmStep: null, 187 paymentId: null, 188 dateBasedService: false, 189 serviceData: null, 190 191 globalDayOffs: {}, 192 globalTimesheet: {}, 193 194 195 loading: function ( onOff ) 196 { 197 if( typeof onOff === 'undefined' || onOff ) 198 { 199 booking_panel_js.find('#booknetic_progress').removeClass('booknetic_progress_done').show(); 200 $({property: 0}).animate({property: 100}, { 201 duration: 1000, 202 step: function() 203 { 204 var _percent = Math.round(this.property); 205 if( !booking_panel_js.find('#booknetic_progress').hasClass('booknetic_progress_done') ) 206 { 207 booking_panel_js.find('#booknetic_progress').css('width', _percent+"%"); 208 } 209 } 210 }); 211 212 booking_panel_js.append( this.options.templates.loader ); 213 } 214 else if( ! booking_panel_js.find('#booknetic_progress').hasClass('booknetic_progress_done') ) 215 { 216 booking_panel_js.find('#booknetic_progress').addClass('booknetic_progress_done').css('width', 0); 217 218 // IOS bug... 219 setTimeout(function () 220 { 221 booking_panel_js.find('.booknetic_loading_layout').remove(); 222 }, 0); 223 } 224 }, 225 226 htmlspecialchars_decode: function (string, quote_style) 227 { 228 var optTemp = 0, 229 i = 0, 230 noquotes = false; 231 if(typeof quote_style==='undefined') 232 { 233 quote_style = 2; 234 } 235 string = string.toString().replace(/</g, '<').replace(/>/g, '>'); 236 var OPTS = { 237 'ENT_NOQUOTES': 0, 238 'ENT_HTML_QUOTE_SINGLE': 1, 239 'ENT_HTML_QUOTE_DOUBLE': 2, 240 'ENT_COMPAT': 2, 241 'ENT_QUOTES': 3, 242 'ENT_IGNORE': 4 243 }; 244 if(quote_style===0) 245 { 246 noquotes = true; 247 } 248 if(typeof quote_style !== 'number') 249 { 250 quote_style = [].concat(quote_style); 251 for (i = 0; i < quote_style.length; i++){ 252 if(OPTS[quote_style[i]]===0){ 253 noquotes = true; 254 } else if(OPTS[quote_style[i]]){ 255 optTemp = optTemp | OPTS[quote_style[i]]; 256 } 257 } 258 quote_style = optTemp; 259 } 260 if(quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) 261 { 262 string = string.replace(/�*39;/g, "'"); 263 } 264 if(!noquotes){ 265 string = string.replace(/"/g, '"'); 266 } 267 string = string.replace(/&/g, '&'); 268 return string; 269 }, 270 271 htmlspecialchars: function ( string, quote_style, charset, double_encode ) 272 { 273 var optTemp = 0, 274 i = 0, 275 noquotes = false; 276 if(typeof quote_style==='undefined' || quote_style===null) 277 { 278 quote_style = 2; 279 } 280 string = typeof string != 'string' ? '' : string; 281 282 string = string.toString(); 283 if(double_encode !== false){ 284 string = string.replace(/&/g, '&'); 285 } 286 string = string.replace(/</g, '<').replace(/>/g, '>'); 287 var OPTS = { 288 'ENT_NOQUOTES': 0, 289 'ENT_HTML_QUOTE_SINGLE': 1, 290 'ENT_HTML_QUOTE_DOUBLE': 2, 291 'ENT_COMPAT': 2, 292 'ENT_QUOTES': 3, 293 'ENT_IGNORE': 4 294 }; 295 if(quote_style===0) 296 { 297 noquotes = true; 298 } 299 if(typeof quote_style !== 'number') 300 { 301 quote_style = [].concat(quote_style); 302 for (i = 0; i < quote_style.length; i++) 303 { 304 if(OPTS[quote_style[i]]===0) 305 { 306 noquotes = true; 307 } 308 else if(OPTS[quote_style[i]]) 309 { 310 optTemp = optTemp | OPTS[quote_style[i]]; 311 } 312 } 313 quote_style = optTemp; 314 } 315 if(quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) 316 { 317 string = string.replace(/'/g, '''); 318 } 319 if(!noquotes) 320 { 321 string = string.replace(/"/g, '"'); 322 } 323 return string; 324 }, 325 326 sanitizeHTML: function (node){ 327 node = node.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gi, '$1'); 328 node = node.replace(/<script\b[^>]*>([\s\S]*?)<\/script>/gi, '$1'); 329 return node; 330 }, 331 332 throttle: function(func, wait = 500) { 333 let processing = false; 334 335 return function (e) { 336 if (processing) return; 337 338 processing = true; 339 340 func.call(this, e); 341 342 setTimeout(() => { 343 processing = false; 344 }, wait); 345 } 346 }, 347 348 getCurrentCartItem: () => { 349 return booknetic.cartArr[booknetic.cartCurrentIndex] 350 }, 351 352 debounce: function(func, wait) { 353 let timeout; 354 355 return function(...args) { 356 clearTimeout(timeout); 357 timeout = setTimeout(() => func.apply(this, args), wait); 358 }; 359 }, 360 361 formDataToObject: function ( formData ) 362 { 363 var object = {}; 364 365 formData.forEach(function(value, key) 366 { 367 object[key] = value; 368 }); 369 370 return object; 371 }, 372 373 ajaxResultCheck: function ( res ) 374 { 375 376 if( typeof res != 'object' ) 377 { 378 try 379 { 380 res = JSON.parse(res); 381 } 382 catch(e) 383 { 384 this.toast( 'Error!' ); 385 return false;
386 } 387 } 388 389 if( typeof res['status'] == 'undefined' ) 390 { 391 this.toast( 'Error!' ); 392 return false; 393 } 394 395 if( res['status'] === 'error' ) 396 { 397 if( typeof res['errors'] != 'undefined' && res['errors'].length > 0) 398 { 399 return false; 400 } 401 this.toast( typeof res['error_msg'] == 'undefined' ? 'Error!' : res['error_msg'] ); 402 return false; 403 } 404 405 if( res['status'] === 'ok' ) 406 return true; 407 408 // else 409 410 this.toast( 'Error!' ); 411 return false; 412 }, 413 414 refreshNonce: function ( callback ) 415 { 416 callback = typeof callback === 'function' ? callback : function(){}; 417 418 if ( ! window.bookneticNonceRefreshingPromise ) 419 { 420 window.bookneticNonceRefreshingPromise = new Promise( function ( resolve ) 421 { 422 $.ajax( { 423 url: BookneticData.ajax_url, 424 method: 'POST', 425 data: { action: 'bkntc_ping', tenant_id: BookneticData.tenant_id } 426 } ).done( function ( rawResult ) 427 { 428 let result; 429 try { result = JSON.parse( rawResult ); } catch ( e ) { result = rawResult; } 430 431 if ( result && result['status'] === 'ok' && result['nonce'] ) 432 { 433 BookneticData.ajax_nonce = result['nonce']; 434 } 435 } ).always( function () 436 { 437 window.bookneticNonceRefreshingPromise = null; 438 resolve(); 439 } ); 440 } ); 441 } 442 443 return window.bookneticNonceRefreshingPromise.then( callback ); 444 }, 445 446 ensureFreshNonce: function ( callback ) 447 { 448 callback = typeof callback === 'function' ? callback : function(){}; 449 450 if ( window.bookneticNonceInitialized ) 451 { 452 callback(); 453 return; 454 } 455 456 window.bookneticNonceInitialized = true; 457 booknetic.refreshNonce( callback ); 458 }, 459 460 ajax: function ( action , params , func , loading, fnOnError, async_param ) 461 { 462 // helper: best-effort JSON parse 463 const tryParse = (x) => { try { return JSON.parse(x); } catch { return x; } }; 464 465 // backward-compatible defaults 466 params = typeof params === 'undefined' ? {} : params; 467 func = typeof func === 'function' ? func : function(){}; 468 loading = loading !== false; 469 fnOnError = typeof fnOnError === 'function' ? fnOnError : function(){}; 470 async_param = typeof async_param === 'undefined' ? true : async_param; 471 472 const endLoader = () => { if ( loading ) booknetic.loading( 0 ); }; 473 474 /* ------------------------------------------------------------- 475 Resolve backend / frontend identifiers 476 ------------------------------------------------------------- */ 477 const { backend, frontend } = ( action instanceof Object ) 478 ? { backend: action[ 'backend_action' ], frontend: action[ 'frontend_action' ] } 479 : { backend: action, frontend: action }; 480 481 /* ------------------------------------------------------------- 482 Prepare payload 483 ------------------------------------------------------------- */ 484 const tenantId = BookneticData.tenant_id || booking_panel_js.data( 'tenant_id' ); 485 const isFormData = params instanceof FormData; 486 487 if ( isFormData ) 488 { 489 params.append( 'action', 'bkntc_' + backend ); 490 params.append( 'tenant_id', tenantId ); 491 params.append( '_ajax_nonce', BookneticData.ajax_nonce ); 492 } 493 else 494 { 495 params = { action: 'bkntc_' + backend, tenant_id: tenantId, _ajax_nonce: BookneticData.ajax_nonce, ...params }; 496 } 497 498 /* ------------------------------------------------------------- 499 Hooks (before) and loader 500 ------------------------------------------------------------- */ 501 bookneticHooks.doAction( 'ajax_before_' + frontend, params, booknetic ); 502 params = bookneticHooks.doFilter( 'ajax', params, booknetic ); 503 params = bookneticHooks.doFilter( 'ajax_' + frontend, params, booknetic ); 504 505 if ( loading ) 506 { 507 booknetic.loading( true ); 508 } 509 510 /* ------------------------------------------------------------- 511 Execute request 512 ------------------------------------------------------------- */ 513 let nonceRetried = false; 514 515 const applyNonce = () => 516 { 517 if ( isFormData ) 518 params.set( '_ajax_nonce', BookneticData.ajax_nonce ); 519 else 520 params[ '_ajax_nonce' ] = BookneticData.ajax_nonce; 521 }; 522 523 const performRequest = () => 524 { 525 $.ajax( { 526 url: BookneticData.ajax_url, 527 method: 'POST', 528 data: params, 529 async: async_param, 530 ...( isFormData ? { processData: false, contentType: false } : {} ) 531 } ).done( function ( rawResult ) 532 { 533 endLoader(); 534 535 const result = tryParse( rawResult ); 536 const ok = booknetic.ajaxResultCheck( result, fnOnError ); 537 538 if ( ok ) 539 { 540 func( result ); 541 bookneticHooks.doAction( 'ajax_after_' + frontend + '_success', booknetic, params, result ); 542 } 543 else 544 { 545 fnOnError( result ); 546 bookneticHooks.doAction( 'ajax_after_' + frontend + '_error', booknetic, params, result ); 547 } 548 } ).fail( function ( jqXHR ) 549 { 550 if ( jqXHR.status === 403 && ! nonceRetried ) 551 { 552 nonceRetried = true; 553 booknetic.refreshNonce( function () 554 { 555 applyNonce(); 556 performRequest(); 557 } ); 558 return; 559 } 560 561 endLoader(); 562 booknetic.toast( jqXHR.status + ' error!' ); 563 fnOnError({ status: 'error', error_msg: jqXHR.status + ' error!', jqXHR: jqXHR }); 564 bookneticHooks.doAction( 'ajax_after_' + frontend + '_error', booknetic, params ); 565 } ); 566 }; 567 568 performRequest(); 569 }, 570 571 ajaxAsync: function ( action , params , loading, async_param ) 572 { 573 return new Promise( function ( resolve, reject ) 574 { 575 booknetic.ajax( 576 action, 577 params, 578 function( result ) 579 { 580 resolve( result ); 581 }, 582 loading, 583 function ( error ) 584 { 585 reject( error ); 586 }, 587 async_param 588 ); 589 }); 590 }, 591 592 ajaxRest: (endpoint, method = "GET", data = {}, {noLoading = false} = {}) => { 593 if (!noLoading) booknetic.loading(true); 594 595 return $.ajax({ 596 url: restURL + endpoint, 597 method, 598 data, 599 headers: {"X-Wp-Nonce": BookneticData.rest_nonce}, 600 }) 601 .always(() => { 602 if (!noLoading) booknetic.loading(false); 603 }) 604 .fail((jqXHR) => { 605 booknetic.toast(jqXHR.status + " error!", "unsuccess"); 606 }); 607 }, 608 609 select2Ajax: function ( select, action, parameters ) 610 { 611 var params = {}; 612 params['action'] = 'bkntc_' + action; 613 params['tenant_id'] = BookneticData.tenant_id || booking_panel_js.data('tenant_id'); 614 params['_ajax_nonce'] = BookneticData.ajax_nonce; 615 616 select.select2({ 617 theme: 'bootstrap', 618 placeholder: __('select'), 619 language: { 620 searching: function() { 621 return __('searching'); 622 } 623 }, 624 allowClear: true, 625 ajax: { 626 url: BookneticData.ajax_url, 627 dataType: 'json', 628 type: "POST", 629 data: function ( q ) 630 { 631 var sendParams = params; 632 sendParams['q'] = q['term']; 633 634 if( typeof parameters == 'function' ) 635 { 636 var additionalParameters = parameters( $(this) ); 637 638 for (let key in additionalParameters) 639 { 640 sendParams[key] = additionalParameters[key]; 641 } 642 } 643 else if( typeof parameters == 'object' ) 644 { 645 for (let key in parameters) 646 { 647 sendParams[key] = parameters[key]; 648 } 649 } 650 651 return sendParams; 652 }, 653 processResults: function ( result ) 654 { 655 if( booknetic.ajaxResultCheck( result ) ) 656 { 657 try 658 { 659 result = JSON.parse(result); 660 } 661 catch(e) 662 { 663 664 } 665 666 return result; 667 } 668 } 669 } 670 }); 671 }, 672 673 zeroPad: function(n, p) 674 { 675 p = p > 0 ? p : 2; 676 677 n = String(n); 678 while (n.length < p) 679 n = '0' + n; 680 681 return n; 682 }, 683 684 toast: function( title ) 685 { 686 if( title === false ) 687 { 688 booking_panel_js.find('.booknetic_warning_message').fadeOut(200); 689 return; 690 } 691 692 booking_panel_js.find('.booknetic_warning_message').text( booknetic.htmlspecialchars_decode( title, 'ENT_QUOTES' ) ).fadeIn(300); 693 setTimeout(function () 694 { 695 booking_panel_js.find('.booknetic_warning_message').fadeOut(200); 696 }, 5000); 697 }, 698 699 nonRecurringCalendar: function ( year , month, load_dates_from_backend, load_calendar ) 700 { 701 load_calendar = !!load_calendar; 702 703 const now = new Date(); 704 705 year = year ?? now.getFullYear();
706 month = month ?? now.getMonth(); 707 708 booknetic.loadDefaultDate( year, month, load_calendar ); 709 710 if ( load_dates_from_backend ) 711 booknetic.loadDateFromBackend( year, month, load_calendar ); 712 }, 713 714 loadDefaultDate: function( year, month, load ) 715 { 716 booknetic.calendarYear = year; 717 booknetic.calendarMonth = month; 718 719 booknetic.displayCalendar( load ); 720 booknetic.displayBringPeopleSelect(); 721 }, 722 723 loadDateFromBackend: function( year, month, load ) 724 { 725 booknetic.ajax( 'get_data', booknetic.ajaxParameters( { 726 current_step: 'date_time', 727 year: year, 728 month: month + 1, 729 info: booking_panel_js.data( 'info' ) 730 } ), function ( result ) 731 { 732 booknetic.calendarDateTimes = result[ 'data' ]; 733 booknetic.time_show_format = result[ 'time_show_format' ]; 734 735 booknetic.calendarYear = result[ 'calendar_start_year' ]; 736 booknetic.calendarMonth = result[ 'calendar_start_month' ] - 1; 737 738 booknetic.displayCalendar( load ); 739 booknetic.displayBringPeopleSelect(); 740 741 booknetic.addGroupAppointmentsCounterForBookneticCalendarDays(); 742 } , load ); 743 }, 744 745 displayBringPeopleSelect: function() 746 { 747 var select = booking_panel_js.find('.booknetic_number_of_brought_customers select'); 748 749 var options = ''; 750
751 for(var i = 1; i < booknetic.serviceMaxCapacity; i++ ) 752 { 753 options += '<option value="' + i + '"> + ' + i + '</option>' 754 } 755 756 select.html( options ); 757 758 }, 759 760 displayCalendar: function( loader ) 761 { 762 var _year = booknetic.calendarYear; 763 var _month = booknetic.calendarMonth; 764 765 var htmlContent = "", 766 febNumberOfDays = "", 767 counter = 1, 768 dateNow = new Date(_year , _month ), 769 month = dateNow.getMonth()+1, 770 year = dateNow.getFullYear(), 771 currentDate = new Date(); 772 773 if (month == 2) 774 { 775 febNumberOfDays = ( (year%100!=0) && (year%4==0) || (year%400==0)) ? '29' : '28'; 776 } 777 778 var monthNames = booknetic.localization.month_names; 779 var dayPerMonth = [null, '31', febNumberOfDays ,'31','30','31','30','31','31','30','31','30','31'] 780 781 var nextDate = new Date(month +'/01/'+year); 782 var weekdays = nextDate.getDay(); 783 784 let weekdays2; 785 let week_start_n; 786 let week_end_n; 787 788 if( BookneticData.week_starts_on === 'monday' ) 789 { 790 weekdays2 = weekdays == 0 ? 7 : weekdays; 791 week_start_n = 1; 792 week_end_n = 7; 793 } 794 else 795 { 796 weekdays2 = weekdays; 797 week_start_n = 0; 798 week_end_n = 6; 799 } 800 801 var numOfDays = dayPerMonth[month]; 802 803 for( let w = week_start_n; w < weekdays2; w++ ) 804 { 805 htmlContent += "<div class=\"booknetic_td booknetic_empty_day\"></div>"; 806 } 807 808 while (counter <= numOfDays) 809 { 810 if (weekdays2 > week_end_n) 811 { 812 weekdays2 = week_start_n; 813 htmlContent += "</div><div class=\"booknetic_calendar_rows\">"; 814 } 815 var date_formatted = year + '-' + booknetic.zeroPad(month) + '-' + booknetic.zeroPad(counter); 816 let date_format_view; 817 818 if( BookneticData.date_format === 'Y-m-d' ) 819 { 820 date_format_view = year + '-' + booknetic.zeroPad(month) + '-' + booknetic.zeroPad(counter); 821 } 822 else if( BookneticData.date_format === 'd-m-Y' ) 823 { 824 date_format_view = booknetic.zeroPad(counter) + '-' + booknetic.zeroPad(month) + '-' + year; 825 } 826 else if( BookneticData.date_format === 'm/d/Y' ) 827 { 828 date_format_view = booknetic.zeroPad(month) + '/' + booknetic.zeroPad(counter) + '/' + year; 829 } 830 else if( BookneticData.date_format === 'd/m/Y' ) 831 { 832 date_format_view = booknetic.zeroPad(counter) + '/' + booknetic.zeroPad(month) + '/' + year; 833 } 834 else if( BookneticData.date_format === 'd.m.Y' ) 835 { 836 date_format_view = booknetic.zeroPad(counter) + '.' + booknetic.zeroPad(month) + '.' + year; 837 } 838 839 var addClass = ''; 840 if( !(date_formatted in booknetic.calendarDateTimes['dates']) || booknetic.calendarDateTimes['dates'][ date_formatted ].length == 0 ) 841 { 842 addClass = ' booknetic_calendar_empty_day'; 843 } 844 845 var loadLine = booknetic.drawLoadLine( date_formatted ); 846 847 htmlContent +="<div class=\"booknetic_td booknetic_calendar_days"+addClass+"\" data-date=\"" + date_formatted + "\" data-date-format=\"" + date_format_view + "\"><div>"+counter+"<span>" + loadLine + "</span></div></div>"; 848 849 weekdays2++; 850 counter++; 851 } 852 853 for( let w = weekdays2; w <= week_end_n; w++ ) 854 { 855 htmlContent += "<div class=\"booknetic_td booknetic_empty_day\"></div>"; 856 } 857 858 var calendarBody = "<div class=\"booknetic_calendar\">"; 859 860 calendarBody += "<div class=\"booknetic_calendar_rows booknetic_week_names\">"; 861 862 for( var w = 0; w < booknetic.localization.day_of_week.length; w++ ) 863 { 864 if( w > week_end_n || w < week_start_n ) 865 continue; 866 867 calendarBody += "<div class=\"booknetic_td\">" + booknetic.localization.day_of_week[ w ] + "</div>"; 868 } 869 870 calendarBody += "</div>"; 871 872 calendarBody += "<div class=\"booknetic_calendar_rows\">"; 873 calendarBody += htmlContent; 874 calendarBody += "</div></div>"; 875 876 booking_panel_js.find("#booknetic_calendar_area").html( calendarBody ); 877 878 booking_panel_js.find("#booknetic_calendar_area .days[data-count]:first").trigger('click'); 879 880 booking_panel_js.find(".booknetic_month_name").text( monthNames[ _month ] + ' ' + _year ); 881 booking_panel_js.find('.booknetic_times_list').empty(); 882 booking_panel_js.find('.booknetic_times_title').text(__('Select date')); 883 884 if( !loader ) 885 { 886 booking_panel_js.find(".booknetic_preloader_card3_box").hide(); 887
888 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="date_time"]').fadeIn(200, function() 889 { 890 booking_panel_js.find(".booknetic_appointment_container_body").scrollTop(0); 891 booknetic.handleScroll(); 892 }); 893 } 894 }, 895 896 drawLoadLine: function( date ) 897 { 898 let zoom = function (input, outputSize) { 899 if (input.length === outputSize) { 900 return input; 901 } 902 const ratio = outputSize / input.length; 903 904 const output = new Array(outputSize); 905 906 for (let i = 0; i < outputSize; i++) { 907 let value = false; 908 909 const from = i / ratio; 910 const inc = Math.max(1, 1 / ratio); 911 912 for (let j = Math.floor(from); j < Math.floor(from + inc); j++) { 913 value = value || input[j]; 914 } 915 916 output[i] = value; 917 } 918 919 return output; 920 }; 921 922 var fills = date in booknetic.calendarDateTimes['fills'] ? booknetic.calendarDateTimes['fills'][ date ] : [0]; 923 var data = date in booknetic.calendarDateTimes['dates'] ? booknetic.calendarDateTimes['dates'][ date ] : []; 924 if (data.length === 1 && booknetic.dateBasedService && !( 'hide_available_slots' in booknetic.calendarDateTimes && booknetic.calendarDateTimes['hide_available_slots'] === 'on' )) { 925 fills = []; 926 for (let i = 0; i < data[0].max_capacity; i++) { 927 fills.push(data[0].max_capacity - data[0].weight - i > 0 ? 1 : 0); 928 } 929 } 930 931 var day_schedule = zoom(fills, 17); 932 933 var line = ''; 934 for( var j = 0; j < day_schedule.length; j++ ) 935 { 936 var isFree = day_schedule[j]; 937 line += '<i '+ (isFree?'a':'b') + '></i>'; 938 } 939 940 return line; 941 }, 942 943 timeToMin: function(str) 944 { 945 str = str.split(':'); 946 947 return parseInt(str[0]) * 60 + parseInt(str[1]); 948 }, 949 950 timeZoneOffset: function() 951 { 952 if( BookneticData.client_time_zone === 'off' ) 953 return '-'; 954 955 if ( window.Intl && typeof window.Intl === 'object' ) 956 { 957 return Intl.DateTimeFormat().resolvedOptions().timeZone; 958 } 959 else 960 { 961 return new Date().getTimezoneOffset(); 962 } 963 }, 964 965 datePickerFormat: function() 966 { 967 if( BookneticData.date_format === 'd-m-Y' ) 968 { 969 return 'dd-mm-yyyy'; 970 } 971 else if( BookneticData.date_format === 'm/d/Y' ) 972 { 973 return 'mm/dd/yyyy'; 974 } 975 else if( BookneticData.date_format === 'd/m/Y' ) 976 { 977 return 'dd/mm/yyyy'; 978 } 979 else if( BookneticData.date_format === 'd.m.Y' ) 980 { 981 return 'dd.mm.yyyy'; 982 } 983 984 return 'yyyy-mm-dd'; 985 }, 986 987 convertDate: function( date, from, to ) 988 { 989 if( date == '' ) 990 return date; 991 if( typeof to === 'undefined' ) 992 { 993 to = booknetic.datePickerFormat(); 994 } 995 996 to = to.replace('yyyy', 'Y').replace('dd', 'd').replace('mm', 'm'); 997 from = from.replace('yyyy', 'Y').replace('dd', 'd').replace('mm', 'm'); 998 999 var delimetr = from.indexOf('-') > -1 ? '-' : ( from.indexOf('.') > -1 ? '.' : '/' ); 1000 var delimetr_to = to.indexOf('-') > -1 ? '-' : ( to.indexOf('.') > -1 ? '.' : '/' ); 1001 var date_split = date.split(delimetr); 1002 var date_from_split = from.split(delimetr); 1003 var date_to_split = to.split(delimetr_to); 1004 1005 var parts = {'m':0, 'd':0, 'Y':0}; 1006 1007 date_from_split.forEach(function( val, i ) 1008 { 1009 parts[ val ] = i; 1010 }); 1011 1012 var new_date = ''; 1013 date_to_split.forEach(function( val, j ) 1014 { 1015 new_date += (new_date == '' ? '' : delimetr_to) + date_split[ parts[ val ] ]; 1016 }); 1017 1018 return new_date; 1019 }, 1020 1021 getSelected: { 1022 1023 location: function() 1024 { 1025 if( booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="location"]').hasClass('booknetic_menu_hidden') ) 1026 { 1027 var val = booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="location"]').data('value'); 1028 } 1029 else 1030 { 1031 val = booking_panel_js.find(".booknetic_appointment_container_body [data-step-id=\"location\"] .booknetic_card_selected").data('id'); 1032 } 1033 1034 return val ? val : ''; 1035 }, 1036 1037 locationCategory: function() 1038 { 1039 return booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="location"]').data('location-category'); 1040 }, 1041 1042 staff: function() 1043 { 1044 if( booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="staff"]').hasClass('booknetic_menu_hidden') ) 1045 { 1046 var val = booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="staff"]').data('value'); 1047 } 1048 else 1049 {
1050 val = booking_panel_js.find(".booknetic_appointment_container_body [data-step-id=\"staff\"] .booknetic_card_selected").data('id'); 1051 } 1052 1053 return val ? val : ''; 1054 }, 1055 1056 service: function() 1057 { 1058 const serviceId = booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="service"]').data('value'); 1059 1060 if( serviceId ) 1061 { 1062 var val = serviceId; 1063 } 1064 else 1065 { 1066 val = booking_panel_js.find(".booknetic_appointment_container_body [data-step-id=\"service\"] .booknetic_service_card_selected").data('id'); 1067 } 1068 1069 return val ? val : ''; 1070 }, 1071 1072 serviceCategory: function() 1073 { 1074 return booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="service"]').data('service-category'); 1075 }, 1076 1077 serviceIsRecurring: function() 1078 { 1079 let val; 1080 1081 if( booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="service"]').hasClass('booknetic_menu_hidden') ) 1082 { 1083 val = booking_panel_js.find( '.booknetic_appointment_step_element[data-step-id="service"]' ).data( 'is-recurring' ); 1084 } 1085 else 1086 { 1087 val = booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="service"] .booknetic_service_card_selected').data('is-recurring'); 1088 } 1089 1090 return val == '1'; 1091 }, 1092 1093 serviceExtras: function() 1094 { 1095 var extras = []; 1096 1097 booking_panel_js.find(".booknetic_appointment_container_body [data-step-id=\"service_extras\"] .booknetic_service_extra_card_selected").each(function() 1098 { 1099 var extra_id = $(this).data('id'), 1100 quantity = parseInt( $(this).find('.booknetic_service_extra_quantity_input').val() ); 1101 1102 if( quantity > 0 ) 1103 { 1104 extras.push({ 1105 extra: extra_id, 1106 quantity: quantity, 1107 }); 1108 } 1109 }); 1110 1111 return extras; 1112 }, 1113 1114 date: function() 1115 { 1116 if( booknetic.getSelected.serviceIsRecurring() ) 1117 return ''; 1118 1119 var val = booking_panel_js.find(".booknetic_selected_time").data('date-original'); 1120 return val ? val : ''; 1121 }, 1122 1123 date_in_customer_timezone: function() 1124 { 1125 if( booknetic.getSelected.serviceIsRecurring() ) 1126 return ''; 1127 1128 var val = booking_panel_js.find(".booknetic_calendar_selected_day").data('date'); 1129 return val ? val : ''; 1130 }, 1131 1132 time: function() 1133 { 1134 if( booknetic.getSelected.serviceIsRecurring() ) 1135 return booknetic.getSelected.recurringTime(); 1136 1137 var val = booking_panel_js.find(".booknetic_selected_time").data('time'); 1138 return val ? val : ''; 1139 }, 1140 1141 brought_people_count: function() 1142 { 1143 if( ! booking_panel_js.find('#booknetic_bring_someone_checkbox ').is(':checked') ) 1144 return 0; 1145 1146 let broughtPeopleInput = booking_panel_js.find('.booknetic_number_of_brought_customers_quantity_input'); 1147 let val = Number( broughtPeopleInput.val() ); 1148 let max = Number( broughtPeopleInput.data( 'max-quantity' ) ); 1149 1150 val = Number.isInteger( val ) ? val : 0; 1151 1152 return val > max ? max : val; 1153 }, 1154 1155 dateTime: function() 1156 { 1157 if( booknetic.getSelected.serviceIsRecurring() ) 1158 return booknetic.getSelected.recurringTime(); 1159 1160 var val = booking_panel_js.find(".booknetic_selected_time").data('full-date-time-start'); 1161 return val ? val : ''; 1162 }, 1163 1164 formData: function () 1165 { 1166 var data = { data: {email: "", first_name: "", last_name: "", phone: ""} }; 1167 1168 booking_panel_js.find('input[name]#bkntc_input_name, input[name]#bkntc_input_surname, input[name]#bkntc_input_email, input[name]#bkntc_input_phone').each(function() 1169 { 1170 let name = $(this).attr('name'); 1171 let value = $(this).val(); 1172 1173 if(name === 'phone'){ 1174 const itiInstance = booking_panel_js.find('#bkntc_input_phone').data('iti'); 1175 value = itiInstance.getNumber(bookneticIntlTelInput.utils?.numberFormat?.E164 ?? 0); 1176 } 1177 1178 if ( name === 'email' ) 1179 value = value.trim() 1180 1181 data['data'][name] = value; 1182 }); 1183 1184 return data; 1185 }, 1186 1187 customerId: function () 1188 {
1189 let customerId = booking_panel_js.find('.bkntc_input_identifier_input').data('customer-id'); 1190 1191 return customerId || 0; 1192 }, 1193 1194 paymentMethod: function () 1195 { 1196 if( booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="confirm_details"]').hasClass('booknetic_menu_hidden') ) 1197 return 'local'; 1198 1199 return booking_panel_js.find('.booknetic_payment_method.booknetic_payment_method_selected').attr('data-payment-type'); 1200 }, 1201 1202 paymentDepositFullAmount: function () 1203 { 1204 return booking_panel_js.find('input[name="input_deposit"][type="radio"]:checked').val() == '0' ? true : false; 1205 }, 1206 1207 recurringStartDate: function() 1208 { 1209 var val = booking_panel_js.find("#booknetic_recurring_start").val(); 1210 1211 if( val == '' || val == undefined ) 1212 return ''; 1213 1214 return booknetic.convertDate( val, booknetic.datePickerFormat(), 'Y-m-d' ); 1215 }, 1216 1217 recurringEndDate: function() 1218 { 1219 var val = booking_panel_js.find("#booknetic_recurring_end").val(); 1220 1221 if( val == '' || val == undefined ) 1222 return ''; 1223 1224 return booknetic.convertDate( val, booknetic.datePickerFormat(), 'Y-m-d' ); 1225 }, 1226 1227 recurringTimesArr: function() 1228 { 1229 if( !booknetic.serviceData ) 1230 return JSON.stringify( {} ); 1231 1232 var repeatType = booknetic.serviceData['repeat_type'], 1233 recurringTimes = {}; 1234 1235 if( repeatType == 'weekly' ) 1236 { 1237 booking_panel_js.find(".booknetic_times_days_of_week_area > .booknetic_active_day").each(function() 1238 { 1239 var dayNum = $(this).data('day'); 1240 var time = $(this).find('.booknetic_wd_input_time').val(); 1241 1242 recurringTimes[ dayNum ] = time; 1243 }); 1244 1245 recurringTimes = JSON.stringify( recurringTimes ); 1246 } 1247 else if( repeatType == 'daily' ) 1248 { 1249 recurringTimes = booking_panel_js.find("#booknetic_daily_recurring_frequency").val(); 1250 } 1251 else if( repeatType == 'monthly' ) 1252 { 1253 recurringTimes = booking_panel_js.find("#booknetic_monthly_recurring_type").val(); 1254 if( recurringTimes == 'specific_day' ) 1255 { 1256 recurringTimes += ':' + ( booking_panel_js.find("#booknetic_monthly_recurring_day_of_month").val() == null ? '' : booking_panel_js.find("#booknetic_monthly_recurring_day_of_month").val().join(',') ); 1257 } 1258 else 1259 { 1260 recurringTimes += ':' + booking_panel_js.find("#booknetic_monthly_recurring_day_of_week").val(); 1261 } 1262 } 1263 1264 return recurringTimes; 1265 }, 1266 1267 recurringTimesArrFinish: function() 1268 { 1269 var recurringDates = []; 1270 var hasTimeError = false; 1271 1272 booking_panel_js.find("#booknetic_recurring_dates > tr").each(function() 1273 { 1274 var sDate = $(this).find('[data-date]').attr('data-date'); 1275 var sTime = $(this).find('[data-time]').attr('data-time'); 1276 if($(this).find('[data-service-type]').attr('data-service-type') === 'datebased') 1277 { 1278 sTime = '00:00'; 1279 } 1280 // if tried to change the time 1281 if( $(this).find('.booknetic_time_select').length ) 1282 { 1283 sTime = $(this).find('.booknetic_time_select').val(); 1284 if( sTime == '' ) 1285 { 1286 hasTimeError = true; 1287 } 1288 } 1289 else if( $(this).find('.booknetic_data_has_error').length > 0 ) 1290 { 1291 hasTimeError = ! booknetic.dateBasedService; 1292 } 1293 1294 recurringDates.push([ sDate, sTime ]); 1295 }); 1296 1297 if( hasTimeError ) 1298 { 1299 return false; 1300 } 1301 1302 return JSON.stringify( recurringDates ); 1303 }, 1304 1305 recurringDateValidate: function() 1306 { 1307 let dateError = true; 1308 booking_panel_js.find("#booknetic_recurring_dates > tr").each(function() 1309 { 1310 if( $(this).find('td[data-date] span.booknetic_data_has_error').length > 0 ) 1311 { 1312 dateError = false; 1313 } 1314 }); 1315 return dateError; 1316 }, 1317 1318 recurringTime: function () 1319 { 1320 if( !booknetic.serviceData ) 1321 return ''; 1322 1323 var repeatType = booknetic.serviceData['repeat_type'], 1324 time = ''; 1325 1326 if( repeatType == 'daily' ) 1327 { 1328 time = booking_panel_js.find("#booknetic_daily_time").val(); 1329 } 1330 else if( repeatType == 'monthly' ) 1331 { 1332 time = booking_panel_js.find("#booknetic_monthly_time").val(); 1333 } 1334 1335 return time; 1336 } 1337 1338 }, 1339 1340 ajaxParameters: function ( defaultData = undefined , bool = true ) 1341 { 1342 var data = new FormData(); 1343
1344 data.append( 'payment_method', booknetic.getSelected.paymentMethod() ); 1345 data.append( 'deposit_full_amount', booknetic.getSelected.paymentDepositFullAmount() ? 1 : 0 ); 1346 data.append( 'client_time_zone', booknetic.timeZoneOffset() ); 1347 1348 data.append( 'google_recaptcha_token', booknetic.google_recaptcha_token ); 1349 data.append( 'google_recaptcha_action', booknetic.google_recaptcha_action ); 1350 1351 if( typeof defaultData != 'undefined' ) 1352 { 1353 for ( var key in defaultData ) 1354 { 1355 data.append( key, defaultData[key] ); 1356 } 1357 } 1358 1359 if ( bool ) 1360 { 1361 this.stepManager.saveData(); 1362 } 1363 1364 data.append( 'cart', JSON.stringify(booknetic.cartArr) ); 1365 data.append( 'current', booknetic.cartCurrentIndex ); 1366 data.append( 'query_params', booknetic.getURLQueryParams() ); 1367 1368 return bookneticHooks.doFilter( 'appointment_ajax_data', data, booknetic ); 1369 }, 1370 1371 getURLQueryParams: function() 1372 { 1373 const queryString = window.location.search; 1374 const searchParams = new URLSearchParams( queryString ); 1375 1376 let query_params = {}; 1377 1378 searchParams.forEach( ( value, key ) => { 1379 query_params[ key ] = value; 1380 } ); 1381 1382 return JSON.stringify( query_params ); 1383 }, 1384 1385 calcRecurringTimes: function() 1386 { 1387 booknetic.serviceFixPeriodEndDate(); 1388 1389 var fullPeriod = booknetic.serviceData['full_period_value']; 1390 var repeatType = booknetic.serviceData['repeat_type']; 1391 var startDate = booknetic.getSelected.recurringStartDate(); 1392 var endDate = booknetic.getSelected.recurringEndDate(); 1393 1394 if( startDate == '' || endDate == '' ) 1395 return; 1396 1397 endDate = booknetic.getDateWithUTC( endDate ); 1398 1399 var cursor = booknetic.getDateWithUTC( startDate ), 1400 numberOfAppointments = 0, 1401 frequency = ( repeatType === 'daily' ) ? booking_panel_js.find( '#booknetic_daily_recurring_frequency' ).val() : 1; 1402 1403 if( !( frequency >= 1 ) ) 1404 { 1405 frequency = 1; 1406 if( repeatType === 'daily' ) 1407 { 1408 booking_panel_js.find('#booknetic_daily_recurring_frequency').val('1'); 1409 } 1410 } 1411 1412 var activeDays = {}; 1413 if( repeatType === 'weekly' ) 1414 { 1415 booking_panel_js.find(".booknetic_times_days_of_week_area > .booknetic_active_day").each(function() 1416 { 1417 activeDays[ $(this).data('day') ] = true; 1418 }); 1419 1420 if( $.isEmptyObject( activeDays ) ) 1421 { 1422 return; 1423 } 1424 } 1425 else if( repeatType === 'monthly' ) 1426 { 1427 var monthlyRecurringType = booking_panel_js.find("#booknetic_monthly_recurring_type").val(); 1428 var monthlyDayOfWeek = booking_panel_js.find("#booknetic_monthly_recurring_day_of_week").val(); 1429 1430 var selectedDays = booking_panel_js.find("#booknetic_monthly_recurring_day_of_month").select2('val'); 1431 1432 if( selectedDays ) 1433 { 1434 for( var i = 0; i < selectedDays.length; i++ ) 1435 { 1436 activeDays[ selectedDays[i] ] = true; 1437 } 1438 } 1439 } 1440 1441 while( cursor <= endDate ) 1442 { 1443 var weekNum = cursor.getDay(); 1444 //todo://why did we use parseInt here and on many other places? busy with other tasks rn 1445 // test it out in the future. 1446 var dayNumber = parseInt( cursor.getDate() ); 1447 weekNum = weekNum > 0 ? weekNum : 7;
1448 var dateFormat = cursor.getFullYear() + '-' + booknetic.zeroPad( cursor.getMonth() + 1 ) + '-' + booknetic.zeroPad( cursor.getDate() ); 1449 1450 if( repeatType === 'monthly' ) 1451 { 1452 if( ( monthlyRecurringType === 'specific_day' && typeof activeDays[ dayNumber ] != 'undefined' ) || booknetic.getMonthWeekInfo( cursor, monthlyRecurringType, monthlyDayOfWeek ) ) 1453 { 1454 if( 1455 // if is not off day for staff or service 1456 !( typeof booknetic.globalTimesheet[ weekNum-1 ] != 'undefined' && booknetic.globalTimesheet[ weekNum-1 ]['day_off'] ) && 1457 // if is not holiday for staff or service 1458 typeof booknetic.globalDayOffs[ dateFormat ] == 'undefined' 1459 ) 1460 { 1461 numberOfAppointments++; 1462 } 1463 } 1464 } 1465 else if( 1466 // if weekly repeat type then only selected days of the week... 1467 ( typeof activeDays[ weekNum ] != 'undefined' || repeatType === 'daily' ) && 1468 // if is not off day for staff or service 1469 !( typeof booknetic.globalTimesheet[ weekNum-1 ] != 'undefined' && booknetic.globalTimesheet[ weekNum-1 ]['day_off'] ) && 1470 // if is not holiday for staff or service 1471 typeof booknetic.globalDayOffs[ dateFormat ] == 'undefined' 1472 ) 1473 { 1474 numberOfAppointments++; 1475 } 1476 1477 cursor = new Date( cursor.getTime() + 1000 * 24 * 3600 * frequency ); 1478 } 1479 1480 booking_panel_js.find('#booknetic_recurring_times').val( numberOfAppointments ); 1481 1482 }, 1483 1484 initRecurringElements: function( ) 1485 { 1486 booknetic.select2Ajax( booking_panel_js.find(".booknetic_wd_input_time, #booknetic_daily_time, #booknetic_monthly_time"), 'get_available_times_all', function( select ) 1487 { 1488 var dayNumber = ( select.attr( 'id' ) === 'booknetic_daily_time' || select.attr( 'id' ) === 'booknetic_monthly_time' ) ? -1 : select.attr( 'id' ).replace( 'booknetic_time_wd_', '' ); 1489 1490 return booknetic.formDataToObject( booknetic.ajaxParameters({day_number: dayNumber}) ); 1491 }); 1492 1493 booking_panel_js.find("#booknetic_monthly_recurring_day_of_month").select2({ 1494 theme: 'bootstrap', 1495 placeholder: __('select'), 1496 allowClear: true, 1497 maximumSelectionLength: booknetic.serviceData[ 'repeat_frequency' ], 1498 closeOnSelect: false, 1499 }).on( 'select2:select', function( e ) 1500 { 1501 // https://github.com/select2/select2/issues/3514 1502 1503 if ( 1504 $( this ).select2( "data" ).length >= 1505 $( this ).data( "select2" ).results.data.maximumSelectionLength 1506 ) 1507 { 1508 $( this ).select2( "close" ); 1509 } 1510 }); 1511 1512 booking_panel_js.find("#booknetic_monthly_recurring_type, #booknetic_monthly_recurring_day_of_week").select2({ 1513 theme: 'bootstrap', 1514 placeholder: __('select'), 1515 minimumResultsForSearch: -1 1516 }); 1517 1518 booking_panel_js.find('#booknetic_monthly_recurring_type').trigger('change'); 1519 1520 booknetic.initDatepicker( booking_panel_js.find("#booknetic_recurring_start") ); 1521 booknetic.initDatepicker( booking_panel_js.find("#booknetic_recurring_end") ); 1522 1523 booknetic.serviceFixPeriodEndDate(); 1524 booknetic.serviceFixFrequency(); 1525 booking_panel_js.find("#booknetic_recurring_start").trigger('change'); 1526 }, 1527 1528 loadAvailableDate: function(instance ,data) 1529 { 1530 booknetic.ajax( 'get_recurring_available_dates', data, function ( result ) 1531 { 1532 instance.set('enable',result['available_dates']); 1533 }); 1534 }, 1535 1536 serviceFixPeriodEndDate: function() 1537 { 1538 let startDate, endDate; 1539 let serviceData = booknetic.serviceData; 1540 1541 if( serviceData && serviceData['full_period_value'] > 0 ) 1542 { 1543 booking_panel_js.find("#booknetic_recurring_end").attr('disabled', true); 1544 booking_panel_js.find("#booknetic_recurring_times").attr('disabled', true); 1545 1546 startDate = booknetic.getSelected.recurringStartDate(); 1547 1548 if( serviceData[ 'full_period_type' ] === 'month' ) 1549 { 1550 endDate = new Date( startDate + "T00:00:00" ); 1551 endDate.setMonth( endDate.getMonth() + parseInt( serviceData['full_period_value'] ) ); 1552 endDate.setDate( endDate.getDate() - 1 ); 1553 1554 booking_panel_js.find("#booknetic_recurring_end").val( booknetic.convertDate( endDate.getFullYear() + '-' + booknetic.zeroPad( endDate.getMonth() + 1 ) + '-' + booknetic.zeroPad( endDate.getDate() ), 'Y-m-d' ) ); 1555 } 1556 else if( serviceData[ 'full_period_type' ] === 'week' ) 1557 { 1558 1559 endDate = new Date( startDate + "T00:00:00" ); 1560 endDate.setDate( endDate.getDate() + parseInt( serviceData['full_period_value'] ) * 7 - 1 ); 1561 1562 booking_panel_js.find("#booknetic_recurring_end").val( booknetic.convertDate( endDate.getFullYear() + '-' + booknetic.zeroPad( endDate.getMonth() + 1 ) + '-' + booknetic.zeroPad( endDate.getDate() ), 'Y-m-d' ) ); 1563 } 1564 else if( serviceData[ 'full_period_type' ] === 'day' ) 1565 { 1566 endDate = new Date( startDate + "T00:00:00" ); 1567 endDate.setDate( endDate.getDate() + parseInt( serviceData['full_period_value'] ) - 1 ); 1568 1569 booking_panel_js.find("#booknetic_recurring_end").val( booknetic.convertDate( endDate.getFullYear() + '-' + booknetic.zeroPad( endDate.getMonth() + 1 ) + '-' + booknetic.zeroPad( endDate.getDate() ), 'Y-m-d' ) ); 1570 } 1571 else if( serviceData[ 'full_period_type' ] === 'time' ) 1572 {
1573 if( booknetic.getSelected.recurringEndDate() == '' ) 1574 { 1575 startDate = new Date( booknetic.getSelected.recurringStartDate() ); 1576 endDate = new Date( startDate.setMonth( startDate.getMonth() + 1 ) ); 1577 1578 booking_panel_js.find("#booknetic_recurring_end").val( booknetic.convertDate( endDate.getFullYear() + '-' + booknetic.zeroPad( endDate.getMonth() + 1 ) + '-' + booknetic.zeroPad( endDate.getDate() ), 'Y-m-d' ) ); 1579 } 1580 1581 booking_panel_js.find("#booknetic_recurring_times").val( serviceData['full_period_value'] ).trigger('keyup'); 1582 } 1583 } 1584 else 1585 { 1586 booking_panel_js.find("#booknetic_recurring_end").attr('disabled', false); 1587 booking_panel_js.find("#booknetic_recurring_times").attr('disabled', false); 1588 1589 if( booknetic.getSelected.recurringEndDate() == '' ) 1590 { 1591 startDate = new Date( booknetic.getSelected.recurringStartDate() ); 1592 endDate = new Date( startDate.setMonth( startDate.getMonth() + 1 ) ); 1593 1594 booking_panel_js.find("#booknetic_recurring_end").val( booknetic.convertDate( endDate.getFullYear() + '-' + booknetic.zeroPad( endDate.getMonth() + 1 ) + '-' + booknetic.zeroPad( endDate.getDate() ), 'Y-m-d' ) ); 1595 } 1596 } 1597 }, 1598 1599 serviceFixFrequency: function() 1600 { 1601 var serviceData = booknetic.serviceData; 1602 1603 if( serviceData && serviceData[ 'repeat_frequency' ] > 0 && serviceData[ 'repeat_type' ] === 'daily' ) 1604 { 1605 booking_panel_js.find("#booknetic_daily_recurring_frequency").val( serviceData['repeat_frequency'] ).attr('disabled', true); 1606 } 1607 else 1608 { 1609 booking_panel_js.find("#booknetic_daily_recurring_frequency").attr('disabled', false); 1610 } 1611 }, 1612 1613 getMonthWeekInfo: function( date, type, dayOfWeek ) 1614 { 1615 var jsDate = new Date( date ), 1616 weekd = jsDate.getDay(); 1617 weekd = weekd === 0 ? 7 : weekd; 1618 1619 if( weekd != dayOfWeek ) 1620 { 1621 return false; 1622 } 1623
1624 const month = jsDate.getMonth() + 1, 1625 year = jsDate.getFullYear(); 1626 1627 if( type === 'last' ) 1628 { 1629 var nextWeek = new Date(jsDate.getTime()); 1630 nextWeek.setDate( nextWeek.getDate() + 7 ); 1631 1632 return nextWeek.getMonth() + 1 !== month; 1633 } 1634 1635 const firstDayOfMonth = new Date(year + '-' + booknetic.zeroPad(month) + '-01'), 1636 firstWeekDay = firstDayOfMonth.getDay() === 0 ? 7 : firstDayOfMonth.getDay(); 1637 1638 const dif = (dayOfWeek >= firstWeekDay ? dayOfWeek : parseInt(dayOfWeek) + 7) - firstWeekDay; 1639 const days = jsDate.getDate() - dif, 1640 dNumber = parseInt(days / 7) + 1; 1641 1642 return type == dNumber; 1643 }, 1644 1645 confirmAppointment: async function () 1646 { 1647 let result; 1648 1649 try { 1650 result = await booknetic.ajaxAsync( { backend_action: 'get_data', frontend_action: 'confirm' }, 1651 booknetic.ajaxParameters( { 1652 current_step: 'confirm', 1653 previous_step: booking_panel_js.find(".booknetic_appointment_step_element.booknetic_active_step").data('step-id'), 1654 info: booking_panel_js.data( 'info' ) 1655 }), true 1656 ); 1657 } catch (e) { 1658 booknetic.refreshGoogleReCaptchaToken(); 1659 booknetic.stepManager.enableActions(); 1660 1661 if (!e) 1662 return; 1663 1664 booknetic.paymentId = typeof e['payment_id'] != 'undefined' ? e['payment_id'] : null; 1665 1666 // rows are inserted before the gateway call; release the slot they still hold 1667 booknetic.releaseUnpaidAppointment( null, false ); 1668 1669 if (typeof e['id'] == 'undefined') 1670 return; 1671 1672 booknetic.ajaxResultConfirmStep = e; 1673 booknetic.appointmentId = e['id']; 1674 return; 1675 } 1676 1677 booknetic.refreshGoogleReCaptchaToken(); 1678 1679 booknetic.ajaxResultConfirmStep = result; 1680 booknetic.appointmentId = result['id']; 1681 booknetic.paymentId = result['payment_id']; 1682 1683 if ( booknetic.paymentWindow !== null && result["expires_at"] !== undefined && result["expires_at"] < 24*60*60 ) 1684 { 1685 let expiringWindow = booknetic.paymentWindow; 1686 1687 // a retry opens a new window; only close the one this confirm belongs to 1688 setTimeout( function () 1689 { 1690 if ( expiringWindow === booknetic.paymentWindow && ! expiringWindow.closed ) 1691 expiringWindow.close(); 1692 }, result["expires_at"] * 1000 ); 1693 } 1694 1695 if( booknetic.getSelected.paymentMethod() === 'local' ) 1696 { 1697 booknetic.paymentFinished( true ); 1698 booknetic.showFinishStep(); 1699 } else if ( ! booknetic.paymentStatus ) { 1700 // inline-complete methods (e.g. gift card) already finished during confirm; don't poll and re-fire completion 1701 booknetic.startPolling(result.id) 1702 } 1703 1704 booking_panel_js.find('#booknetic_add_to_google_calendar_btn').data('url', result['google_calendar_url'] ); 1705 booking_panel_js.find('#booknetic_add_to_icalendar_btn').attr('href', encodeURI( result['icalendar_url'] ) ); 1706 }, 1707 1708 resetPaymentState: function () 1709 { 1710 booknetic.paymentWindow = null; 1711 booknetic.paymentStatus = null; 1712 booknetic.appointmentId = null; 1713 booknetic.ajaxResultConfirmStep = null; 1714 booknetic.paymentId = null; 1715 }, 1716 1717 releaseUnpaidAppointment: function ( onDone, loading ) 1718 { 1719 onDone = typeof onDone === 'function' ? onDone : function () {}; 1720 1721 if( ! booknetic.paymentId ) 1722 { 1723 onDone(); 1724 return; 1725 } 1726 1727 booknetic.ajax( 1728 'delete_unpaid_appointment', 1729 booknetic.ajaxParameters({ payment_id: booknetic.paymentId }), 1730 function () 1731 { 1732 booknetic.paymentId = null; 1733 onDone(); 1734 }, 1735 loading !== false, 1736 // keep the id and leave the error screen up: those rows still hold the 1737 // timeslot, and on a default install nothing else ever releases them 1738 function () {} 1739 ); 1740 }, 1741 1742 waitPaymentFinish: function() 1743 { 1744 if( booknetic.paymentWindow.closed ) 1745 { 1746 if ( booknetic.paymentStatusListener ) 1747 clearInterval( booknetic.paymentStatusListener ); 1748 1749 booknetic.loading(0); 1750 1751 booknetic.showFinishStep(); 1752 1753 return; 1754 } 1755 1756 setTimeout( booknetic.waitPaymentFinish, 1000 ); 1757 }, 1758 1759 paymentFinished: function ( status ) 1760 { 1761 booknetic.paymentStatus = status; 1762 booking_panel_js.find(".booknetic_appointment_finished_code").text( booknetic.zeroPad( booknetic.appointmentId, 4 ) ); 1763 1764 if( booknetic.paymentWindow && !booknetic.paymentWindow.closed ) 1765 { 1766 if ( booknetic.paymentStatusListener ) 1767 clearInterval( booknetic.paymentStatusListener ); 1768 1769 booknetic.paymentWindow.close(); 1770 } 1771 1772 bookneticHooks.doAction( 'payment_completed_deprecated', booknetic ); 1773 booknetic.stopPolling() 1774 }, 1775 1776 showFinishStep: function () 1777 { 1778 if ( BookneticData.settings.redirect_users_on_confirm === true && booknetic.paymentStatus === true ) 1779 { 1780 window.location.href = BookneticData.settings.redirect_users_on_confirm_url; 1781 return; 1782 } 1783 1784 if( booknetic.paymentStatus === true ) 1785 {
1786 booking_panel_js.find('.booknetic_appointment_container').fadeOut(95); 1787 1788 if ( booking_panel_js.find('.booknetic_appointment_steps').css( 'display' ) === 'none' ) 1789 { 1790 booking_panel_js.find('.booknetic_appointment_finished').fadeIn(100).css('display', 'flex'); 1791 } else { 1792 booking_panel_js.find('.booknetic_appointment_steps').fadeOut(100, function () 1793 { 1794 booking_panel_js.find('.booknetic_appointment_finished').fadeIn(100).css('display', 'flex'); 1795 }); 1796 } 1797 1798 bookneticHooks.doAction( 'booking_finished_successfully', booknetic ); 1799 } 1800 else 1801 { 1802 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="confirm_details"]').fadeOut( 150, function() 1803 { 1804 booking_panel_js.find('.booknetic_appointment_container_body > .booknetic_appointment_finished_with_error').removeClass('booknetic_hidden').hide().fadeIn( 150 ); 1805 }); 1806 1807 booking_panel_js.find('.booknetic_next_step.booknetic_confirm_booking_btn').fadeOut( 150, function() 1808 { 1809 booking_panel_js.find('.booknetic_try_again_btn').removeClass('booknetic_hidden').hide().fadeIn( 150 ); 1810 }); 1811 1812 booking_panel_js.find('.booknetic_appointment_container_header_cart').fadeOut( 150 ); 1813 1814 booking_panel_js.find('.booknetic_prev_step').css('opacity', '0').attr('disabled', true); 1815 1816 bookneticHooks.doAction( 'payment_error', booknetic ); 1817 } 1818 1819 if ( booknetic.isMobileView() ) 1820 { 1821 $('html,body').animate({scrollTop: parseInt(booking_panel_js.offset().top) - 100}, 1000); 1822 } 1823 }, 1824 1825 fadeInAnimate: function(el, fn) 1826 { 1827 let sec = 150; 1828 let delay = 50; 1829 let count = booking_panel_js.find(el).length; 1830 1831 if( booking_panel_js.find(el).length === 0 ) 1832 fn(); 1833 1834 booking_panel_js.find(el).hide().each(function (i) 1835 { 1836 (function( i, t, isLast, fn ) 1837 { 1838 setTimeout( function () 1839 { 1840 t.fadeIn( (i > 6 ? 6 : i) * sec, ( isLast ? fn : null )); 1841 }, (i > 6 ? 6 : i) * delay ); 1842 })( i, $(this), (count-1===i), fn ); 1843 }); 1844 }, 1845 1846 fadeOutAnimate: function(el, sec, delay) 1847 { 1848 sec = sec > 0 ? sec : 150; 1849 delay = delay > 0 ? delay : 50; 1850 1851 booking_panel_js.find(el).each(function (i) 1852 { 1853 (function( i, t ) 1854 { 1855 setTimeout( function () 1856 { 1857 t.fadeOut( (i > 6 ? 6 : i) * sec ); 1858 }, (i > 6 ? 6 : i) * delay ); 1859 })( i, $(this) ); 1860 }); 1861 }, 1862 1863 1864 _bookneticScroll: false, 1865 handleScroll: function () 1866 { 1867 if( !booknetic._bookneticScroll && !booknetic.isMobileView() ) 1868 { 1869 booking_panel_js.find(".booknetic_appointment_container_body").addClass('nice-scrollbar-primary'); 1870 1871 booknetic._bookneticScroll = true; 1872 1873 return; 1874 } 1875 1876 if( booknetic.isMobileView() && booknetic._bookneticScroll ) 1877 { 1878 booknetic._bookneticScroll = false; 1879 1880 booking_panel_js.find(".booknetic_appointment_container_body").removeClass('nice-scrollbar-primary'); 1881 1882 if ( $( '#country-listbox' ).length ) 1883 { 1884 $( '#country-listbox' ).removeClass('nice-scrollbar-primary'); 1885 } 1886 } 1887 }, 1888 1889 getDateWithUTC: function ( date )//if the client timezone is negative 1890 { 1891 date = new Date( date ); 1892 let offset = date.getTimezoneOffset(); 1893 1894 if ( offset > 0 ) // if offset a positive number, client's timezone is negative 1895 date.setTime( date.getTime() + ( offset * 60 * 1000 ) ); //get UTC time 1896 1897 return date; 1898 }, 1899 1900 initDatepicker: function ( el ) 1901 { 1902 // Map our display tokens (yyyy/mm/dd) to flatpickr's (Y/m/d). 1903 var fpFormat = booknetic.datePickerFormat() 1904 .replace('yyyy', 'Y') 1905 .replace('mm', 'm') 1906 .replace('dd', 'd'); 1907 1908 var minDate; 1909 if ( el[0].getAttribute('data-apply-min') && el[0].value ) 1910 { 1911 minDate = booknetic.convertDate( el[0].value, booknetic.datePickerFormat(), 'Y-m-d' ); 1912 } 1913 1914 flatpickr( el[0], { 1915 dateFormat: fpFormat, 1916 allowInput: true, 1917 locale: { 1918 firstDayOfWeek: BookneticData.week_starts_on == 'sunday' ? 0 : 1, 1919 weekdays: { 1920 shorthand: [__('Sun'), __('Mon'), __('Tue'), __('Wed'), __('Thu'), __('Fri'), __('Sat')], 1921 longhand: [__('Sunday'), __('Monday'), __('Tuesday'), __('Wednesday'), __('Thursday'), __('Friday'), __('Saturday')] 1922 }, 1923 months: { 1924 shorthand: [__('Jan'), __('Feb'), __('Mar'), __('Apr'), __('May'), __('Jun'), __('Jul'), __('Aug'), __('Sep'), __('Oct'), __('Nov'), __('Dec')], 1925 longhand: [__('January'), __('February'), __('March'), __('April'), __('May'), __('June'), __('July'), __('August'), __('September'), __('October'), __('November'), __('December')] 1926 } 1927 }, 1928 minDate: minDate, 1929 onChange: function ( selectedDates, dateStr, instance ) 1930 { 1931 $(instance.input).trigger('change'); 1932 } 1933 }); 1934 }, 1935 1936 refreshGoogleReCaptchaToken: function () 1937 { 1938 if( 'google_recaptcha_site_key' in BookneticData ) 1939 { 1940 grecaptcha.execute( BookneticData['google_recaptcha_site_key'], { action: booknetic.google_recaptcha_action }).then(function (token) 1941 { 1942 booknetic.google_recaptcha_token = token; 1943 }); 1944 } 1945 }, 1946 1947 isMobileView: function () 1948 { 1949 return window.matchMedia('(max-width: 1000px)').matches; 1950 }, 1951 1952 stepManager: { 1953 1954 stepValidation: function ( step ) 1955 { 1956 let result = bookneticHooks.doFilter( `step_validation_${step}`, { 1957 status: true, 1958 errorMsg: '' 1959 }, booknetic ); 1960 1961 if( result.status ) 1962 bookneticHooks.doAction(`step_end_${step}` , booknetic); 1963 1964 return result; 1965 }, 1966 1967 loadStep: function( step ) 1968 { 1969 if( ! bookneticHooks.doFilter( `load_step_${step}` , booknetic ) ) 1970 return false;
1971 1972 var current_step_el = booking_panel_js.find('.booknetic_appointment_step_element.booknetic_active_step'); 1973 var next_step_el = booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="'+step+'"]'); 1974 1975 while( next_step_el.hasClass('booknetic_menu_hidden') ) 1976 next_step_el = next_step_el.next(); 1977 1978 booknetic.stepManager.disableAction(); 1979 1980 var next_step_id = next_step_el.data('step-id'); 1981 var current_step_id = ''; 1982 1983 if( current_step_el.length > 0 ) 1984 { 1985 current_step_el.removeClass('booknetic_active_step'); 1986 current_step_id = current_step_el.data('step-id'); 1987 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="' + current_step_id + '"]').hide(); 1988 } 1989 1990 next_step_el.addClass('booknetic_active_step'); 1991 booking_panel_js.find(".booknetic_appointment_container_header_text").text( next_step_el.data('title') ); 1992 1993 booknetic.stepManager.updateGoNextBtn( next_step_el ); 1994 1995 bookneticHooks.doAction('before_step_loading', booknetic, next_step_id, current_step_id); 1996 1997 booknetic.stepManager.saveData(); 1998 1999 booknetic.stepManager.updateBookingPanelFooter(); 2000 2001 return true; 2002 }, 2003 2004 loadStandartSteps: function ( next_step_id, current_step_id ) 2005 { 2006 if( ! booknetic.stepManager.needToReloadCache( next_step_id ) ) 2007 { 2008 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="' + next_step_id + '"]').show(); 2009 2010 booknetic.fadeInAnimate('.booknetic_appointment_container_body [data-step-id="' + next_step_id + '"] .booknetic_fade', function() 2011 { 2012 booking_panel_js.find(".booknetic_appointment_container_body").scrollTop(0); 2013 booknetic.handleScroll(); 2014 booknetic.stepManager.enableActions(); 2015 }); 2016 bookneticHooks.doAction('loaded_cached_step', booknetic, next_step_id); 2017 } 2018 else 2019 { 2020 var next_step_el = booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="'+next_step_id+'"]'); 2021 var loader = booking_panel_js.find('.booknetic_preloader_' + next_step_el.data('loader') + '_box'); 2022 2023 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="' + next_step_id + '"]').empty(); 2024 2025 bookneticHooks.doAction('start_step_loading', booknetic, next_step_id, current_step_id); 2026 2027 loader.removeClass('booknetic_hidden').show(); 2028 let step 2029 const bookingPanelParams = { 2030 current_step: next_step_id, 2031 previous_step: booknetic.stepManager.getPrevStep().data( 'step-id' ), 2032 info: booking_panel_js.data( 'info' ), 2033 } 2034 if (next_step_id === "location" && (step = booking_panel_js.data('steps').find(s => s.id === "location"))?.options) { 2035 bookingPanelParams.location_filter = JSON.stringify(step.options); 2036 } 2037 2038 booknetic.ajax( 'get_data', booknetic.ajaxParameters({...bookingPanelParams}), function ( result ) 2039 { 2040 loader.hide(); 2041 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="' + next_step_id + '"]').show().html( booknetic.htmlspecialchars_decode( booknetic.sanitizeHTML(result['html']) ) ); 2042 2043 booknetic.fadeInAnimate('.booknetic_appointment_container_body [data-step-id="' + next_step_id + '"] .booknetic_fade', function () 2044 { 2045 booking_panel_js.find(".booknetic_appointment_container_body").scrollTop(0); 2046 booknetic.handleScroll(); 2047 }); 2048 2049 booknetic.stepManager.enableActions(); 2050 2051 bookneticHooks.doAction( 'loaded_step', booknetic, next_step_id, current_step_id, result ); 2052 bookneticHooks.doAction( `loaded_step_${next_step_id}`, booknetic, result ); 2053 2054 }, false , function ( result ) 2055 { 2056 loader.hide(); 2057 booknetic.stepManager.goBack(); 2058 bookneticHooks.doAction('step_loaded_with_error', booknetic, next_step_id, current_step_id, result); 2059 }); 2060 } 2061 }, 2062 2063 disableAction: function () 2064 { 2065 booking_panel_js.find(".booknetic_next_step, .booknetic_prev_step").attr('disabled', true); 2066 }, 2067 2068 enableActions: function () 2069 { 2070 booking_panel_js.find(".booknetic_next_step, .booknetic_prev_step").attr('disabled', false); 2071 }, 2072 2073 updateBookingPanelFooter: function () 2074 { 2075 booknetic.stepManager.updateGoBackButton(); 2076 }, 2077 2078 updateGoNextBtn: function ( step_el ) 2079 { 2080 var next_step_el = step_el.next('.booknetic_appointment_step_element'); 2081 2082 while( next_step_el.hasClass('booknetic_menu_hidden') ) 2083 next_step_el = next_step_el.next(); 2084 2085 if ( next_step_el.length === 0 ) { 2086 booking_panel_js.find('.booknetic_confirm_booking_btn').show(); 2087 booking_panel_js.find('.booknetic_next_step_btn').hide(); 2088 } 2089 else 2090 { 2091 booking_panel_js.find('.booknetic_confirm_booking_btn').hide(); 2092 booking_panel_js.find('.booknetic_next_step_btn').show(); 2093 } 2094 }, 2095 2096 updateGoBackButton: function () 2097 { 2098 const stepElement = '.booknetic_appointment_step_element'; 2099 const activeElement = '.booknetic_active_step'; 2100 const menuHidden = '.booknetic_menu_hidden'; 2101 2102 // hide the BACK button for the first step 2103 if ( booking_panel_js.find(stepElement ).not( menuHidden ).first().is( booking_panel_js.find( activeElement ) ) ) { 2104 booking_panel_js.find( '.booknetic_prev_step' ).css( 'opacity', 0 ).css( 'pointer-events', 'n
2104one' ); 2105 return; 2106 } 2107 2108 booking_panel_js.find( '.booknetic_prev_step' ).css( 'opacity', 1 ).css( 'pointer-events', 'auto' ); 2109 }, 2110 2111 saveData: ()=> 2112 { 2113 let obj = {}; 2114 2115 if(booknetic.cartArr[ booknetic.cartCurrentIndex ] !== undefined) 2116 { 2117 obj = booknetic.cartArr[ booknetic.cartCurrentIndex ]; 2118 } 2119 2120 obj['location'] = booknetic.getSelected.location(); 2121 obj['location_category'] = booknetic.getSelected.locationCategory(); 2122 obj['staff'] = booknetic.getSelected.staff(); 2123 obj['service_category'] = booknetic.getSelected.serviceCategory(); 2124 obj['service'] = booknetic.getSelected.service(); 2125 obj['service_extras'] = booknetic.getSelected.serviceExtras(); 2126 2127 obj['date'] = booknetic.getSelected.date(); 2128 obj['time'] = booknetic.getSelected.time(); 2129 obj['brought_people_count'] = booknetic.getSelected.brought_people_count(); 2130 2131 obj['recurring_start_date'] = booknetic.getSelected.recurringStartDate(); 2132 obj['recurring_end_date'] = booknetic.getSelected.recurringEndDate(); 2133 obj['recurring_times'] = booknetic.getSelected.recurringTimesArr(); 2134 obj['appointments'] = booknetic.getSelected.recurringTimesArrFinish(); 2135 2136 obj['customer_id'] = booknetic.getSelected.customerId(); 2137 obj['customer_data'] = booknetic.getSelected.formData()['data']; 2138 2139 booknetic.cartArr[ booknetic.cartCurrentIndex ] = bookneticHooks.doFilter('bkntc_cart' , obj , booknetic ); 2140 }, 2141 2142 goForward: function () 2143 { 2144 let current_step_el = booking_panel_js.find(".booknetic_appointment_step_element.booknetic_active_step"), 2145 current_step_id = current_step_el.data('step-id'), 2146 next_step_el = booknetic.stepManager.getNextStep(), 2147 next_step_id = next_step_el.data('step-id'), 2148 validate_step = booknetic.stepManager.stepValidation( current_step_id ); 2149 2150 if( validate_step.status == false ) 2151 { 2152 booknetic.toast( validate_step.errorMsg ); 2153 return; 2154 } 2155 2156 if( next_step_el.length == 0 ) 2157 { 2158 booknetic.stepManager.disableAction(); 2159 booknetic.confirmAppointment(); 2160 return; 2161 } 2162 2163 current_step_el.addClass('booknetic_selected_step'); 2164 2165 let oldData = JSON.stringify( booknetic.cartArr[ booknetic.cartCurrentIndex ] ); 2166 booknetic.stepManager.saveData(); 2167 let newData = JSON.stringify( booknetic.cartArr[ booknetic.cartCurrentIndex ] ); 2168 2169 if( oldData !== newData ) 2170 { 2171 let startToEmpty = next_step_el; 2172 while( startToEmpty.length > 0 ) 2173 { 2174 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="'+startToEmpty.data('step-id')+'"]').empty(); 2175 startToEmpty = startToEmpty.next(); 2176 } 2177 } 2178 2179 booknetic.toast( false ); 2180 2181 if( booknetic.stepManager.loadStep( next_step_id ) && booknetic.isMobileView() ) 2182 { 2183 const modalBody = booknetic.panel_js.closest('.bkntc_booking_modal').find('.content .body'); 2184 2185 if (modalBody.length > 0) { 2186 modalBody.animate({scrollTop: 0}, 500); 2187 return; 2188 } 2189 2190 $('html,body').animate({scrollTop: parseInt(booknetic.panel_js.offset().top) - 100}, 1000); 2191 } 2192 }, 2193 2194 goBack: function () 2195 { 2196 let current_step_el = booking_panel_js.find(".booknetic_appointment_step_element.booknetic_active_step"), 2197 prev_step_el = booknetic.stepManager.getPrevStep(); 2198 2199 if (current_step_el.data('step-id') === "date_time") { 2200 booknetic.current_month_is_empty = undefined 2201 } 2202 2203 current_step_el.removeClass('booknetic_selected_step').nextAll('.booknetic_appointment_step_element').removeClass('booknetic_selected_step'); 2204 2205 if( prev_step_el.length > 0 ) 2206 { 2207 if (prev_step_el.data( 'step-id' ) === 'service_extras' && BookneticData[ 'skip_extras_step_if_need' ] === 'on' && prev_step_el.css('display') === 'none') 2208 { 2209 prev_step_el.css('display', 'block'); 2210 prev_step_el.removeClass('booknetic_selected_step'); 2211 2212 do{ 2213 prev_step_el = prev_step_el.prev(); 2214 } 2215 while(prev_step_el.hasClass('booknetic_menu_hidden')); 2216 } 2217 2218 current_step_el.removeClass('booknetic_active_step'); 2219 prev_step_el.addClass('booknetic_active_step'); 2220 2221 booknetic.stepManager.disableAction();
2222 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="' + current_step_el.data('step-id') + '"]').fadeOut(200, function() 2223 { 2224 booknetic.stepManager.enableActions(); 2225 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="' + prev_step_el.data('step-id') + '"]').fadeIn(200, function () 2226 { 2227 booknetic.handleScroll(); 2228 }); 2229 }); 2230 2231 booking_panel_js.find(".booknetic_appointment_container_header_text").text( prev_step_el.data('title') ); 2232 } 2233 2234 booking_panel_js.find('.booknetic_confirm_booking_btn').hide(); 2235 booking_panel_js.find('.booknetic_next_step_btn').show(); 2236 2237 booknetic.stepManager.updateBookingPanelFooter(); 2238 }, 2239 2240 getNextStep: function () 2241 { 2242 let current_step_el = booking_panel_js.find(".booknetic_appointment_step_element.booknetic_active_step"), 2243 next_step_el = current_step_el.next('.booknetic_appointment_step_element'); 2244 2245 while( next_step_el.hasClass('booknetic_menu_hidden') ) 2246 next_step_el = next_step_el.next(); 2247 2248 return next_step_el; 2249 }, 2250 2251 getPrevStep: function () 2252 { 2253 if( booknetic.cartPrevStep != undefined) 2254 { 2255 let x = booknetic.cartPrevStep; 2256 booknetic.cartPrevStep = undefined; 2257 return booking_panel_js.find(".booknetic_appointment_steps_body div[data-step-id=" + x + "]"); 2258 } 2259 let current_step_el = booking_panel_js.find(".booknetic_appointment_step_element.booknetic_active_step"), 2260 prev_step_el = current_step_el.prev('.booknetic_appointment_step_element'); 2261 2262 while( prev_step_el.hasClass('booknetic_menu_hidden') ) 2263 prev_step_el = prev_step_el.prev(); 2264 2265 return prev_step_el; 2266 }, 2267 2268 getCurrentStep: function () 2269 { 2270 return booking_panel_js.find(".booknetic_appointment_step_element.booknetic_active_step"); 2271 }, 2272 2273 refreshStepNumbers: function () 2274 { 2275 var index = 1; 2276 2277 booking_panel_js.find('.booknetic_appointment_steps_body > .booknetic_appointment_step_element').each(function() 2278 { 2279 if( $(this).css('display') != 'none' ) 2280 { 2281 $(this).find('.booknetic_badge').text( index ); 2282 index++; 2283 } 2284 }); 2285 }, 2286 2287 needToReloadCache: function( stepId ) 2288 { 2289 if( stepId == 'confirm_details' || stepId=='cart' ) 2290 return true; 2291 2292 if( booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="' + stepId + '"] > *').length > 0 ) 2293 { 2294 return false; 2295 } 2296 2297 return true; 2298 }, 2299 2300 }, 2301 2302 addGroupAppointmentsCounterForBookneticCalendarDays: function () 2303 { 2304 const dates = booknetic.calendarDateTimes['dates']; 2305 2306 for ( const date in dates ) 2307 { 2308 if ( dates[ date ].length !== 1 ) 2309 { 2310 continue; 2311 } 2312 2313 let max_capacity = dates[ date ][ 0 ].max_capacity; 2314 let weight = dates[ date ][ 0 ].weight; 2315 2316 if ( 2317 weight == 0 || 2318 'hide_available_slots' in booknetic.calendarDateTimes && 2319 booknetic.calendarDateTimes['hide_available_slots'] == 'on' 2320 ) 2321 { 2322 continue; 2323 } 2324 2325 booking_panel_js.find( `.booknetic_calendar_days[data-date=${date}] > div` ).append( `<div class="booknetic_time_group_num booknetic_date_group_num">${weight} / ${max_capacity}</div>` ); 2326 } 2327 }, 2328 2329 checkIfNoDatesAvailable: function ( backendResult ) 2330 { 2331 return backendResult[ 'data' ][ 'dates' ].length === 0 || Object.values( backendResult[ 'data' ][ 'dates' ] ).every( d => d.length === 0 ); 2332 }, 2333 2334 intervalId: 0, 2335 2336 startPolling: (appointmentId) => { 2337 // Clear any existing polling 2338 if (booknetic.intervalId) clearInterval(booknetic.intervalId); 2339 2340 const intervalId = setInterval(() => { 2341 booknetic.ajaxAsync('isPaymentDone', { id: appointmentId }, false) 2342 .then(result => { 2343 if (!result.isDone) return; 2344 clearInterval(intervalId); 2345 booknetic.paymentFinished(true); 2346 }) 2347 .catch(_ => clearInterval(intervalId)); 2348 }, 3000); 2349 2350 setTimeout(() => clearInterval(intervalId), 1000 * 60 * 10); 2351 2352 booknetic.intervalId = intervalId; 2353 }, 2354 2355 2356 stopPolling: () => { 2357 if (!booknetic.intervalId) return; 2358 2359 clearInterval(booknetic.intervalId); 2360 booknetic.intervalId = 0; 2361 }, 2362 }; 2363 2364 // steplerle bagli basic eventler 2365 booking_panel_js.on('click', '.booknetic_next_step', function() 2366 { 2367 booknetic.stepManager.goForward(); 2368 }).on('click', '.booknetic_prev_step', function() 2369 { 2370 booknetic.stepManager.goBack(); 2371 }); 2372 2373 booking_panel_js.on('click', '#booknetic_finish_btn', function () 2374 { 2375 let page = window.location; 2376 2377 // check if iframe 2378 if ( window.location !== window.parent.location ) 2379 { 2380 page = window.parent.location; 2381 } 2382 2383 if( ! $(this).data('redirect-url') ) 2384 { 2385 page.reload(); 2386 } 2387 else 2388 {
2389 page.href = $(this).data('redirect-url'); 2390 } 2391 }).on('click' ,'.bkntc_again_booking' , function () 2392 { 2393 let currentBody = booking_panel_js.find('.booknetic_need_copy'); 2394 booknetic.cartHTMLBody[booknetic.cartCurrentIndex] = currentBody.clone(true,true).get(0); 2395 2396 booking_panel_js.find('.booknetic_appointment_steps_body .booknetic_appointment_step_element.need_copy').each(function() 2397 { 2398 let id = $(this).attr('data-step-id'); 2399 if(booknetic.cartHTMLSideBar[booknetic.cartCurrentIndex] == undefined) 2400 { 2401 booknetic.cartHTMLSideBar[booknetic.cartCurrentIndex] = {}; 2402 } 2403 booknetic.cartHTMLSideBar[booknetic.cartCurrentIndex][id] = $(this).clone(true,true).get(0); 2404 }); 2405 2406 booknetic.cartCurrentIndex = booknetic.cartArr.length; 2407 booknetic.cartArr[booknetic.cartCurrentIndex] = {}; 2408 2409 booking_panel_js.find("#booknetic_start_new_booking_btn").trigger('click' , true); 2410 2411 }).on('click', '#booknetic_start_new_booking_btn', function ( e , param ) 2412 { 2413 if( param === undefined ) 2414 { 2415 booking_panel_js.find('.booknetic_appointment_container_header_cart').fadeOut(); 2416 2417 booknetic.cartHTMLBody = []; 2418 booknetic.cartHTMLSideBar = []; 2419 booknetic.cartArr = []; 2420 booknetic.cartCurrentIndex = 0; 2421 } 2422 booking_panel_js.find('.booknetic_appointment_finished').fadeOut(100, function() 2423 { 2424 const appointment_step_style = booking_panel_js.find('.booknetic_appointment_steps').attr( 'style' ); 2425 2426 // in case if booking_panel_js.find('.booknetic_appointment_steps').fadeOut() called 2427 if ( appointment_step_style && appointment_step_style.search( 'display: none;' ) !== -1 ) 2428 booking_panel_js.find('.booknetic_appointment_steps').fadeIn(100); 2429 2430 booking_panel_js.find('.booknetic_appointment_container').fadeIn(100); 2431 }); 2432 2433 booking_panel_js.find(".booknetic_selected_step").removeClass('booknetic_selected_step'); 2434 booking_panel_js.find(".booknetic_active_step").removeClass('booknetic_active_step'); 2435 2436 booknetic.current_month_is_empty = undefined 2437 booknetic.calendarDateTimes = {}; 2438 booknetic.time_show_format = 1; 2439 booknetic.calendarYear = null; 2440 booknetic.calendarMonth = null; 2441 booknetic.resetPaymentState(); 2442 2443 var start_step = booking_panel_js.find(".booknetic_appointment_step_element:not(.booknetic_menu_hidden):eq(0)"); 2444 start_step.addClass('booknetic_active_step'); 2445 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id] ').empty(); 2446 booknetic.stepManager.loadStep( start_step.data('step-id') ); 2447 2448 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id]').hide(); 2449 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="' + start_step.data('step-id') + '"]').show(); 2450 2451 booking_panel_js.find('.booknetic_card_selected').removeClass('booknetic_card_selected'); 2452 booking_panel_js.find('.booknetic_service_card_selected').removeClass('booknetic_service_card_selected'); 2453 booking_panel_js.find('.booknetic_service_card_selected').removeClass('booknetic_service_card_selected'); 2454 2455 booking_panel_js.find(".booknetic_calendar_selected_day").data('date', null); 2456 booking_panel_js.find(".booknetic_selected_time").data('time', null); 2457 2458 booknetic.handleScroll(); 2459 2460 }).on('click', '#booknetic_add_to_google_calendar_btn', function () 2461 { 2462 window.open( $(this).data('url') ); 2463 }).on('click', '.booknetic_try_again_btn', function () 2464 { 2465 booknetic.stopPolling(); 2466 2467 let restoreConfirmStep = function () 2468 { 2469 booknetic.resetPaymentState(); 2470 2471 booknetic.stepManager.enableActions(); 2472 2473 booking_panel_js.find('.booknetic_appointment_finished_with_error').fadeOut(150, function () 2474 { 2475 booking_panel_js.find('.booknetic_appointment_container_body [data-step-id="confirm_details"]').fadeIn(150, function () 2476 { 2477 booknetic.handleScroll(); 2478 }); 2479 }); 2480 2481 booking_panel_js.find('.booknetic_try_again_btn').fadeOut(150, function () 2482 { 2483 booking_panel_js.find('.booknetic_next_step.booknetic_confirm_booking_btn').fadeIn(150); 2484 booking_panel_js.find('.booknetic_prev_step').css('opacity', '1'); 2485 }); 2486 2487 if( ! booking_panel_js.find('.booknetic_appointment_step_element[data-step-id="cart"]').hasClass('booknetic_menu_hidden') ) 2488 {
2489 booking_panel_js.find('.booknetic_appointment_container_header_cart').fadeIn( 150 ); 2490 } 2491 }; 2492 2493 booknetic.releaseUnpaidAppointment( restoreConfirmStep ); 2494 }); 2495 2496 //doit staff stepi (locationdan ayirmag) 2497 2498 $( window ).resize(function () 2499 { 2500 booknetic.handleScroll(); 2501 }); 2502 2503 let bookneticStepsArray = booking_panel_js.data('steps'); 2504 for( let step of bookneticStepsArray ) 2505 { 2506 let classAttr = 'booknetic_appointment_step_element'; 2507 if( step.id === 'cart' || step.id === 'confirm_details' ) 2508 classAttr += ' need_copy'; 2509 2510 if( step.hidden ) 2511 classAttr += ' booknetic_menu_hidden'; 2512 2513 let attrs = step.attrs ?? ''; 2514 2515 attrs += ` data-step-id="${step.id}"`; 2516 attrs += ` data-loader="${step.loader}"`; 2517 attrs += ` data-title="${step.head_title}"`; 2518 2519 if( step.value != "" ) 2520 attrs += ` data-value="${step.value}"`; 2521 2522 booking_panel_js.find('.booknetic_appointment_steps_body').append(` 2523 <div class="${classAttr}" ${attrs}> 2524 <span class="booknetic_badge"></span> 2525 <span class="booknetic_step_title"> ${step.title}</span> 2526 </div> 2527 `); 2528 } 2529 2530 var first_step_id = booking_panel_js.find('.booknetic_appointment_steps_body > .booknetic_appointment_step_element:not(.booknetic_menu_hidden)').eq(0).data('step-id'); 2531 booknetic.ensureFreshNonce( function () 2532 { 2533 booknetic.stepManager.loadStep( first_step_id ); 2534 } ); 2535 2536 booknetic.handleScroll(); 2537 2538 booknetic.fadeInAnimate('.booknetic_appointment_step_element:not(.booknetic_menu_hidden)', function () 2539 { 2540 booknetic.stepManager.refreshStepNumbers(); 2541 }); 2542 2543 booking_panel_js.find(".booknetic_appointment_steps_footer").fadeIn(200); 2544 2545 if( 'google_recaptcha_site_key' in BookneticData ) 2546 { 2547 grecaptcha.ready(function () 2548 { 2549 booknetic.refreshGoogleReCaptchaToken(); 2550 }); 2551 } 2552 2553 bookneticHooks.doAction('booking_panel_loaded', booknetic); 2554 2555 return booknetic; 2556 }; 2557 2558 $(document).ready( function() 2559 { 2560 if ( $( 'html' ).attr( 'dir' ) === 'rtl' ) 2561 { 2562 $( 'body' ).addClass( 'rtl booknetic_rtl' ); 2563 } 2564 2565 /** 2566 * doit bu duzelmelidi. hem alqoritmde xeta var, meselen Cart stepi load olanda headerda icon show olur. geri qayidib nese deyishiklik eledik meselen Servisi deyishdik. 2567 * data deyishir deye cart iconu sifirlanmalidi. chunki butun stepleri tamamlamalidi, date&time sechmelidi, formu tezeden doldurmalidi, chunki service deyishende form inputlarda deyishe biler. 2568 * Amma bu stepleri doldurmadan hemen show olmush icona clickledikde birbasha tulluyur cart stepine. Ordanda next bassan error verir ki, meselen formu doldur ve ya date&time sech ve s. 2569 * Elave olarag $('.*') kimi select yolverilmezdir. booknetic.panel_js.find('.*') || booking_panel_js.find('.*') istifade olunmalidir. 2570 * Cunki 1 pagede eyni anda bir neche booking panel load ola biler. biri digerinin iconlariynan oynamasin deye bu metod hokmen her yerde istifade edilmelidi. 2571 */ 2572 $("body").click(function(e) 2573 { 2574 if( $(e.target).parent().hasClass('booknetic-cart-item-more') ) 2575 { 2576 let a = $(e.target).parents('.booknetic-cart-item-header').find('.booknetic-cart-item-btns').first(); 2577 let b = a.hasClass('show'); 2578 $(".booknetic-cart-item-btns").removeClass("show"); 2579 if(!b) 2580 { 2581 a.addClass('show') 2582 } 2583 else 2584 { 2585 a.removeClass('show'); 2586 } 2587 } 2588 else 2589 { 2590 2591 $(".booknetic-cart-item-btns").removeClass("show"); 2592 2593 } 2594 }); 2595 }); 2596 2597})(jQuery); 2598 2599// local payment gateway 2600(function($) 2601{ 2602 "use strict"; 2603 2604 $(document).ready(function() 2605 { 2606 let paymentData; 2607 2608 bookneticHooks.addAction( 'before_processing_payment', function ( payment_method, data ) 2609 { 2610 if( payment_method !== 'local' ) 2611 return; 2612 2613 paymentData = data; 2614 }); 2615 2616 bookneticHooks.addAction( 'after_processing_payment', function( payment_method, process_status, data ) 2617 { 2618 if( payment_method !== 'local' ) 2619 return; 2620 2621 if( ! process_status ) 2622 { 2623 return; 2624 } 2625 2626 bookneticHooks.doAction('payment_completed', true, paymentData); 2627 }); 2628 }); 2629 2630})(jQuery);
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.