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