PageSourceSearch

https://tirtaayuspa.com.ng/lekki/wp-content/plugins/latepoint/public/javascripts/front.js?ver=5.1.93

js tirtaayuspa.com.ng collected 2026-10-04 18:54:18 UTC 157,204 bytes, 3,518 lines download raw bytes

1function latepoint_is_timeframe_in_periods(timeframe_start, timeframe_end, periods_arr) {
2  var is_inside = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : false;
3
4  for (var i = 0; i < periods_arr.length; i++) {
5
6    var period_start = 0;
7    var period_end = 0;
8    var buffer_before = 0;
9    var buffer_after = 0;
10
11    var period_info = periods_arr[i].split(':');
12    if (period_info.length == 2) {
13      period_start = period_info[0];
14      period_end = period_info[1];
15    } else {
16      buffer_before = period_info[2];
17      buffer_after = period_info[3];
18      period_start = parseFloat(period_info[0]) - parseFloat(buffer_before);
19      period_end = parseFloat(period_info[1]) + parseFloat(buffer_after);
20    }
21    if (is_inside) {
22      if (latepoint_is_period_inside_another(timeframe_start, timeframe_end, period_start, period_end)) {
23        return true;
24      }
25    } else {
26      if (latepoint_is_period_overlapping(timeframe_start, timeframe_end, period_start, period_end)) {
27        return true;
28      }
29    }
30  }
31  return false;
32}
33
34function latepoint_is_period_overlapping(period_one_start, period_one_end, period_two_start, period_two_end) {
35  // https://stackoverflow.com/questions/325933/determine-whether-two-date-ranges-overlap/
36  return period_one_start < period_two_end && period_two_start < period_one_end;
37}
38
39function latepoint_is_period_inside_another(period_one_start, period_one_end, period_two_start, period_two_end) {
40  return period_one_start >= period_two_start && period_one_end <= period_two_end;
41}
42
43
44// Converts time in minutes to hours if possible, if minutes also exists - shows minutes too
45function latepoint_minutes_to_hours_preferably(time) {
46  var army_clock = latepoint_is_army_clock();
47
48  var hours = Math.floor(time / 60);
49  if (!army_clock && hours > 12) hours = hours - 12;
50
51  var minutes = time % 60;
52  if (minutes > 0) hours = hours + ':' + minutes;
53  return hours;
54}
55
56
57function latepoint_minutes_to_hours(time) {
58  var army_clock = latepoint_is_army_clock();
59
60  var hours = Math.floor(time / 60);
61  if (!army_clock && hours > 12) hours = hours - 12;
62  return hours;
63}
64
65
66function latepoint_am_or_pm(minutes) {
67  if (latepoint_is_army_clock()) return '';
68  return (minutes < 720 || minutes == 1440) ? 'am' : 'pm';
69}
70
71function latepoint_hours_and_minutes_to_minutes(hours_and_minutes, ampm) {
72  var hours_and_minutes_arr = hours_and_minutes.split(':');
73  var hours = hours_and_minutes_arr[0];
74  var minutes = hours_and_minutes_arr[1];
75  if (ampm == "pm" && hours < 12) hours = parseInt(hours) + 12;
76  if (ampm == "am" && hours == 12) hours = 0;
77  minutes = parseInt(minutes) + (hours * 60);
78  return minutes;
79}
80
81function latepoint_get_time_system() {
82  return latepoint_helper.time_system;
83}
84
85function latepoint_is_army_clock() {
86  return (latepoint_get_time_system() == '24');
87}
88
89function latepoint_minutes_to_hours_and_minutes(minutes) {
90  var army_clock = latepoint_is_army_clock();
91  var format = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : '%02d:%02d';
92
93  var hours = Math.floor(minutes / 60);
94  if (!army_clock && (hours > 12)) hours = hours - 12;
95  if (!army_clock && hours == 0) hours = 12;
96  minutes = minutes % 60;
97  // Check if sprintf is available (either native or from a library)
98  if (typeof sprintf === 'function') {
99    return sprintf(format, hours, minutes);
100  } else {
101    return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}`;
102  }
103}
104
105
106function latepoint_timestamped_ajaxurl(){
107    let url = latepoint_helper.ajaxurl;
108    let timestamp = Date.now();
109
110    // Check if the URL already has GET parameters
111    if (url.includes('?')) {
112        return `${url}&t=${timestamp}`;
113    } else {
114        return `${url}?t=${timestamp}`;
115    }
116}
117
118function latepoint_random_generator() {
119  var S4 = function () {
120    return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
121  };
122  return (S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4());
123}
124
125function latepoint_validate_form($form) {
126  let errors = [];
127  $form.find('select[data-os-validate], input[data-os-validate], textarea[data-os-validate]').each(function () {
128    let validations = jQuery(this).data('os-validate').split(' ');
129    let $input = jQuery(this);
130    let label = $input.closest('.os-form-group').find('label').text();
131    let field_has_errors = false;
132    if (validations) {
133      for (let i = 0; i < validations.length; i++) {
134        switch (validations[i]) {
135          case 'presence':
136            if($input.is(':checkbox')){
137              if (!$input.is(':checked')) {
138                errors.push({message: label + ' ' + latepoint_helper.msg_validation_presence_checkbox});
139                field_has_errors = true;
140              }
141            }else{
142              if (!$input.val()) {
143                errors.push({message: label + ' ' + latepoint_helper.msg_validation_presence});
144                field_has_errors = true;
145              }
146            }
147            break;
148          case 'phone':
149            if (!window.lp_intlTelInputGlobals.getInstance($input[0]).isValidNumber()) {
150              errors.push({message: label + ' ' + latepoint_helper.msg_validation_invalid});
151              field_has_errors = true;
152            }
153            break;
154        }
155      }
156    }
157    if (field_has_errors) {
158      $input.closest('.os-form-group').addClass('os-invalid');
159    } else {
160      $input.closest('.os-form-group').removeClass('os-invalid');
161    }
162  });
163  return errors;
164}
165
166function latepoint_create_form_data_from_non_form_element($elem) {
167  let formData = new FormData();
168  // create objecte from all input fields that are inside of the element
169  let fields = $elem.find('select, input, textarea').serializeArray();
170  if (fields) {
171    fields.forEach(field => formData.append(field.name, field.value));
172  }
173  return formData;
174}
175
176function latepoint_create_form_data($form, route_name = false, extra_params = false) {
177  let form_data = new FormData();
178  let params = new FormData($form[0]);
179
180  if (extra_params) {
181    Object.keys(extra_params).forEach(key => {
182      params.set(key, extra_params[key]);
183    });
184  }
185
186  // get values from phone number fields
187  if (('lp_intlTelInputGlobals' in window) && ('lp_intlTelInputUtils' in window)) {
188    $form.find('input.os-mask-phone').each(function () {
189      const phoneInputName = this.getAttribute('name');
190      const phoneInputValue = window.lp_intlTelInputGlobals.getInstance(this).getNumber(window.lp_intlTelInputUtils.numberFormat.E164);
191      // override value generated automatically by formdata with a formatted value of a phone field with country code
192      params.set(phoneInputName, phoneInputValue);
193    });
194  }
195
196  form_data.append('params', latepoint_formdata_to_url_encoded_string(params));
197  form_data.append('action', latepoint_helper.route_action);
198  form_data.append('route_name', route_name ? route_name : $form.data('route-name'));
199  form_data.append('layout', 'none');
200  form_data.append('return_format', 'json');
201
202  let file_data;
203  // put file data into main form_data object, since we can't send them in "params" string
204  $form.find('input[type="file"]').each(function () {
205    file_data = this.files; // get multiple files from input file
206    let file_name = this.getAttribute("name");
207    for (let i = 0; i < file_data.length; i++) {
208      form_data.append(file_name + '[]', file_data[i]);
209    }
210  });
211  return form_data;
212}
213
214function latepoint_mask_timefield($elem) {
215  if (jQuery().inputmask) {
216    $elem.inputmask({
217      'mask': '99:99',
218      'placeholder': 'HH:MM'
219    });
220  }
221}
222
223function latepoint_formdata_to_url_encoded_string(form_data) {
224  let filtered_form_data = new FormData();
225  // remove file fields from params, so we can serialize it into string,
226  // !important, this will not include file fields into the form_data, so you have to include them manually, see latepoint_create_form_data() that does it
227  // note: we don't use form_data.remove(key) on original object because we might want to preserve it
228  for (const [key, value] of form_data) {
229    if (value instanceof File) continue;
230    if (key.slice(-2) === '[]') {
231      // expecting array, append
232      filtered_form_data.append(key, value);
233    } else {
234      filtered_form_data.set(key, value);
235    }
236  }
237  return new URLSearchParams(filtered_form_data).toString();
238}
239
240function latepoint_mask_percent($elem) {
241  if (jQuery().inputmask) {
242    $elem.inputmask({
243      'alias': 'decimal',
244      'radixPoint': latepoint_helper.decimal_separator,
245      'digits': 4,
246      'digitsOptional': false,
247      'suffix': '%',
248      'placeholder': '0',
249      'rightAlign': false
250    });
251  }
252}
253
254function latepoint_mask_minutes($elem) {
255  if (jQuery().inputmask) {
256    $elem.inputmask({
257      'removeMaskOnSubmit': true,
258      'alias': 'numeric',
259      'digits': 0,
260      'suffix': latepoint_helper.msg_minutes_suffix,
261      'placeholder': '0',
262      'rightAlign': false
263    });
264  }
265}
266
267
268function latepoint_mask_money($elem) {
269  if (jQuery().inputmask) {
270    $elem.inputmask({
271      'alias': 'currency',
272      'groupSeparator': latepoint_helper.thousand_separator,
273      'radixPoint': latepoint_helper.decimal_separator,
274      'digits': latepoint_helper.number_of_decimals,
275      'digitsOptional': false,
276      'prefix': latepoint_helper.currency_symbol_before ? latepoint_helper.currency_symbol_before + ' ' : '',
277      'suffix': latepoint_helper.currency_symbol_after ? ' ' + latepoint_helper.currency_symbol_after : '',
278      'placeholder': '0',
279      'rightAlign': false
280    });
281  }
282}
283
284function latepoint_mask_date($elem) {
285  if (jQuery().inputmask) {
286    $elem.inputmask({
287      'alias': 'datetime',
288      'inputFormat': latepoint_helper.date_format_for_js
289    });
290  }
291}
292
293function latepoint_init_phone_masking_from_placeholder($input) {
294  if (!latepoint_helper.mask_phone_number_fields) return;
295  let format = $input.attr('placeholder');
296  if (format && jQuery().inputmask) {
297    $input.inputmask(format.replace(/[0-9]/g, 9));
298  }
299}
300
301function latepoint_mask_phone($elem) {
302  let jsElem = $elem[0];
303
304  // First priority is to prevent duplicates (common in non-document.body contexts)
305  if (jsElem && !window.lp_intlTelInputGlobals.getInstance(jsElem)) {
306    let dropdownContainer = document.body;
307
308    let onlyCountries = JSON.parse(latepoint_helper.included_phone_countries);
309    // Remedy a quirk with json_encode(EMPTY_ARRAY)
310    if (onlyCountries.length === 1 && onlyCountries[0] === "") {
311      onlyCountries = [];
312    }
313    const preferredCountries = onlyCountries.length ? [] : window.lp_intlTelInputGlobals.defaults.preferredCountries;
314
315    // remove country name in english and only use names in country language
316    var countryData = window.lp_intlTelInputGlobals.getCountryData();
317
318    for (var i = 0; i < countryData.length; i++) {
319      var country = countryData[i];
320      country.name = country.name.replace(/ *\([^)]*\) */g, "");
321    }
322
323    let defaultCountryCode = latepoint_helper.default_phone_country;
324    if (onlyCountries.length && !onlyCountries.includes(defaultCountryCode)) {
325      defaultCountryCode = onlyCountries[0];
326    }
327
328
329    let iti = window.lp_intlTelInput(jsElem, {
330      dropdownContainer: dropdownContainer,
331      formatOnDisplay: true,
332      nationalMode: true,
333      autoPlaceholder: 'aggressive',
334      initialCountry: defaultCountryCode,
335      geoIpLookup: function (callback) {
336        const cookieName = 'latepoint_phone_country';
337
338        if (latepoint_has_cookie(cookieName)) {
339          callback(latepoint_get_cookie(cookieName));
340        } else {
341          jQuery.get('https://ipinfo.io', function () {
342          }, 'jsonp').always(function (response) {
343            // Sensible default
344            let countryCode = defaultCountryCode;
345
346            if (response && response.country) {
347              countryCode = response.country.toLowerCase();
348              latepoint_set_cookie(cookieName, countryCode);
349            }
350            callback(countryCode);
351          })
352        }
353      },
354      allowDropdown: onlyCountries.length != 1,
355      onlyCountries: onlyCountries,
356      preferredCountries: preferredCountries,
357      separateDialCode: latepoint_helper.is_enabled_show_dial_code_with_flag
358    });
359
360    iti.promise.then(function () {
361      latepoint_init_phone_masking_from_placeholder($elem);
362    });
363
364
365    $elem.on("countrychange", function (event) {
366      latepoint_init_phone_masking_from_placeholder(jQuery(this));
367    });
368  }
369}
370
371function latepoint_show_booking_end_time() {
372  return (latepoint_helper.show_booking_end_time == 'yes');
373}
374
375function latepoint_set_cookie(name, value, days) {
376  let date = new Date;
377  date.setTime(date.getTime() + 24 * 60 * 60 * 1000 * days);
378  document.cookie = name + "=" + value + ";path=/;expires=" + date.toGMTString();
379}
380
381function latepoint_get_cookie(name) {
382  let cookie = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
383  return cookie ? cookie[2] : null;
384}
385
386function latepoint_has_cookie(name) {
387  return latepoint_get_cookie(name) !== null;
388}
389
390function latepoint_delete_cookie(name) {
391  latepoint_set_cookie(name, '', -1);
392}
393
394function latepoint_add_notification(message, message_type = 'success'){
395	var wrapper = jQuery('body').find('.os-notifications');
396	if(!wrapper.length){
397		jQuery('body').append('<div class="os-notifications"></div>');
398		wrapper = jQuery('body').find('.os-notifications');
399	}
400	if(wrapper.find('.item').length > 0) wrapper.find('.item:first-child').remove();
401	wrapper.append('<div class="item item-type-'+ message_type +'">' + message + '<span class="os-notification-close"><i class="latepoint-icon latepoint-icon-x"></i></span></div>');
402}
403
404function latepoint_add_lightbox_notification(message, message_type = 'success'){
405	var wrapper = jQuery('.latepoint-lightbox-content').find('.os-notifications');
406	if(!wrapper.length){
407		jQuery('.latepoint-lightbox-content').prepend('<div class="os-notifications"></div>');
408		wrapper = jQuery('.latepoint-lightbox-content').find('.os-notifications');
409	}
410	if(wrapper.find('.item').length > 0) wrapper.find('.item:first-child').remove();
411	wrapper.append('<div class="item item-type-'+ message_type +'">' + message + '<span class="os-notification-close"><i class="latepoint-icon latepoint-icon-x"></i></span></div>');
412}
413
414function latepoint_generate_form_message_html(messages, status){
415  var message_html = '<div class="os-form-message-w status-' + status + '"><ul>';
416  if(Array.isArray(messages)){
417    messages.forEach(function(message){
418      message_html+= '<li>' + message + '</li>';
419    });
420  }else{
421    message_html+= '<li>' + messages + '</li>';
422  }
423  message_html+= '</ul></div>';
424  return message_html;
425}
426
427function latepoint_display_in_side_sub_panel(html){
428  if(!jQuery('.latepoint-side-panel-w').length) latepoint_show_data_in_side_panel('');
429  jQuery('.latepoint-side-panel-w .latepoint-side-panels .side-sub-panel-wrapper').remove();
430  jQuery('.latepoint-side-panel-w .latepoint-side-panels').append(html);
431}
432
433function latepoint_clear_form_messages($form){
434  $form.find('.os-form-message-w').remove();
435}
436
437function latepoint_show_data_in_side_panel(message, extra_classes = '', close_btn = true){
438  jQuery('.latepoint-side-panel-w').remove();
439  jQuery('body').append('<div class="latepoint-side-panel-w ' + extra_classes + ' os-loading"><div class="latepoint-side-panel-shadow"></div><div class="latepoint-side-panels"><div class="latepoint-side-panel-i"></div></div></div>');
440  jQuery('.latepoint-side-panel-i').html(message);
441  if(close_btn){
442    jQuery('.latepoint-side-panel-i').find('.os-form-header .latepoint-side-panel-close').remove();
443    jQuery('.latepoint-side-panel-i').find('.os-form-header').append('<a href="#" class="latepoint-side-panel-close latepoint-side-panel-close-trigger"><i class="latepoint-icon latepoint-icon-x"></i></a>');
444  }
445  setTimeout(function(){
446    jQuery('.latepoint-side-panel-w').removeClass('os-loading');
447  }, 100);
448}
449
450function latepoint_show_data_in_lightbox(message, extra_classes = '', close_btn = true, tag = 'div', inner_extra_classes = '', inner_tag = 'div'){
451  jQuery('.latepoint-lightbox-w').remove();
452  let lightbox_css_classes = 'latepoint-lightbox-w latepoint-w latepoint-border-radius-' + latepoint_helper.style_border_radius+ ' ';
453  if(extra_classes) lightbox_css_classes+= extra_classes;
454  let lightbox_css_inner_classes = 'latepoint-lightbox-i ';
455  if(inner_extra_classes) lightbox_css_inner_classes += inner_extra_classes;
456
457  let close_btn_html = close_btn ? '<a href="#" class="latepoint-lightbox-close" tabindex="0"><i class="latepoint-icon latepoint-icon-x"></i></a>' : '';
458  jQuery('body').append('<'+tag+' class="'+ lightbox_css_classes +'"><'+inner_tag+' class="'+ lightbox_css_inner_classes +'">' + message + close_btn_html + '</'+inner_tag+'><div class="latepoint-lightbox-shadow"></div></'+tag+'>');
459
460  jQuery('body').addClass('latepoint-lightbox-active');
461}
462
463
464
465// DOCUMENT READY
466jQuery(function( $ ) {
467
468  if($('.latepoint').find('[data-os-action-onload]').length){
469    $('.latepoint').find('[data-os-action-onload]').each(function(){
470      var $this = jQuery(this);
471      $this.addClass('os-loading');
472      var params = $this.data('os-params');
473      var return_format = $this.data('os-return-format') ? $this.data('os-return-format') : 'json'
474      var data = { action: 'latepoint_route_call', route_name: $this.data('os-action-onload'), params: params, return_format: return_format }
475      jQuery.ajax({
476        type : "post",
477        dataType : "json",
478        url : latepoint_timestamped_ajaxurl(),
479        data : data,
480        success: function(response) {
481          $this.removeClass('os-loading');
482          if (response.status === "success") {
483            if($this.data('os-output-target') == 'self'){
484              $this.html(response.message);
485            }
486          }
487        }
488      });
489    });
490  }
491
492  /*
493    Ajax buttons action
494  */
495  $('.latepoint').on('click', 'button[data-os-action], a[data-os-action], div[data-os-action], span[data-os-action], tr[data-os-action]', function(e){
496    var $this = jQuery(this);
497    if($this.data('os-prompt') && !confirm($this.data('os-prompt'))) return false;
498    var params = $this.data('os-params');
499    if($this.data('os-source-of-params')){
500      var form_data = latepoint_create_form_data_from_non_form_element($($this.data('os-source-of-params')));
501      params = latepoint_formdata_to_url_encoded_string(form_data);
502    }
503    var return_format = $this.data('os-return-format') ? $this.data('os-return-format') : 'json'
504    var data = { action: 'latepoint_route_call', route_name: $this.data('os-action'), params: params, return_format: return_format }
505    $this.addClass('os-loading');
506    if($this.data('os-output-target') == 'side-panel'){
507      $('.latepoint-side-panel-w').remove();
508      let css_classes = $this.data('os-lightbox-classes') ? $this.data('os-lightbox-classes') : '';
509      $('body').append('<div class="latepoint-side-panel-w ' + css_classes + ' os-loading"><div class="latepoint-side-panel-shadow"></div><div class="latepoint-side-panels"><div class="latepoint-side-panel-i"></div></div></div>');
510    }else if($this.data('os-output-target') == 'full-panel'){
511      $('.latepoint-full-panel-w').remove();
512      $('body').append('<div class="latepoint-full-panel-w os-loading"></div>');
513    }
514    $.ajax({
515      type : "post",
516      dataType : "json",
517      url : latepoint_timestamped_ajaxurl(),
518      data : data,
519      success: function(response){
520        if(response.status === "success"){
521          if($this.data('os-output-target') == 'lightbox'){
522            latepoint_show_data_in_lightbox(response.message, $this.data('os-lightbox-classes'), ($this.data('os-lightbox-no-close-button') !== 'yes'), $this.data('os-lightbox-tag'), $this.data('os-lightbox-inner-classes'), $this.data('os-lightbox-inner-tag'));
523          }else if($this.data('os-output-target') == 'side-panel'){
524            $('.latepoint-side-panel-i').html(response.message);
525            jQuery('.latepoint-side-panel-i').find('.os-form-header .latepoint-side-panel-close').remove();
526            jQuery('.latepoint-side-panel-i').find('.os-form-header').append('<a href="#" class="latepoint-side-panel-close latepoint-side-panel-close-trigger"><i class="latepoint-icon latepoint-icon-x"></i></a>');
527            setTimeout(function(){
528              $('.latepoint-side-panel-w').removeClass('os-loading');
529            }, 100);
530          }else if($this.data('os-output-target') == 'full-panel'){
531            $('.latepoint-full-panel-w').html(response.message);
532            setTimeout(function(){
533              $('.latepoint-full-panel-w').removeClass('os-loading');
534            }, 100);
535          }else if($this.data('os-success-action') == 'reload'){
536            latepoint_add_notification(response.message);
537            location.reload();
538            return;
539          }else if($this.data('os-success-action') == 'redirect'){
540            if($this.data('os-redirect-to')){
541              latepoint_add_notification(response.message);
542              window.location.replace($this.data('os-redirect-to'));
543            }else{
544              window.location.replace(response.message); 
545            }
546            return;
547          }else if($this.data('os-output-target') && $($this.data('os-output-target')).length){
548            if($this.data('os-output-target-do') == 'append') {
549              $($this.data('os-output-target')).append(response.message);
550            }else if($this.data('os-output-target-do') == 'prepend'){
551              $($this.data('os-output-target')).prepend(response.message);
552            }else{
553              $($this.data('os-output-target')).html(response.message);
554            }
555          }else{
556            switch($this.data('os-before-after')){
557              case 'before':
558                $this.before(response.message);
559                break;
560              case 'after':
561                $this.after(response.message);
562                break;
563              case 'replace':
564                $this.replaceWith(response.message);
565                break;
566              case 'none':
567                break;
568              default:
569                latepoint_add_notification(response.message);
570            }
571          }
572          if($this.data('os-after-call')){
573            var func_name = $this.data('os-after-call');
574            var callback = false;
575            if(func_name.includes('.')){
576              var func_arr = func_name.split('.');
577              if(typeof window[func_arr[0]][func_arr[1]] !== 'function'){
578                console.log(func_name + ' is undefined');
579              }
580              if($this.data('os-pass-this') && $this.data('os-pass-response')){
581                window[func_arr[0]][func_arr[1]]($this, response);
582              }else if($this.data('os-pass-this')){
583                window[func_arr[0]][func_arr[1]]($this);
584              }else if($this.data('os-pass-response')){
585                window[func_arr[0]][func_arr[1]](response);
586              }else{
587                window[func_arr[0]][func_arr[1]]();
588              }
589            }else{
590              if(typeof window[func_name] !== 'function'){
591                console.log(func_name + ' is undefined');
592              }
593              if($this.data('os-pass-this') && $this.data('os-pass-response')){
594                window[func_name]($this, response);
595              }else if($this.data('os-pass-this')){
596                window[func_name]($this);
597              }else if($this.data('os-pass-response')){
598                window[func_name](response);
599              }else{
600                window[func_name]();
601              }
602            }
603          }
604          $this.removeClass('os-loading');
605        }else{
606          $this.removeClass('os-loading');
607          if($this.data('os-output-target') && $($this.data('os-output-target')).length){
608            $($this.data('os-output-target')).prepend(latepoint_generate_form_message_html(response.message, 'error'));
609          }else{
610            alert(response.message);
611          }
612          if($this.data('os-after-call-error')){
613            var func_name = $this.data('os-after-call-error');
614            var callback = false;
615            if(func_name.includes('.')){
616              var func_arr = func_name.split('.');
617              if(typeof window[func_arr[0]][func_arr[1]] !== 'function'){
618                console.log(func_name + ' is undefined');
619              }
620              if($this.data('os-pass-this') && $this.data('os-pass-response')){
621                window[func_arr[0]][func_arr[1]]($this, response);
622              }else if($this.data('os-pass-this')){
623                window[func_arr[0]][func_arr[1]]($this);
624              }else if($this.data('os-pass-response')){
625                window[func_arr[0]][func_arr[1]](response);
626              }else{
627                window[func_arr[0]][func_arr[1]]();
628              }
629            }else{
630              if(typeof window[func_name] !== 'function'){
631                console.log(func_name + ' is undefined');
632              }
633              if($this.data('os-pass-this') && $this.data('os-pass-response')){
634                window[func_name]($this, response);
635              }else if($this.data('os-pass-this')){
636                window[func_name]($this);
637              }else if($this.data('os-pass-response')){
638                window[func_name](response);
639              }else{
640                window[func_name]();
641              }
642            }
643          }
644        }
645      }
646    });
647    return false;
648  });
649
650
651  $('.latepoint').on('click', 'form[data-os-action] button[type="submit"]', function(e){
652    $(this).addClass('os-loading');
653  });
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671  /* 
672    Form ajax submit action
673  */
674  $('.latepoint').on('submit', 'form[data-os-action]', function(e){
675    e.preventDefault(); // prevent native submit
676      var $form = $(this);
677      var form_data = new FormData($form[0]);
678
679    if (('lp_intlTelInputGlobals' in window) && ('lp_intlTelInputUtils' in window)) {
680      // Get e164 formatted number from phone fields when form is submitted
681      $form.find('input.os-mask-phone').each(function () {
682        let telInstance = window.lp_intlTelInputGlobals.getInstance(this);
683        if(telInstance){
684          const phoneInputName = this.getAttribute('name');
685          const phoneInputValue = window.lp_intlTelInputGlobals.getInstance(this).getNumber(window.lp_intlTelInputUtils.numberFormat.E164);
686          form_data.set(phoneInputName, phoneInputValue);
687        }
688      });
689    }
690
691    let data = latepoint_create_form_data($form, $(this).data('os-action'));
692
693    // var data = { action: 'latepoint_route_call', route_name: $(this).data('os-action'), params: latepoint_formdata_to_url_encoded_string(form_data), return_format: 'json' }
694    $form.find('button[type="submit"]').addClass('os-loading');
695    $.ajax({
696      type : "post",
697      dataType : "json",
698      processData: false,
699      contentType: false,
700      url : latepoint_timestamped_ajaxurl(),
701      data : data,
702      success: function(response){
703        $form.find('button[type="submit"].os-loading').removeClass('os-loading');
704        latepoint_clear_form_messages($form);
705        if(response.status === "success"){
706          if($form.data('os-success-action') == 'reload'){
707            latepoint_add_notification(response.message);
708            location.reload();
709            return;
710          }else if($form.data('os-success-action') == 'redirect'){
711            if($form.data('os-redirect-to')){
712              latepoint_add_notification(response.message);
713              window.location.replace($form.data('os-redirect-to'));
714            }else{
715              window.location.replace(response.message);
716            }
717            return;
718          }else if($form.data('os-output-target') && $($form.data('os-output-target')).length){
719            $($form.data('os-output-target')).html(response.message);
720          }else{
721            if(response.message == 'redirect'){
722              window.location.replace(response.url);
723            }else{
724              latepoint_add_notification(response.message);
725            }
726          }
727          if($form.data('os-record-id-holder') && response.record_id){
728            $form.find('[name="' + $form.data('os-record-id-holder') + '"]').val(response.record_id)
729          }
730          if($form.data('os-after-call')){
731
732            var func_name = $form.data('os-after-call');
733            var callback = false;
734            if(func_name.includes('.')){
735              var func_arr = func_name.split('.');
736              if(typeof window[func_arr[0]][func_arr[1]] !== 'function'){
737                console.log(func_name + ' is undefined');
738              }
739              if($form.data('os-pass-this') && $form.data('os-pass-response')){
740                window[func_arr[0]][func_arr[1]]($form, response);
741              }else if($form.data('os-pass-this')){
742                window[func_arr[0]][func_arr[1]]($form);
743              }else if($form.data('os-pass-response')){
744                window[func_arr[0]][func_arr[1]](response);
745              }else{
746                window[func_arr[0]][func_arr[1]]();
747              }
748            }else{
749              if(typeof window[func_name] !== 'function'){
750                console.log(func_name + ' is undefined');
751              }
752              if($form.data('os-pass-this') && $form.data('os-pass-response')){
753                window[func_name]($form, response);
754              }else if($form.data('os-pass-this')){
755                window[func_name]($form);
756              }else if($form.data('os-pass-response')){
757                window[func_name](response);
758              }else{
759                window[func_name]();
760              }
761            }
762          }
763          $('button.os-loading').removeClass('os-loading');
764        }else{
765          $('button.os-loading').removeClass('os-loading');
766          if($form.data('os-show-errors-as-notification')){
767            latepoint_add_notification(response.message, 'error');
768          }else{
769            latepoint_add_notification(response.message, 'error');
770            $([document.documentElement, document.body]).animate({
771                scrollTop: ($form.find(".os-form-message-w").offset().top - 30)
772            }, 200);
773          }
774        }
775        if(response.form_values_to_update){
776          $.each(response.form_values_to_update, function(name, value){
777            $form.find('[name="'+ name +'"]').val(value);
778          });
779        }
780      }
781    });
782    return false;
783  });
784});
785
786/*
787 * Copyright (c) 2022 LatePoint LLC. All rights reserved.
788 */
789
790
791function latepoint_init_order_summary_lightbox() {
792    let $lightbox = jQuery('.customer-dashboard-order-summary-lightbox');
793    latepoint_init_qr_trigger($lightbox);
794    latepoint_init_item_details_popup($lightbox);
795}
796
797function latepoint_init_qr_trigger($lightbox){
798    $lightbox.on('click', '.qr-show-trigger', function () {
799        jQuery(this).closest('.summary-box-wrapper').find('.qr-code-on-full-summary').toggleClass('show-vevent-qr-code');
800        return false;
801    });
802}
803
804function latepoint_init_item_details_popup($lightbox){
805
806    $lightbox.on('click', '.os-item-details-popup-close', function () {
807        var $ligthbox = jQuery(this).closest('.latepoint-lightbox-content');
808        $ligthbox.find('.os-item-details-popup.open').remove();
809        $ligthbox.find('.full-summary-wrapper').show();
810        $ligthbox.find('.booking-status-info-wrapper').show();
811        return false;
812    });
813
814    $lightbox.on('click', '.os-trigger-item-details-popup', function () {
815        var $ligthbox = jQuery(this).closest('.latepoint-lightbox-content');
816        $ligthbox.find('.full-summary-wrapper').hide();
817        $ligthbox.find('.booking-status-info-wrapper').hide();
818        $ligthbox.find('.os-item-details-popup.open').remove();
819        var $popup = $ligthbox.find('#' + jQuery(this).data('item-details-popup-id')).clone();
820        $popup.addClass('open').appendTo($ligthbox);
821        return false;
822    });
823}
824
825function latepoint_init_bundle_scheduling_summary() {
826
827}
828
829
830function latepoint_manage_by_key_reload_booking() {
831    let $wrapper = jQuery('.manage-booking-wrapper');
832    $wrapper.addClass('os-loading')
833    let params = {
834        key: $wrapper.data('key')
835    }
836    let data = {
837        action: latepoint_helper.route_action,
838        route_name: $wrapper.data('route-name'),
839        params: params,
840        layout: 'none',
841        return_format: 'json'
842    };
843
844    jQuery.ajax({
845        type: "post",
846        dataType: "json",
847        url: latepoint_timestamped_ajaxurl(),
848        data: data,
849        success: function (data) {
850            $wrapper.removeClass('os-loading')
851            if (data.status === "success") {
852                $wrapper.replaceWith(data.message);
853            } else {
854                latepoint_show_message_inside_element(data.message, $wrapper, 'error');
855            }
856        }
857    });
858}
859
860function latepoint_init_manage_booking_by_key() {
861    let $wrapper = jQuery('.manage-booking-wrapper');
862    if (!$wrapper.length) return;
863    jQuery('.latepoint-w').on('change', '.change-booking-status-trigger', function () {
864        $wrapper.addClass('os-loading')
865        let params = {
866            key: $wrapper.data('key'),
867            status: jQuery(this).val()
868        }
869        let data = {
870            action: latepoint_helper.route_action,
871            route_name: jQuery(this).closest('.change-booking-status-trigger-wrapper').data('route-name'),
872            params: params,
873            layout: 'none',
874            return_format: 'json'
875        };
876
877        jQuery.ajax({
878            type: "post",
879            dataType: "json",
880            url: latepoint_timestamped_ajaxurl(),
881            data: data,
882            success: function (data) {
883                $wrapper.removeClass('os-loading')
884                if (data.status === "success") {
885                    latepoint_manage_by_key_reload_booking();
886                } else {
887                    latepoint_show_message_inside_element(data.message, $wrapper, 'error');
888                }
889            }
890        });
891        return false;
892    });
893
894
895    $wrapper.on('click', '.qr-show-trigger', function () {
896        jQuery(this).closest('.manage-booking-wrapper').find('.qr-code-on-full-summary').addClass('show-vevent-qr-code');
897        return false;
898    });
899    $wrapper.on('click', '.os-item-details-popup-close', function () {
900        var $wrapper = jQuery(this).closest('.manage-booking-wrapper');
901        $wrapper.find('.os-item-details-popup.open').remove();
902        $wrapper.find('.manage-booking-inner, .manage-booking-controls').show();
903        return false;
904    });
905
906    $wrapper.on('click', '.os-trigger-item-details-popup', function () {
907        var $wrapper = jQuery(this).closest('.manage-booking-wrapper');
908        $wrapper.find('.manage-booking-inner, .manage-booking-controls').hide();
909        $wrapper.find('.os-item-details-popup.open').remove();
910        var $popup = $wrapper.find('#' + jQuery(this).data('item-details-popup-id')).clone();
911        $popup.addClass('open').appendTo($wrapper);
912        return false;
913    });
914}
915
916function latepoint_init_form_masks() {
917    if (('lp_intlTelInput' in window) && ('lp_intlTelInputGlobals' in window)) {
918        jQuery('.os-mask-phone').each(function () {
919            latepoint_mask_phone(jQuery(this));
920        });
921    }
922}
923
924function latepoint_scroll_to_top_of_booking_form($booking_form_element) {
925    // if it's a form shortcode (not lightbox), scroll to top of the form
926    if ($booking_form_element.parent().hasClass('latepoint-inline-form')) {
927        $booking_form_element[0].scrollIntoView({block: "nearest", behavior: 'smooth'}); // SHOULD NOT BE FIRST!! Also need to FIX, scroll only if TOP of the booking form is above the viewport
928    }
929    // if lightbox - scroll body of lightbox to top
930    if ($booking_form_element.parent().hasClass('latepoint-lightbox-i')) {
931        $booking_form_element.find('.latepoint-body').scrollTop(0);
932    }
933}
934
935async function latepoint_init_payment_method_actions($booking_form_element, payment_method) {
936    let callbacks_list = [];
937    let is_last_step = $booking_form_element.data('next-submit-is-last') == 'yes';
938    $booking_form_element.trigger('latepoint:initPaymentMethod', [{
939        payment_method: payment_method,
940        callbacks_list: callbacks_list,
941        is_last_step: is_last_step
942    }]);
943    $booking_form_element.removeClass('step-content-loaded').addClass('step-content-loading');
944
945
946    try {
947        for (const callback of callbacks_list) {
948            await callback.action();
949        }
950        $booking_form_element.removeClass('step-content-loading').addClass('step-content-loaded').find('.lp-payment-method-content[data-payment-method="' + payment_method + '"]').show();
951    } catch (error) {
952        latepoint_show_error_and_stop_loading_booking_form(error, $booking_form_element);
953    }
954}
955
956function latepoint_lightbox_close() {
957    jQuery('body').removeClass('latepoint-lightbox-active');
958    jQuery('.latepoint-lightbox-w').remove();
959}
960
961function latepoint_show_next_btn($booking_form_element) {
962    $booking_form_element.find('.latepoint-next-btn').removeClass('disabled');
963    $booking_form_element.removeClass('hidden-buttons');
964}
965
966function clear_step_services($booking_form_element) {
967}
968
969function clear_step_service_extras($booking_form_element) {
970}
971
972function clear_step_locations($booking_form_element) {
973}
974
975function clear_step_agents($booking_form_element) {
976}
977
978function clear_step_datepicker($booking_form_element) {
979}
980
981function latepoint_hide_next_btn($booking_form_element) {
982    $booking_form_element.find('.latepoint-next-btn').addClass('disabled');
983    if ($booking_form_element.find('.latepoint-prev-btn.disabled').length) $booking_form_element.addClass('hidden-buttons');
984}
985
986
987function latepoint_show_prev_btn($booking_form_element) {
988    $booking_form_element.find('.latepoint-prev-btn').removeClass('disabled');
989    $booking_form_element.removeClass('hidden-buttons');
990}
991
992function latepoint_hide_prev_btn($booking_form_element) {
993    $booking_form_element.find('.latepoint-prev-btn').addClass('disabled');
994    if ($booking_form_element.find('.latepoint-next-btn.disabled').length) $booking_form_element.addClass('hidden-buttons');
995}
996
997
998function latepoint_remove_cart_item($trigger) {
999    let $booking_form_element = $trigger.closest('.latepoint-booking-form-element');
1000    let cart_item_id = $trigger.data('cart-item-id');
1001
1002
1003    $trigger.addClass('os-loading');
1004    let data = {
1005        action: latepoint_helper.route_action,
1006        route_name: $trigger.data('route'),
1007        params: jQuery.param({cart_item_id: cart_item_id}),
1008        layout: 'none',
1009        return_format: 'json'
1010    }
1011    jQuery.ajax({
1012        type: "post",
1013        dataType: "json",
1014        url: latepoint_timestamped_ajaxurl(),
1015        data: data,
1016        success: function (data) {
1017            if (data.status === "success") {
1018                if (cart_item_id != $booking_form_element.find('input[name="active_cart_item[id]"]').val()) {
1019                    // cart has other items - just reload the summary/step
1020                    if ($trigger.closest('.latepoint-summary-w').length) {
1021                        // removed by clicking on summary side panel
1022                        latepoint_reload_summary($booking_form_element);
1023                    } else {
1024                        // remove by clicking on cart item on verify step
1025                        latepoint_reload_step($booking_form_element);
1026                    }
1027                } else {
1028                    // this was a last item, need to go back to the start of a booking process
1029                    latepoint_restart_booking_process($booking_form_element);
1030                }
1031            } else {
1032                $trigger.removeClass('os-loading');
1033                latepoint_show_message_inside_element(data.message, $booking_form_element.find('.latepoint-body'), 'error');
1034            }
1035        }
1036    });
1037}
1038
1039function latepoint_apply_coupon($elem) {
1040    var $booking_form_element = $elem.closest('.latepoint-booking-form-element');
1041
1042    var $coupon_input = $elem;
1043    $coupon_input.closest('.coupon-code-input-w').addClass('os-loading');
1044    var form_data = new FormData($booking_form_element.find('.latepoint-form')[0]);
1045    var data = {
1046        action: latepoint_helper.route_action,
1047        route_name: $elem.data('route'),
1048        params: latepoint_formdata_to_url_encoded_string(form_data),
1049        layout: 'none',
1050        return_format: 'json'
1051    }
1052    jQuery.ajax({
1053        type: "post",
1054        dataType: "json",
1055        url: latepoint_timestamped_ajaxurl(),
1056        data: data,
1057        success: function (data) {
1058            $coupon_input.closest('.coupon-code-input-w').removeClass('os-loading');
1059            if (data.status === "success") {
1060                latepoint_show_message_inside_element(data.message, $booking_form_element.find('.latepoint-body'), 'success');
1061                $booking_form_element.find('.step-payment-w input[name="cart[payment_method]"]').val('');
1062                $booking_form_element.find('input[name="cart[payment_token]"]').val('');
1063                $booking_form_element.find('input[name="cart[payment_portion]"]').val('');
1064                latepoint_reload_step($booking_form_element);
1065            } else {
1066                latepoint_show_message_inside_element(data.message, $booking_form_element.find('.latepoint-body'), 'error');
1067            }
1068        }
1069    });
1070}
1071
1072function latepoint_remove_coupon($elem) {
1073    $elem.closest('.applied-coupon-code-wrapper').fadeOut();
1074    var $booking_form_element = $elem.closest('.latepoint-booking-form-element');
1075    let $coupon_input = $booking_form_element.find('input[name="coupon_code"]');
1076    $coupon_input.val('');
1077    latepoint_apply_coupon($coupon_input);
1078}
1079
1080function latepoint_restart_booking_process($booking_form_element) {
1081    // first first step
1082    let first_step_code = $booking_form_element.find('.latepoint-step-content').first().data('step-code');
1083    latepoint_reload_step($booking_form_element, first_step_code);
1084    return false;
1085}
1086
1087function latepoint_reload_step($booking_form_element, step_code = false) {
1088
1089    if (step_code) {
1090        $booking_form_element.find('.latepoint_current_step_code').val(step_code);
1091        $booking_form_element.removeClass(function (index, className) {
1092            return (className.match(/(^|\s)current-step-\S+/g) || []).join(' ');
1093        }).addClass('current-step-' + step_code);
1094        if ($booking_form_element.find('.latepoint-step-content[data-step-code="' + step_code + '"]')) {
1095            $booking_form_element.find('.latepoint-step-content[data-step-code="' + step_code + '"]').nextAll('.latepoint-step-content').remove();
1096            $booking_form_element.find('.latepoint-step-content[data-step-code="' + step_code + '"]').remove();
1097        }
1098    }
1099
1100    $booking_form_element.find('.latepoint_step_direction').val('specific');
1101    latepoint_submit_booking_form($booking_form_element.find('.latepoint-form'));
1102
1103    return false;
1104}
1105
1106
1107function latepoint_reset_password_from_booking_init() {
1108    jQuery('.os-step-existing-customer-login-w').hide();
1109    jQuery('.os-password-reset-form-holder').on('click', '.password-reset-back-to-login', function () {
1110        jQuery('.os-password-reset-form-holder').html('');
1111        jQuery('.os-step-existing-customer-login-w').show();
1112        return false;
1113    });
1114}
1115
1116function latepoint_bundle_selected($item) {
1117    let $booking_form_element = $item.closest('.latepoint-booking-form-element');
1118    $booking_form_element.find('input[name="active_cart_item[variant]"]').val('bundle');
1119    $booking_form_element.find('input[name="booking[service_id]"]').val('');
1120}
1121
1122function latepoint_service_selected($item) {
1123    let $booking_form_element = $item.closest('.latepoint-booking-form-element');
1124    $booking_form_element.find('input[name="active_cart_item[variant]"]').val('booking');
1125}
1126
1127async function latepoint_reload_summary($booking_form_element) {
1128    let $summary_panel = $booking_form_element.closest('.latepoint-with-summary');
1129    if (!$summary_panel.length) return;
1130
1131    if ($booking_form_element.hasClass('is-bundle-scheduling')) {
1132        return;
1133    }
1134
1135    let current_step = $booking_form_element.find('.latepoint_current_step_code').val();
1136
1137    $booking_form_element.find('.latepoint-summary-w').addClass('os-loading');
1138    let $booking_form = $booking_form_element.find('.latepoint-form');
1139    let form_data = new FormData($booking_form[0]);
1140    let data = {
1141        action: latepoint_helper.route_action,
1142        route_name: latepoint_helper.reload_booking_form_summary_route,
1143        params: latepoint_formdata_to_url_encoded_string(form_data),
1144        layout: 'none',
1145        return_format: 'json'
1146    }
1147
1148    let response = await jQuery.ajax({
1149        type: "post",
1150        dataType: "json",
1151        url: latepoint_timestamped_ajaxurl(),
1152        data: data
1153    });
1154    if (response.status === 'success') {
1155        $booking_form_element.find('.os-summary-contents').html(response.message);
1156        $booking_form_element.find('.latepoint-summary-w').removeClass('os-loading');
1157        // hide on verify and confirmation steps
1158        if (current_step && !['verify', 'confirmation'].includes(current_step) && response.message) {
1159            $summary_panel.addClass('latepoint-summary-is-open');
1160        } else {
1161            $summary_panel.removeClass('latepoint-summary-is-open');
1162        }
1163        latepoint_init_booking_summary_panel($booking_form_element);
1164    } else {
1165        throw new Error(response.message ? response.message : 'Error reloading summary');
1166    }
1167}
1168
1169function latepoint_init_booking_summary_panel($booking_form_element) {
1170    let $summary_panel = $booking_form_element.find('.latepoint-summary-w');
1171    if (!$summary_panel.length) return;
1172
1173    $summary_panel.find('.price-breakdown-unfold').on('click', function () {
1174        jQuery(this).closest('.summary-price-breakdown-wrapper').removeClass('compact-summary');
1175        return false;
1176    });
1177
1178    $summary_panel.find('.os-remove-item-from-cart').on('click keydown', function (event) {
1179        if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1180        latepoint_remove_cart_item(jQuery(this));
1181        return false;
1182    });
1183}
1184
1185function latepoint_password_changed_show_login(response) {
1186    jQuery('.os-step-existing-customer-login-w').show();
1187    jQuery('.os-password-reset-form-holder').html('');
1188    latepoint_show_message_inside_element(response.message, jQuery('.os-step-existing-customer-login-w'), 'success');
1189}
1190
1191function latepoint_hide_message_inside_element($elem = jQuery('.latepoint-body')) {
1192    if ($elem.length && $elem.find('.latepoint-message').length) {
1193        $elem.find('.latepoint-message').remove();
1194    }
1195}
1196
1197function latepoint_show_message_inside_element(message, $elem = jQuery('.latepoint-body'), message_type = 'error') {
1198    message = message || 'Error. Please try again.';
1199    if ($elem.length) {
1200        if ($elem.find('.latepoint-message').length) {
1201            $elem.find('.latepoint-message').removeClass('latepoint-message-success').removeClass('latepoint-message-error').addClass('latepoint-message-' + message_type + '').html(message).show();
1202        } else {
1203            $elem.prepend('<div class="latepoint-message latepoint-message-' + message_type + '">' + message + '</div>');
1204        }
1205        // scroll errors into view
1206        if (message_type == 'error') $elem.find('.latepoint-message')[0].scrollIntoView();
1207    }
1208}
1209
1210function latepoint_add_action(callbacks_list, action, priority = 10) {
1211    callbacks_list.push({priority: priority, action: action});
1212    callbacks_list.sort((a, b) => a.priority - b.priority);
1213    return callbacks_list;
1214}
1215
1216function latepoint_update_next_btn_label($booking_form_element) {
1217    let btn_label = $booking_form_element.find('.latepoint-step-content').last().data('next-btn-label')
1218    if (btn_label) {
1219        $booking_form_element.find('.latepoint-next-btn span').text(btn_label);
1220    }
1221}
1222
1223function latepoint_init_step(step_code, $booking_form_element) {
1224    latepoint_init_step_selectable_items($booking_form_element);
1225    latepoint_init_step_category_items(step_code);
1226    switch (step_code) {
1227        case 'customer':
1228            latepoint_init_step_contact();
1229            break;
1230        case 'booking__datepicker':
1231            latepoint_init_step_datepicker($booking_form_element);
1232            break;
1233        case 'booking__agents':
1234            latepoint_init_step_agents();
1235            break;
1236        case 'booking__locations':
1237            latepoint_init_step_locations();
1238            break;
1239        case 'booking__services':
1240            latepoint_init_step_services();
1241            break;
1242        case 'payment__methods':
1243            latepoint_init_step_payment__methods($booking_form_element);
1244            break;
1245        case 'payment__times':
1246            latepoint_init_step_payment__times($booking_form_element);
1247            break;
1248        case 'payment__portions':
1249            latepoint_init_step_payment__portions($booking_form_element);
1250            break;
1251        case 'payment__pay':
1252            latepoint_init_step_payment__pay($booking_form_element);
1253            break;
1254        case 'verify':
1255            latepoint_init_step_verify($booking_form_element);
1256            break;
1257        case 'confirmation':
1258            latepoint_init_step_confirmation($booking_form_element);
1259            break;
1260    }
1261
1262    $booking_form_element.trigger("latepoint:initStep", [{step_code: step_code}]);
1263    $booking_form_element.trigger("latepoint:initStep:" + step_code);
1264}
1265
1266
1267async function latepoint_generate_day_timeslots($day, $wrapper_element = false, $scrollable_wrapper = false) {
1268    if (!$wrapper_element) $wrapper_element = $day.closest('.latepoint-booking-form-element');
1269
1270    $day.addClass('selected');
1271
1272    var service_duration = $day.data('service-duration');
1273    var interval = $day.data('interval');
1274    var work_start_minutes = $day.data('work-start-time');
1275    var work_end_minutes = $day.data('work-end-time');
1276    var total_work_minutes = $day.data('total-work-minutes');
1277    var bookable_minutes = [];
1278    var available_capacities_of_bookable_minute = [];
1279    if ($day.attr('data-bookable-minutes')) {
1280        if ($day.data('bookable-minutes').toString().indexOf(':') > -1) {
1281            // has capacity information embedded into bookable minutes string
1282            let bookable_minutes_with_capacity = $day.data('bookable-minutes').toString().split(',');
1283            for (let i = 0; i < bookable_minutes_with_capacity.length; i++) {
1284                bookable_minutes.push(parseInt(bookable_minutes_with_capacity[i].split(':')[0]));
1285                available_capacities_of_bookable_minute.push(parseInt(bookable_minutes_with_capacity[i].split(':')[1]));
1286            }
1287        } else {
1288            bookable_minutes = $day.data('bookable-minutes').toString().split(',').map(Number);
1289        }
1290    }
1291    var work_minutes = $day.data('work-minutes').toString().split(',').map(Number);
1292
1293    var $timeslots = $wrapper_element.find('.timeslots');
1294    $timeslots.html('');
1295
1296    if (total_work_minutes > 0 && bookable_minutes.length && work_minutes.length) {
1297        var prev_minutes = false;
1298        work_minutes.forEach(function (current_minutes) {
1299            var ampm = latepoint_am_or_pm(current_minutes);
1300
1301            var timeslot_class = 'dp-timepicker-trigger';
1302            var timeslot_available_capacity = 0;
1303            if (latepoint_helper.time_pick_style == 'timeline') {
1304                timeslot_class += ' dp-timeslot';
1305            } else {
1306                timeslot_class += ' dp-timebox';
1307            }
1308
1309            if (prev_minutes !== false && ((current_minutes - prev_minutes) > service_duration)) {
1310                // show interval that is off between two work periods
1311                var off_label = latepoint_minutes_to_hours_and_minutes(prev_minutes + service_duration) + ' ' + latepoint_am_or_pm(prev_minutes + service_duration) + ' - ' + latepoint_minutes_to_hours_and_minutes(current_minutes) + ' ' + latepoint_am_or_pm(current_minutes);
1312                var off_width = (((current_minutes - prev_minutes - service_duration) / total_work_minutes) * 100);
1313                $timeslots.append('<div class="' + timeslot_class + ' is-off" style="max-width:' + off_width + '%; width:' + off_width + '%"><span class="dp-label">' + off_label + '</span></div>');
1314            }
1315
1316            if (!bookable_minutes.includes(current_minutes)) {
1317                timeslot_class += ' is-booked';
1318            } else {
1319                if (available_capacities_of_bookable_minute.length) timeslot_available_capacity = available_capacities_of_bookable_minute[bookable_minutes.indexOf(current_minutes)];
1320            }
1321            var tick_html = '';
1322            var capacity_label = '';
1323            var capacity_label_html = '';
1324            var capacity_internal_label_html = '';
1325
1326            if (((current_minutes % 60) == 0) || (interval >= 60)) {
1327                timeslot_class += ' with-tick';
1328                tick_html = '<span class="dp-tick"><strong>' + latepoint_minutes_to_hours_preferably(current_minutes) + '</strong>' + ' ' + ampm + '</span>';
1329            }
1330            var timeslot_label = latepoint_minutes_to_hours_and_minutes(current_minutes) + ' ' + ampm;
1331            if (latepoint_show_booking_end_time()) {
1332                var end_minutes = current_minutes + service_duration;
1333                if (end_minutes > 1440) end_minutes = end_minutes - 1440;
1334                var end_minutes_ampm = latepoint_am_or_pm(end_minutes);
1335                timeslot_label += ' - <span class="dp-label-end-time">' + latepoint_minutes_to_hours_and_minutes(end_minutes) + ' ' + end_minutes_ampm + '</span>';
1336            }
1337            if (timeslot_available_capacity) {
1338                var spaces_message = timeslot_available_capacity > 1 ? latepoint_helper.many_spaces_message : latepoint_helper.single_space_message;
1339                capacity_label = timeslot_available_capacity + ' ' + spaces_message;
1340                capacity_label_html = '<span class="dp-capacity">' + capacity_label + '</span>';
1341                capacity_internal_label_html = '<span class="dp-label-capacity">' + capacity_label + '</span>';
1342            }
1343            timeslot_label = timeslot_label.trim();
1344            $timeslots.removeClass('slots-not-available').append('<div tabindex="0" class="' + timeslot_class + '" data-minutes="' + current_minutes + '"><span class="dp-label">' + capacity_internal_label_html + '<span class="dp-label-time">' + timeslot_label + '</span>' + '</span>' + tick_html + capacity_label_html + '</div>');
1345            prev_minutes = current_minutes;
1346        });
1347    } else {
1348        // No working hours this day
1349        $timeslots.addClass('slots-not-available').append('<div class="not-working-message">' + latepoint_helper.msg_not_available + "</div>");
1350    }
1351    jQuery('.times-header-label span').text($day.data('nice-date'));
1352    $wrapper_element.find('.time-selector-w').slideDown(200, function () {
1353        if (!$scrollable_wrapper) $scrollable_wrapper = $wrapper_element.find('.latepoint-body');
1354        $scrollable_wrapper.stop();
1355        $wrapper_element.find('.time-selector-w')[0].scrollIntoView({block: "nearest", behavior: 'smooth'});
1356    });
1357}
1358
1359
1360function latepoint_recurring_option_clicked(event) {
1361    if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1362    let $btn = jQuery(this);
1363    let $booking_form_element = $btn.closest('.latepoint-booking-form-element');
1364    $booking_form_element.find('.latepoint_is_recurring').val($btn.data('value'));
1365    latepoint_trigger_next_btn($booking_form_element);
1366    $booking_form_element.find('.step-datepicker-w').removeClass('show-recurring-prompt');
1367    return false;
1368}
1369
1370function latepoint_timeslot_clicked(event) {
1371    if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1372    event.preventDefault();
1373    let $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1374    let $trigger = jQuery(this);
1375    if ($trigger.hasClass('is-booked') || jQuery(this).hasClass('is-off')) {
1376        // Show error message that you cant select a booked period
1377    } else {
1378        if ($trigger.hasClass('selected')) {
1379            $trigger.removeClass('selected');
1380            $trigger.find('.dp-success-label').remove();
1381            $booking_form_element.find('.latepoint_start_time').val('');
1382            latepoint_hide_next_btn($booking_form_element);
1383            latepoint_reload_summary($booking_form_element);
1384        } else {
1385            $booking_form_element.find('.dp-timepicker-trigger.selected').removeClass('selected').find('.dp-success-label').remove();
1386            let selected_timeslot_time = $trigger.find('.dp-label-time').html();
1387            $trigger.addClass('selected').find('.dp-label').prepend('<span class="dp-success-label">' + latepoint_helper.datepicker_timeslot_selected_label + '</span>');
1388
1389            let start_minutes = parseInt($trigger.data('minutes'));
1390            let start_date = $trigger.closest('.os-dates-and-times-w').find('.os-day.selected').data('date');
1391
1392            if ($booking_form_element.find('.recurring-bookings-preview-wrapper').length && $booking_form_element.find('.os-recurrence-rules').length) {
1393                // recurring datepicker
1394                if ($booking_form_element.find('.recurring-bookings-preview-wrapper .recurring-booking-preview.is-editing').length) {
1395                    // editing one of timeslots, not the recurrence settings
1396                    let $recurring_bookings_fields = $booking_form_element.find('.os-recurrence-selection-fields-wrapper');
1397                    let $edited_booking = $booking_form_element.find('.recurring-bookings-preview-wrapper .recurring-booking-preview.is-editing');
1398
1399                    $recurring_bookings_fields.find('input[name="recurrence[overrides][' + $edited_booking.data('stamp') + '][custom_day]"]').val(start_date);
1400                    $recurring_bookings_fields.find('input[name="recurrence[overrides][' + $edited_booking.data('stamp') + '][custom_minutes]"]').val(start_minutes);
1401
1402                    return window.latepointRecurringBookingsFrontFeature.reload_recurrence_rules($booking_form_element, false);
1403                } else {
1404                    // editing recurrence start date/time
1405                    $booking_form_element.find('.latepoint_start_date').val(start_date);
1406                    $booking_form_element.find('.latepoint_start_time').val(start_minutes);
1407                    return window.latepointRecurringBookingsFrontFeature.reload_recurrence_rules($booking_form_element, true);
1408                }
1409            } else {
1410                $booking_form_element.find('.latepoint_start_date').val(start_date);
1411                $booking_form_element.find('.latepoint_start_time').val(start_minutes);
1412                if ($trigger.closest('.os-dates-and-times-w').data('allow-recurring') === 'yes') {
1413                    $booking_form_element.find('.step-datepicker-w').addClass('show-recurring-prompt');
1414                    $booking_form_element.find('.os-recurring-suggestion-wrapper')[0].scrollIntoView({block: "nearest", behavior: 'smooth'});
1415                    latepoint_hide_next_btn($booking_form_element);
1416                    latepoint_hide_prev_btn($booking_form_element);
1417                } else {
1418                    latepoint_trigger_next_btn($booking_form_element);
1419                }
1420            }
1421        }
1422    }
1423    return false;
1424}
1425
1426function latepoint_init_timeslots($booking_form_element = false) {
1427    if (!$booking_form_element) return;
1428    $booking_form_element.off('click', '.dp-timepicker-trigger', latepoint_timeslot_clicked);
1429    $booking_form_element.on('click', '.dp-timepicker-trigger', latepoint_timeslot_clicked);
1430    $booking_form_element.off('keydown', '.dp-timepicker-trigger', latepoint_timeslot_clicked);
1431    $booking_form_element.on('keydown', '.dp-timepicker-trigger', latepoint_timeslot_clicked);
1432
1433    $booking_form_element.off('click', '.os-recurring-suggestion-option', latepoint_recurring_option_clicked);
1434    $booking_form_element.on('click', '.os-recurring-suggestion-option', latepoint_recurring_option_clicked);
1435    $booking_form_element.off('keydown', '.os-recurring-suggestion-option', latepoint_recurring_option_clicked);
1436    $booking_form_element.on('keydown', '.os-recurring-suggestion-option', latepoint_recurring_option_clicked);
1437}
1438
1439async function latepoint_monthly_calendar_load_next_month($booking_form_element) {
1440    try {
1441
1442        if ($booking_form_element.find('.os-monthly-calendar-days-w.active + .os-monthly-calendar-days-w').length) {
1443            $booking_form_element.find('.os-monthly-calendar-days-w.active').removeClass('active').next('.os-monthly-calendar-days-w').addClass('active');
1444            $booking_form_element.find('.os-month-prev-btn').removeClass('disabled');
1445            latepoint_calendar_set_month_label($booking_form_element);
1446            return true;
1447        } else {
1448            let $btn = $booking_form_element.find('.os-month-next-btn');
1449            let next_month_route_name = $btn.data('route');
1450            $btn.addClass('os-loading');
1451            let $calendar_element = $booking_form_element.find('.os-monthly-calendar-days-w').last();
1452            let calendar_year = $calendar_element.data('calendar-year');
1453            let calendar_month = $calendar_element.data('calendar-month');
1454            if (calendar_month == 12) {
1455                calendar_year = calendar_year + 1;
1456                calendar_month = 1;
1457            } else {
1458                calendar_month = calendar_month + 1;
1459            }
1460            let form_data = new FormData($booking_form_element.find('.latepoint-form')[0]);
1461            form_data.set('target_date_string', `${calendar_year}-${calendar_month}-1`);
1462            let params = latepoint_formdata_to_url_encoded_string(form_data);
1463            let data = {
1464                action: latepoint_helper.route_action,
1465                route_name: next_month_route_name,
1466                params: params,
1467                layout: 'none',
1468                return_format: 'json'
1469            }
1470            let response = await jQuery.ajax({
1471                type: "post",
1472                dataType: "json",
1473                url: latepoint_timestamped_ajaxurl(),
1474                data: data,
1475                success: function (data) {
1476                }
1477            });
1478            $btn.removeClass('os-loading');
1479            if (response.status === "success") {
1480                $booking_form_element.find('.os-months').append(response.message);
1481                $booking_form_element.find('.os-monthly-calendar-days-w.active').removeClass('active').next('.os-monthly-calendar-days-w').addClass('active');
1482                latepoint_calendar_set_month_label($booking_form_element);
1483                latepoint_calendar_show_or_hide_prev_next_buttons($booking_form_element);
1484                return true;
1485            } else {
1486                console.log(response.message);
1487                return false;
1488            }
1489
1490        }
1491    } catch (e) {
1492        console.log(e);
1493        alert('Error:' + e);
1494        return false;
1495    }
1496}
1497
1498function latepoint_init_monthly_calendar_navigation($booking_form_element = false) {
1499    if (!$booking_form_element) return;
1500    $booking_form_element.find('.os-month-next-btn').on('click', async function () {
1501        let $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1502        return latepoint_monthly_calendar_load_next_month($booking_form_element);
1503    });
1504    $booking_form_element.find('.os-month-prev-btn').on('click', function () {
1505        var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1506        if ($booking_form_element.find('.os-monthly-calendar-days-w.active').prev('.os-monthly-calendar-days-w').length) {
1507            $booking_form_element.find('.os-monthly-calendar-days-w.active').removeClass('active').prev('.os-monthly-calendar-days-w').addClass('active');
1508            latepoint_calendar_set_month_label($booking_form_element);
1509        }
1510        latepoint_calendar_show_or_hide_prev_next_buttons($booking_form_element);
1511        return false;
1512    });
1513}
1514
1515function latepoint_calendar_set_month_label($booking_form_element) {
1516    $booking_form_element.find('.os-current-month-label .current-year').text($booking_form_element.find('.os-monthly-calendar-days-w.active').data('calendar-year'));
1517    $booking_form_element.find('.os-current-month-label .current-month').text($booking_form_element.find('.os-monthly-calendar-days-w.active').data('calendar-month-label'));
1518}
1519
1520
1521function latepoint_calendar_show_or_hide_prev_next_buttons($booking_form_element) {
1522    $booking_form_element.find('.os-current-month-label .current-year').text($booking_form_element.find('.os-monthly-calendar-days-w.active .os-monthly-calendar-days').data('calendar-year'));
1523    $booking_form_element.find('.os-current-month-label .current-month').text($booking_form_element.find('.os-monthly-calendar-days-w.active .os-monthly-calendar-days').data('calendar-month-label'));
1524
1525    if ($booking_form_element.find('.os-monthly-calendar-days-w.active').prev('.os-monthly-calendar-days-w').length) {
1526        $booking_form_element.find('.os-month-prev-btn').removeClass('disabled');
1527    } else {
1528        $booking_form_element.find('.os-month-prev-btn').addClass('disabled');
1529    }
1530}
1531
1532function latepoint_format_minutes_to_time(minutes, service_duration) {
1533    var ampm = latepoint_am_or_pm(minutes);
1534    var formatted_time = latepoint_minutes_to_hours_and_minutes(minutes) + ' ' + ampm;
1535    if (latepoint_show_booking_end_time()) {
1536        var end_minutes = minutes + service_duration;
1537        var end_minutes_ampm = latepoint_am_or_pm(end_minutes);
1538        formatted_time += ' - ' + latepoint_minutes_to_hours_and_minutes(end_minutes) + ' ' + end_minutes_ampm;
1539    }
1540    formatted_time = formatted_time.trim();
1541    return formatted_time;
1542}
1543
1544function latepoint_monthly_calendar_day_clicked(event) {
1545    if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1546    let $day = jQuery(this);
1547    if ($day.hasClass('os-day-passed')) return false;
1548    if ($day.hasClass('os-not-in-allowed-period')) return false;
1549    if($day.closest('.os-dates-and-times-w').hasClass('calendar-style-modern')) {
1550        if ($day.hasClass('os-month-prev')) return false;
1551        if ($day.hasClass('os-month-next')) return false;
1552    }
1553    var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1554    if ($day.closest('.os-recurrence-datepicker-wrapper').length) {
1555        // recurrent datepicker
1556        $day.closest('.os-recurrence-datepicker-wrapper').find('.os-day.selected').removeClass('selected');
1557        $day.addClass('selected');
1558        if ($day.closest('.os-dates-and-times-w').hasClass('days-only')) {
1559            $day.closest('.step-recurring-bookings-w').find('input[name="recurrence[rules][repeat_end_date]"]').val($day.data('date'));
1560            window.latepointRecurringBookingsFrontFeature.reload_recurrence_rules($booking_form_element, true);
1561        } else {
1562            latepoint_generate_day_timeslots($day);
1563            $day.closest('.os-recurrence-datepicker-wrapper').find('.time-selector-w')[0].scrollIntoView({block: "nearest", behavior: 'smooth'});
1564        }
1565    } else {
1566        // regular datepicker
1567        if ($day.closest('.os-monthly-calendar-days-w').hasClass('hide-if-single-slot')) {
1568            // HIDE TIMESLOT IF ONLY ONE TIMEPOINT
1569            if ($day.hasClass('os-not-available')) {
1570                // clicked on a day that has no available timeslots
1571                // do nothing
1572            } else {
1573                $booking_form_element.find('.os-day.selected').removeClass('selected');
1574                $day.addClass('selected');
1575                // set date
1576                $booking_form_element.find('.latepoint_start_date').val($day.data('date'));
1577                if ($day.hasClass('os-one-slot-only')) {
1578                    // clicked on a day that has only one slot available
1579                    var bookable_minutes = $day.data('bookable-minutes').toString().split(':')[0];
1580                    var selected_timeslot_time = latepoint_format_minutes_to_time(Number(bookable_minutes), Number($day.data('service-duration')));
1581                    $booking_form_element.find('.latepoint_start_time').val($day.data('bookable-minutes'));
1582                    latepoint_show_next_btn($booking_form_element);
1583                    $booking_form_element.find('.time-selector-w').slideUp(200);
1584                } else {
1585                    // regular day with more than 1 timeslots available
1586                    // build timeslots
1587                    latepoint_generate_day_timeslots($day);
1588                    // clear time and hide next btn
1589                    $booking_form_element.find('.latepoint_start_time').val('');
1590                    latepoint_hide_next_btn($booking_form_element);
1591                }
1592                latepoint_reload_summary($booking_form_element);
1593            }
1594        } else {
1595
1596            // SHOW TIMESLOTS EVEN IF ONLY ONE TIMEPOINT
1597            $booking_form_element.find('.latepoint_start_date').val($day.data('date'));
1598            $booking_form_element.find('.os-day.selected').removeClass('selected');
1599            $day.addClass('selected');
1600
1601            // build timeslots
1602            latepoint_generate_day_timeslots($day);
1603            // clear time and hide next btn
1604            latepoint_reload_summary($booking_form_element);
1605            $booking_form_element.find('.latepoint_start_time').val('');
1606            latepoint_hide_next_btn($booking_form_element);
1607        }
1608    }
1609
1610
1611    return false;
1612}
1613
1614async function latepoint_init_step_datepicker($booking_form_element = false) {
1615    if (!$booking_form_element) return true;
1616    latepoint_init_timeslots($booking_form_element);
1617    latepoint_init_monthly_calendar_navigation($booking_form_element);
1618    $booking_form_element.off('click', '.os-months .os-day', latepoint_monthly_calendar_day_clicked);
1619    $booking_form_element.on('click', '.os-months .os-day', latepoint_monthly_calendar_day_clicked);
1620    $booking_form_element.off('keydown', '.os-months .os-day', latepoint_monthly_calendar_day_clicked);
1621    $booking_form_element.on('keydown', '.os-months .os-day', latepoint_monthly_calendar_day_clicked);
1622
1623    if ($booking_form_element.find('input[name="booking[start_date]"]').val()) {
1624        $booking_form_element.find('.os-day[data-date="' + $booking_form_element.find('input[name="booking[start_date]"]').val() + '"]').trigger('click');
1625    } else {
1626        if($booking_form_element.find('.os-dates-and-times-w').hasClass('auto-search')){
1627            let max_number_of_months_to_check = 24;
1628            let current_year = new Date().getFullYear();
1629            for (let i = 0; i < max_number_of_months_to_check; i++) {
1630                let $active_month = $booking_form_element.find('.os-monthly-calendar-days-w.active');
1631                let searching_month_label = $active_month.data('calendar-month-label');
1632                if ($active_month.data('calendar-year') != current_year) searching_month_label += ' ' + $active_month.data('calendar-year');
1633                $booking_form_element.find('.os-calendar-searching-info span').text(searching_month_label);
1634                // check if active month has any days available for booking
1635                let $first_available = $active_month.find('.os-day').not('.os-not-available').first();
1636                if ($first_available.length) {
1637                    break;
1638                } else {
1639                    await latepoint_monthly_calendar_load_next_month($booking_form_element);
1640                }
1641            }
1642        }
1643    }
1644    $booking_form_element.find('.os-dates-and-times-w').removeClass('is-searching');
1645    return true;
1646}
1647
1648
1649function latepoint_init_step_verify($booking_form_element = false) {
1650    if (!$booking_form_element) return;
1651    $booking_form_element.closest('.latepoint-summary-is-open').removeClass('latepoint-summary-is-open');
1652
1653    $booking_form_element.find('.coupon-code-wrapper-on-verify .coupon-code-trigger-on-verify-w a').on('click', function (e) {
1654        jQuery(this).closest('.coupon-code-wrapper-on-verify').addClass('entering-coupon').find('.coupon-code-input').trigger('focus');
1655        return false;
1656    });
1657    $booking_form_element.find('.coupon-code-wrapper-on-verify .coupon-code-input-cancel').on('click', function (e) {
1658        jQuery(this).closest('.coupon-code-wrapper-on-verify').removeClass('entering-coupon');
1659        return false;
1660    });
1661
1662    $booking_form_element.find('.coupon-code-wrapper-on-verify .coupon-code-input-submit').on('click', function (e) {
1663        latepoint_apply_coupon(jQuery(this).closest('.coupon-code-input-w').find('.coupon-code-input'));
1664        return false;
1665    });
1666
1667    $booking_form_element.find('.os-remove-item-from-cart').on('click keydown', function (event) {
1668        if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1669        //make sure to clear active cart item so it doesn't add it again on reload!
1670        if (confirm(jQuery(this).data('confirm-text'))) {
1671            latepoint_remove_cart_item(jQuery(this));
1672        }
1673        return false;
1674    });
1675
1676    $booking_form_element.find('.coupon-code-wrapper-on-verify .coupon-code-clear').on('click', function (e) {
1677        latepoint_remove_coupon(jQuery(this));
1678        return false;
1679    });
1680
1681    $booking_form_element.find('.coupon-code-wrapper-on-verify input.coupon-code-input').on('keyup', function (e) {
1682        if (e.which === 13) {
1683            latepoint_apply_coupon(jQuery(this));
1684            return false;
1685        }
1686    });
1687}
1688
1689
1690function latepoint_init_step_payment__pay($booking_form_element = false) {
1691    var selected_payment_method = $booking_form_element.find('input[name="cart[payment_method]"]').val();
1692    latepoint_init_payment_method_actions($booking_form_element, selected_payment_method);
1693}
1694
1695function latepoint_init_step_payment__portions($booking_form_element = false) {
1696    // Selecting Payment Time
1697    $booking_form_element.find('.lp-payment-trigger-payment-portion-selector').on('click keydown', function (event) {
1698        if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1699        var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1700        $booking_form_element.find('input[name="' + jQuery(this).data('holder') + '"]').val(jQuery(this).data('value'));
1701        latepoint_show_prev_btn($booking_form_element);
1702        latepoint_trigger_next_btn($booking_form_element);
1703        return false;
1704    });
1705}
1706
1707function latepoint_init_step_payment__times($booking_form_element = false) {
1708    // Selecting Payment Time
1709    $booking_form_element.find('.lp-payment-trigger-payment-time-selector').on('click keydown', function (event) {
1710        if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1711        var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1712        $booking_form_element.find('input[name="' + jQuery(this).data('holder') + '"]').val(jQuery(this).data('value'));
1713        latepoint_show_prev_btn($booking_form_element);
1714        latepoint_trigger_next_btn($booking_form_element);
1715        return false;
1716    });
1717}
1718
1719
1720function latepoint_init_step_payment__methods($booking_form_element = false) {
1721    // Selecting Payment Time
1722    $booking_form_element.find('.lp-payment-trigger-payment-method-selector').on('click', function (e) {
1723        var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1724        $booking_form_element.find('input[name="' + jQuery(this).data('holder') + '"]').val(jQuery(this).data('value'));
1725        latepoint_show_prev_btn($booking_form_element);
1726        latepoint_trigger_next_btn($booking_form_element);
1727        return false;
1728    });
1729}
1730
1731function latepoint_category_item_clicked(event) {
1732    if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1733    let $item = jQuery(event.target);
1734
1735    let $booking_form_element = $item.closest('.latepoint-booking-form-element');
1736    latepoint_show_prev_btn($booking_form_element);
1737    $item.closest('.latepoint-step-content').addClass('selecting-item-category');
1738    let $category_wrapper = $item.closest('.os-item-category-w');
1739    let $main_parent = $item.closest('.os-item-categories-main-parent');
1740    if ($category_wrapper.hasClass('selected')) {
1741        $category_wrapper.removeClass('selected');
1742        if ($category_wrapper.parent().closest('.os-item-category-w').length) {
1743            $category_wrapper.parent().closest('.os-item-category-w').addClass('selected');
1744        } else {
1745            $main_parent.removeClass('show-selected-only');
1746        }
1747    } else {
1748        $main_parent.find('.os-item-category-w.selected').removeClass('selected');
1749        $main_parent.addClass('show-selected-only');
1750        $category_wrapper.addClass('selected');
1751    }
1752    return false;
1753}
1754
1755function latepoint_init_step_category_items(step_code) {
1756    let $category_items = jQuery('.latepoint-step-content[data-step-code="' + step_code + '"] .os-item-category-info');
1757    $category_items.on('click', latepoint_category_item_clicked);
1758    $category_items.on('keydown', latepoint_category_item_clicked);
1759}
1760
1761
1762function latepoint_init_step_selectable_items($booking_form_element) {
1763    $booking_form_element.off('click', '.os-selectable-items .os-selectable-item', latepoint_selectable_item_clicked);
1764    $booking_form_element.on('click', '.os-selectable-items .os-selectable-item', latepoint_selectable_item_clicked);
1765
1766    $booking_form_element.off('click', '.os-selectable-items .os-selectable-item .item-quantity-selector-input', latepoint_selectable_item_quantity_keyup);
1767    $booking_form_element.on('click', '.os-selectable-items .os-selectable-item .item-quantity-selector-input', latepoint_selectable_item_quantity_keyup);
1768
1769
1770    $booking_form_element.off('keydown', '.os-selectable-items .os-selectable-item', latepoint_selectable_item_clicked);
1771    $booking_form_element.on('keydown', '.os-selectable-items .os-selectable-item', latepoint_selectable_item_clicked);
1772}
1773
1774
1775function latepoint_update_quantity_for_selectable_items($item) {
1776    var ids = $item.closest('.os-selectable-items')
1777        .find('.os-selectable-item.selected')
1778        .map(function () {
1779            if (jQuery(this).hasClass('has-quantity')) {
1780                return jQuery(this).data('item-id') + ':' + jQuery(this).find('input.item-quantity-selector-input').val();
1781            } else {
1782                return jQuery(this).data('item-id');
1783            }
1784        }).get();
1785    $item.closest('.latepoint-booking-form-element').find($item.data('id-holder')).val(ids);
1786}
1787
1788function latepoint_selectable_item_quantity_keyup(event) {
1789    var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1790    var $item = jQuery(this).closest('.os-selectable-item');
1791    var new_value = jQuery(this).val();
1792    if (new_value && new_value.match(/^\d+$/)) {
1793        var max_quantity = $item.data('max-quantity');
1794        if (max_quantity && (new_value > max_quantity)) new_value = max_quantity;
1795    } else {
1796        new_value = 0;
1797    }
1798    jQuery(this).val(new_value);
1799
1800    if (($item.hasClass('selected') && (new_value > 0)) || (!$item.hasClass('selected') && (new_value == 0))) {
1801        latepoint_update_quantity_for_selectable_items($item);
1802        latepoint_reload_summary($booking_form_element);
1803        return false;
1804    } else {
1805        $item.trigger('click');
1806    }
1807}
1808
1809function latepoint_selectable_item_clicked(event) {
1810    if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1811    event.stopPropagation();
1812    event.stopImmediatePropagation();
1813    var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1814    if (jQuery(this).hasClass('has-quantity')) {
1815        if (jQuery(event.target).hasClass('item-quantity-selector')) {
1816            var current_value = parseInt(jQuery(this).find('input.item-quantity-selector-input').val());
1817            var new_value = (jQuery(event.target).data('sign') == 'minus') ? current_value - 1 : current_value + 1;
1818            var max_quantity = jQuery(this).data('max-quantity');
1819            if (new_value < 0) new_value = 0;
1820            if (max_quantity && (new_value > max_quantity)) new_value = max_quantity;
1821            jQuery(this).find('input.item-quantity-selector-input').val(new_value);
1822            if ((jQuery(this).hasClass('selected') && (new_value > 0)) || (!jQuery(this).hasClass('selected') && (new_value == 0))) {
1823                latepoint_update_quantity_for_selectable_items(jQuery(this));
1824                latepoint_reload_summary($booking_form_element);
1825                return false;
1826            }
1827        }
1828        if (jQuery(event.target).hasClass('item-quantity-selector-input')) {
1829            latepoint_update_quantity_for_selectable_items(jQuery(this));
1830            latepoint_reload_summary($booking_form_element);
1831            return false;
1832        }
1833    }
1834    var summary_value = '';
1835    if (jQuery(this).hasClass('os-allow-multiselect')) {
1836        if (jQuery(this).hasClass('selected')) {
1837            jQuery(this).removeClass('selected');
1838            if (jQuery(this).hasClass('has-quantity')) jQuery(this).find('input.item-quantity-selector-input').val(0);
1839        } else {
1840            jQuery(this).addClass('selected');
1841            if (jQuery(this).hasClass('has-quantity') && !(jQuery(this).find('input.item-quantity-selector-input').val() > 0)) {
1842                jQuery(this).find('input.item-quantity-selector-input').val(1);
1843            }
1844        }
1845        latepoint_update_quantity_for_selectable_items(jQuery(this));
1846        latepoint_reload_summary($booking_form_element);
1847        latepoint_show_next_btn($booking_form_element);
1848    } else {
1849        if (!jQuery(this).hasClass('os-duration-item')) jQuery(this).closest('.os-item-categories-main-parent').find('.os-selectable-item.selected').removeClass('selected');
1850        jQuery(this).closest('.os-selectable-items').find('.os-selectable-item.selected').removeClass('selected');
1851        jQuery(this).addClass('selected');
1852        $booking_form_element.find(jQuery(this).data('id-holder')).val(jQuery(this).data('item-id'));
1853        if (jQuery(this).data('cart-item-item-data-key')) {
1854            latepoint_update_active_cart_item_item_data($booking_form_element, jQuery(this).data('cart-item-item-data-key'), jQuery(this).data('item-id'));
1855        }
1856        if (jQuery(this).data('os-call-func')) {
1857            window[jQuery(this).data('os-call-func')](jQuery(this));
1858        }
1859        if (jQuery(this).data('activate-sub-step')) {
1860            window[jQuery(this).data('activate-sub-step')](jQuery(this));
1861        } else {
1862            latepoint_trigger_next_btn($booking_form_element);
1863        }
1864    }
1865    return false;
1866}
1867
1868function latepoint_update_active_cart_item_item_data($booking_form_element, key, value) {
1869    let item_data_json = $booking_form_element.find('input[name="active_cart_item[item_data]"]').val();
1870    let item_data = item_data_json ? JSON.parse($booking_form_element.find('input[name="active_cart_item[item_data]"]').val()) : {};
1871    item_data[key] = value;
1872    $booking_form_element.find('input[name="active_cart_item[item_data]"]').val(JSON.stringify(item_data));
1873}
1874
1875function latepoint_format_price(price) {
1876    // replace default decimal separator dot with comma if it's in settings
1877    if (latepoint_helper.decimal_separator == ',') price = String(price).replace('.', ',');
1878    return latepoint_helper.currency_symbol_before + String(price) + latepoint_helper.currency_symbol_after;
1879}
1880
1881
1882function latepoint_init_step_services() {
1883}
1884
1885
1886function latepoint_trigger_next_btn($booking_form_element) {
1887    $booking_form_element.find('.latepoint_step_direction').val('next');
1888    latepoint_submit_booking_form($booking_form_element.find('.latepoint-form'));
1889}
1890
1891function latepoint_init_step_locations() {
1892}
1893
1894function latepoint_init_agent_details_link($booking_form_element) {
1895    $booking_form_element.on('click', '.os-trigger-item-details-popup', function () {
1896        $booking_form_element.find('.os-item-details-popup.open').remove();
1897        var $popup = $booking_form_element.find('#' + jQuery(this).data('item-details-popup-id')).first().clone().attr('id', '');
1898        $booking_form_element.find('.latepoint-form-w').addClass('showing-item-details-popup');
1899        $popup.addClass('open').appendTo($booking_form_element.find('.latepoint-body'));
1900        return false;
1901    });
1902    $booking_form_element.on('click', '.os-item-details-popup.open .os-item-details-popup-close', function () {
1903        $booking_form_element.find('.latepoint-form-w').removeClass('showing-item-details-popup');
1904        jQuery(this).closest('.os-item-details-popup.open').remove();
1905        return false;
1906    });
1907}
1908
1909function latepoint_init_step_agents() {
1910}
1911
1912
1913function latepoint_init_booking_summary_lightbox() {
1914    let $lightbox = jQuery('.customer-dashboard-booking-summary-lightbox');
1915    latepoint_init_qr_trigger($lightbox);
1916    latepoint_init_item_details_popup($lightbox);
1917}
1918
1919function latepoint_init_step_confirmation($booking_form_element = false) {
1920    if (!$booking_form_element) return;
1921    $booking_form_element.on('click', '.set-customer-password-btn', function () {
1922        var $btn = jQuery(this);
1923        var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1924
1925        $btn.addClass('os-loading');
1926        var params = {
1927            account_nonse: jQuery('input[name="account_nonse"]').val(),
1928            password: jQuery('input[name="customer[password]"]').val(),
1929            password_confirmation: jQuery('input[name="customer[password_confirmation]"]').val()
1930        }
1931        var data = {
1932            action: latepoint_helper.route_action,
1933            route_name: jQuery(this).data('btn-action'),
1934            params: jQuery.param(params),
1935            layout: 'none',
1936            return_format: 'json'
1937        }
1938        jQuery.ajax({
1939            type: "post",
1940            dataType: "json",
1941            url: latepoint_timestamped_ajaxurl(),
1942            data: data,
1943            success: function (data) {
1944                $btn.removeClass('os-loading');
1945                if (data.status === "success") {
1946                    $booking_form_element.find('.step-confirmation-set-password').html('').hide();
1947                    $booking_form_element.find('.confirmation-cabinet-info').show();
1948                } else {
1949                    latepoint_show_message_inside_element(data.message, $booking_form_element.find('.step-confirmation-set-password'), 'error');
1950                }
1951            }
1952        });
1953        return false;
1954    });
1955
1956    $booking_form_element.on('click', '.qr-show-trigger', function () {
1957        var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1958        $booking_form_element.find('.qr-code-on-full-summary').addClass('show-vevent-qr-code');
1959        return false;
1960    });
1961
1962    $booking_form_element.on('click', '.show-set-password-fields', function () {
1963        var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1964
1965        $booking_form_element.find('.step-confirmation-set-password').show();
1966        $booking_form_element.find('#customer_password').trigger('focus');
1967        jQuery(this).closest('.info-box').hide();
1968        return false;
1969    });
1970}
1971
1972function latepoint_init_customer_dashboard() {
1973    latepoint_init_form_masks();
1974    jQuery('.latepoint-customer-timezone-selector-w select').on('change', function (e) {
1975        var $select_box = jQuery(this);
1976        $select_box.closest('.latepoint-customer-timezone-selector-w').addClass('os-loading');
1977        var data = {
1978            action: latepoint_helper.route_action,
1979            route_name: jQuery(this).closest('.latepoint-customer-timezone-selector-w').data('route-name'),
1980            params: {timezone_name: jQuery(this).val()},
1981            layout: 'none',
1982            return_format: 'json'
1983        }
1984        jQuery.ajax({
1985            type: "post",
1986            dataType: "json",
1987            url: latepoint_timestamped_ajaxurl(),
1988            data: data,
1989            success: function (data) {
1990                $select_box.closest('.latepoint-customer-timezone-selector-w').removeClass('os-loading');
1991                if (data.status === "success") {
1992                    location.reload();
1993                } else {
1994
1995                }
1996            }
1997        });
1998    });
1999
2000
2001    jQuery('.latepoint-request-booking-cancellation').on('click', function () {
2002        if (!confirm(latepoint_helper.cancel_booking_prompt)) return false;
2003        var $this = jQuery(this);
2004        var $booking_box = $this.closest('.customer-booking');
2005
2006        var route = jQuery(this).data('route');
2007        var params = {id: $booking_box.data('id')};
2008
2009        var data = {
2010            action: latepoint_helper.route_action,
2011            route_name: route,
2012            params: params,
2013            layout: 'none',
2014            return_format: 'json'
2015        }
2016        $this.addClass('os-loading');
2017        jQuery.ajax({
2018            type: "post",
2019            dataType: "json",
2020            url: latepoint_timestamped_ajaxurl(),
2021            data: data,
2022            success: function (data) {
2023                if (data.status === "success") {
2024                    $this.remove();
2025                    location.reload();
2026                } else {
2027                    $this.removeClass('os-loading');
2028                }
2029            }
2030        });
2031        return false;
2032    });
2033
2034}
2035
2036
2037function get_customer_name($wrapper) {
2038    var customer_name = '';
2039    var first_name = $wrapper.find('input[name="customer[first_name]"]').val();
2040    var last_name = $wrapper.find('input[name="customer[last_name]"]').val();
2041    if (first_name) customer_name += first_name;
2042    if (last_name) customer_name += ' ' + last_name;
2043    return customer_name.trim();
2044}
2045
2046function latepoint_init_step_contact() {
2047    latepoint_init_form_masks();
2048
2049    // Init Logout button
2050    jQuery('.step-customer-logout-btn').on('click', function () {
2051        var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
2052        var data = {
2053            action: latepoint_helper.route_action,
2054            route_name: jQuery(this).data('btn-action'),
2055            layout: 'none',
2056            return_format: 'json'
2057        }
2058        latepoint_step_content_change_start($booking_form_element);
2059        jQuery.ajax({
2060            type: "post",
2061            dataType: "json",
2062            url: latepoint_timestamped_ajaxurl(),
2063            data: data,
2064            success: function (data) {
2065                latepoint_reload_step($booking_form_element);
2066            }
2067        });
2068        return false;
2069    });
2070
2071    // Init Login Existing Customer Button
2072    jQuery('.step-login-existing-customer-btn').on('click', function () {
2073        var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
2074        var params = {
2075            email: $booking_form_element.find('.os-step-existing-customer-login-w input[name="customer_login[email]"]').val(),
2076            password: $booking_form_element.find('.os-step-existing-customer-login-w input[name="customer_login[password]"]').val()
2077        }
2078        var data = {
2079            action: latepoint_helper.route_action,
2080            route_name: jQuery(this).data('btn-action'),
2081            params: jQuery.param(params),
2082            layout: 'none',
2083            return_format: 'json'
2084        }
2085        latepoint_step_content_change_start($booking_form_element);
2086        jQuery.ajax({
2087            type: "post",
2088            dataType: "json",
2089            url: latepoint_timestamped_ajaxurl(),
2090            data: data,
2091            success: function (data) {
2092                if (data.status === "success") {
2093                    latepoint_reload_step($booking_form_element);
2094                } else {
2095                    latepoint_show_message_inside_element(data.message, $booking_form_element.find('.os-step-existing-customer-login-w'));
2096                    latepoint_step_content_change_end(false, $booking_form_element);
2097                }
2098            }
2099        });
2100        return false;
2101    });
2102}
2103
2104function latepoint_step_content_change_start($booking_form_element) {
2105    $booking_form_element.removeClass('step-content-loaded').addClass('step-content-loading');
2106}
2107
2108// TODO
2109function latepoint_step_content_change_end(new_content, $booking_form_element) {
2110    if (new_content) $booking_form_element.find('.latepoint-body .latepoint-step-content').replaceWith(new_content);
2111    $booking_form_element.removeClass('step-content-loading').addClass('step-content-mid-loading');
2112    setTimeout(function () {
2113        $booking_form_element.removeClass('step-content-mid-loading').addClass('step-content-loaded');
2114    }, 50);
2115}
2116
2117
2118function latepoint_change_step_desc($booking_form_element, step_code) {
2119    $booking_form_element.removeClass('step-changed').addClass('step-changing');
2120    setTimeout(function () {
2121        // Progress bar
2122        var $step_progress = $booking_form_element.find('.latepoint-progress li[data-step-code="' + step_code + '"]');
2123        $step_progress.addClass('active').addClass('complete').prevAll().addClass('complete').removeClass('active');
2124        $step_progress.nextAll().removeClass('complete').removeClass('active');
2125        // Side panel
2126        var side_panel_desc = $booking_form_element.find('.latepoint-step-desc-library[data-step-code="' + step_code + '"]').html();
2127        $booking_form_element.find('.latepoint-step-desc').html(side_panel_desc);
2128
2129        // Top header
2130        var top_header_desc = $booking_form_element.find('.os-heading-text-library[data-step-code="' + step_code + '"]').html();
2131        $booking_form_element.find('.os-heading-text').html(top_header_desc);
2132        setTimeout(function () {
2133            $booking_form_element.removeClass('step-changing').addClass('step-changed');
2134        }, 50);
2135    }, 500);
2136}
2137
2138
2139function latepoint_progress_prev($booking_form_element, step_code) {
2140    var $step_progress = $booking_form_element.find('.latepoint-progress li[data-step-code="' + step_code + '"]');
2141    $step_progress.addClass('active').addClass('complete').prevAll().addClass('complete').removeClass('active');
2142    $step_progress.nextAll().removeClass('complete').removeClass('active');
2143}
2144
2145
2146function latepoint_progress_next($booking_form_element, step_code) {
2147    var $step_progress = $booking_form_element.find('.latepoint-progress li[data-step-code="' + step_code + '"]');
2148    $step_progress.addClass('active').addClass('complete').prevAll().addClass('complete').removeClass('active');
2149    $step_progress.nextAll().removeClass('complete').removeClass('active');
2150}
2151
2152
2153function latepoint_next_step_description($booking_form_element, step_code) {
2154    $booking_form_element.removeClass('step-changed').addClass('step-changing');
2155    setTimeout(function () {
2156        $booking_form_element.find('.latepoint-step-desc').html($booking_form_element.find('.latepoint-step-desc-library.active').removeClass('active').next('.latepoint-step-desc-library').addClass('active').html());
2157        $booking_form_element.find('.os-heading-text').html($booking_form_element.find('.os-heading-text-library.active').removeClass('active').next('.os-heading-text-library').addClass('active').html());
2158        setTimeout(function () {
2159            $booking_form_element.removeClass('step-changing').addClass('step-changed');
2160        }, 50);
2161    }, 500);
2162}
2163
2164function latepoint_prev_step_description($booking_form_element, step_code) {
2165    $booking_form_element.removeClass('step-changed').addClass('step-changing');
2166    setTimeout(function () {
2167        $booking_form_element.find('.latepoint-step-desc').html($booking_form_element.find('.latepoint-step-desc-library.active').removeClass('active').prev('.latepoint-step-desc-library').addClass('active').html());
2168        $booking_form_element.find('.os-heading-text').html($booking_form_element.find('.os-heading-text-library.active').removeClass('active').prev('.os-heading-text-library').addClass('active').html());
2169        setTimeout(function () {
2170            $booking_form_element.removeClass('step-changing').addClass('step-changed');
2171        }, 50);
2172    }, 500);
2173}
2174
2175
2176function latepoint_validate_fields($fields) {
2177    var is_valid = true;
2178    $fields.each(function (index) {
2179        if (jQuery(this).val() == '') {
2180            is_valid = false;
2181            return false;
2182        }
2183    });
2184    return is_valid;
2185}
2186
2187
2188async function latepoint_submit_booking_form($booking_form) {
2189    let $booking_form_element = $booking_form.closest('.latepoint-booking-form-element');
2190
2191    let current_step = $booking_form_element.find('.latepoint_current_step_code').val();
2192    let callbacks_list = [];
2193    if (latepoint_check_if_booking_form_is_final_submit($booking_form_element)) {
2194        // check if order intent is still bookable
2195        latepoint_add_action(callbacks_list, async () => {
2196            return await latepoint_check_if_order_intent_still_bookable($booking_form_element);
2197        }, 1);
2198    }
2199    $booking_form_element.trigger('latepoint:submitBookingForm', [{
2200        current_step: current_step,
2201        callbacks_list: callbacks_list,
2202        is_final_submit: latepoint_check_if_booking_form_is_final_submit($booking_form_element),
2203        direction: $booking_form_element.find('.latepoint_step_direction').val()
2204    }]);
2205    try {
2206        latepoint_hide_prev_btn($booking_form_element);
2207        await latepoint_process_list_of_callbacks(callbacks_list, $booking_form_element, $booking_form);
2208    } catch (error) {
2209        latepoint_show_prev_btn($booking_form_element);
2210        latepoint_show_error_and_stop_loading_booking_form(error, $booking_form_element);
2211        return false;
2212    }
2213
2214
2215    $booking_form_element.removeClass('step-content-loaded').addClass('step-content-loading');
2216    latepoint_hide_prev_btn($booking_form_element);
2217    try {
2218        latepoint_hide_message_inside_element($booking_form_element.find('.latepoint-body'));
2219        let response = await jQuery.ajax({
2220            type: "post",
2221            dataType: "json",
2222            processData: false,
2223            contentType: false,
2224            url: latepoint_timestamped_ajaxurl(),
2225            data: latepoint_create_form_data($booking_form)
2226        });
2227
2228        $booking_form.find('.latepoint_step_direction').val('next');
2229        if (response.status === 'success') {
2230            if (response.fields_to_update) {
2231                for (const [key, value] of Object.entries(response.fields_to_update)) {
2232                    $booking_form_element.find('input[name="' + key + '"]').val(value)
2233                }
2234            }
2235            if ($booking_form_element.data('flash-error')) {
2236                latepoint_show_message_inside_element($booking_form_element.data('flash-error'), $booking_form_element.find('.latepoint-body'));
2237                $booking_form_element.data('flash-error', '');
2238            }
2239            $booking_form_element.find('.latepoint_current_step_code').val(response.step_code);
2240            $booking_form_element.removeClass(function (index, className) {
2241                return (className.match(/(^|\s)current-step-\S+/g) || []).join(' ');
2242            }).addClass('current-step-' + response.step_code);
2243            setTimeout(function () {
2244                $booking_form_element.removeClass('step-content-loading').addClass('step-content-mid-loading');
2245                $booking_form_element.find('.latepoint-body').find('.latepoint-step-content').addClass('is-hidden');
2246                if ($booking_form_element.find('.latepoint-step-content[data-step-code="' + response.step_code + '"]')) {
2247                    $booking_form_element.find('.latepoint-step-content[data-step-code="' + response.step_code + '"]').remove();
2248                }
2249                $booking_form_element.find('.latepoint-body').append(response.message);
2250
2251
2252                latepoint_update_next_btn_label($booking_form_element);
2253                latepoint_init_step(response.step_code, $booking_form_element);
2254                setTimeout(function () {
2255                    $booking_form_element.removeClass('step-content-mid-loading').addClass('step-content-loaded');
2256                    $booking_form_element.find('.latepoint-next-btn, .latepoint-prev-btn').removeClass('os-loading');
2257                    latepoint_scroll_to_top_of_booking_form($booking_form_element);
2258                }, 50);
2259            }, 500);
2260
2261            if (response.is_pre_last_step) {
2262                $booking_form_element.data('next-submit-is-last', 'yes');
2263            } else {
2264                $booking_form_element.data('next-submit-is-last', 'no');
2265            }
2266            if (response.is_last_step) {
2267                $booking_form_element.addClass('hidden-buttons').find('.latepoint-footer').remove();
2268                $booking_form_element.find('.latepoint-progress').css('opacity', 0);
2269                $booking_form_element.closest('.latepoint-summary-is-open').removeClass('latepoint-summary-is-open');
2270                $booking_form_element.closest('.latepoint-show-side-panel').removeClass('latepoint-show-side-panel').addClass('latepoint-hide-side-panel');
2271                $booking_form_element.addClass('is-final-step');
2272            } else {
2273                if (response.show_next_btn === true) {
2274                    latepoint_show_next_btn($booking_form_element);
2275                } else {
2276                    latepoint_hide_next_btn($booking_form_element);
2277                }
2278                if (response.show_prev_btn === true) {
2279                    latepoint_show_prev_btn($booking_form_element);
2280                } else {
2281                    latepoint_hide_prev_btn($booking_form_element);
2282                }
2283            }
2284            latepoint_change_step_desc($booking_form_element, response.step_code);
2285            latepoint_reload_summary($booking_form_element);
2286        } else {
2287            if (response.send_to_step && response.send_to_step === 'resubmit') {
2288                let current_resubmit_count = parseInt($booking_form.data('resubmit-count')) ? parseInt($booking_form.data('resubmit-count')) : 1;
2289                $booking_form.data('resubmit-count', current_resubmit_count + 1);
2290                if (current_resubmit_count > 6) {
2291                    latepoint_show_message_inside_element(response.message, $booking_form_element.find('.latepoint-body'));
2292                } else {
2293                    // resubmission probably caused by order intent still being processed, since
2294                    // order intent is still processing, give it a little more time and try again
2295                    await latepoint_sleep(2000);
2296                    return latepoint_submit_booking_form($booking_form);
2297                }
2298            } else {
2299                $booking_form_element.removeClass('step-content-loading').addClass('step-content-loaded');
2300                $booking_form_element.find('.latepoint-next-btn, .latepoint-prev-btn').removeClass('os-loading');
2301                if (response.send_to_step && $booking_form_element.find('.latepoint-step-content[data-step-code="' + response.send_to_step + '"]').length) {
2302                    $booking_form_element.data('flash-error', response.message);
2303                    latepoint_reload_step($booking_form_element, response.send_to_step);
2304                } else {
2305                    latepoint_show_message_inside_element(response.message, $booking_form_element.find('.latepoint-body'));
2306                    latepoint_show_prev_btn($booking_form_element);
2307                }
2308            }
2309        }
2310    } catch (e) {
2311        console.log(e);
2312        alert('Error:' + e);
2313    }
2314}
2315
2316function latepoint_sleep(ms) {
2317    return new Promise(resolve => setTimeout(resolve, ms));
2318}
2319
2320function latepoint_show_error_and_stop_loading_booking_form(error, $booking_form_element) {
2321    if (error.send_to_step && $booking_form_element.find('.latepoint-step-content[data-step-code="' + error.send_to_step + '"]').length) {
2322        latepoint_reload_step($booking_form_element, error.send_to_step);
2323        $booking_form_element.data('flash-error', error.message);
2324    } else {
2325        latepoint_show_message_inside_element(error.message, $booking_form_element.find('.latepoint-body'), 'error');
2326
2327        if ($booking_form_element.hasClass('step-content-loading')) $booking_form_element.removeClass('step-content-loading').addClass('step-content-loaded');
2328        $booking_form_element.find('.latepoint-next-btn').removeClass('os-loading');
2329
2330        // if previous step exists - show prev button
2331        if ($booking_form_element.find('.latepoint-step-content:last-child').prev('.latepoint-step-content').length) latepoint_show_prev_btn($booking_form_element);
2332        latepoint_scroll_to_top_of_booking_form($booking_form_element);
2333    }
2334}
2335
2336function latepoint_reset_active_cart_item($booking_form_element) {
2337    $booking_form_element.find('input[name="active_cart_item[id]"]').val('');
2338    $booking_form_element.find('input[name="active_cart_item[variant]"]').val('');
2339    $booking_form_element.find('input[name="active_cart_item[item_data]"]').val('');
2340}
2341
2342function latepoint_check_if_booking_form_is_final_submit($booking_form_element) {
2343    return ($booking_form_element.data('next-submit-is-last') == 'yes');
2344}
2345
2346
2347async function latepoint_check_if_order_intent_still_bookable($booking_form_element) {
2348    let response = await jQuery.ajax({
2349        type: "post",
2350        dataType: "json",
2351        processData: false,
2352        contentType: false,
2353        url: latepoint_timestamped_ajaxurl(),
2354        data: latepoint_create_form_data($booking_form_element.find('.latepoint-form'), latepoint_helper.check_order_intent_bookable_route)
2355    });
2356    if (response.status === 'success') {
2357        return true;
2358    } else {
2359        throw new Error(response.message);
2360    }
2361}
2362
2363async function latepoint_process_list_of_callbacks(callbacks, $booking_form_element, $booking_form) {
2364    for (const callback of callbacks) {
2365        await callback.action();
2366    }
2367}
2368
2369function latepoint_clear_presets($booking_form_element) {
2370    $booking_form_element.find('.clear_for_new_item').val('');
2371}
2372
2373function latepoint_init_booking_form($booking_form_element) {
2374    $booking_form_element.on('click keydown', '.checkout-from-summary-panel-btn', function (event) {
2375        if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
2376        latepoint_reload_step($booking_form_element, jQuery(this).data('step'));
2377        jQuery(this).closest('.latepoint-w').removeClass('show-summary-on-mobile');
2378        return false;
2379    });
2380
2381    $booking_form_element.on('click keydown', '.latepoint-add-another-item-trigger', function (event) {
2382        if(event.type === 'keydown' && event.key !== ' ' &&  event.key !== 'Enter') return;
2383
2384        if (latepoint_helper.reset_presets_when_adding_new_item){
2385            latepoint_clear_presets($booking_form_element);
2386        }
2387        latepoint_reset_active_cart_item($booking_form_element);
2388        latepoint_reload_step($booking_form_element, jQuery(this).data('step'));
2389        return false;
2390    });
2391    $booking_form_element.find('.latepoint-form').on('submit', function (e) {
2392        e.preventDefault();
2393        let $booking_form = jQuery(this);
2394        latepoint_submit_booking_form($booking_form);
2395    });
2396
2397    latepoint_init_booking_summary_panel($booking_form_element);
2398
2399    $booking_form_element.on('click keydown', '.latepoint-lightbox-summary-trigger', function (event) {
2400        event.preventDefault();
2401        if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
2402        let $wrapper = jQuery(this).closest('.latepoint-w');
2403        $wrapper.toggleClass('show-summary-on-mobile');
2404        return false;
2405    });
2406
2407    $booking_form_element.find('.latepoint-lightbox-close').on('click', function () {
2408
2409        let params = new URLSearchParams(location.search);
2410        if (params.has('latepoint_order_intent_key')) {
2411            params.delete('latepoint_order_intent_key');
2412            history.replaceState(null, '', '?' + params + location.hash);
2413        }
2414
2415        jQuery('body').removeClass('latepoint-lightbox-active');
2416        jQuery('.latepoint-lightbox-w').remove();
2417        return false;
2418    });
2419
2420
2421    $booking_form_element.on('click keydown', '.lp-option', function (event) {
2422        if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
2423        jQuery(this).closest('.lp-options').find('.lp-option.selected').removeClass('selected');
2424        jQuery(this).addClass('selected');
2425    });
2426
2427
2428    // Next Step button Click
2429    $booking_form_element.find('.latepoint-next-btn').on('click', async function (e) {
2430        e.preventDefault();
2431        if (jQuery(this).hasClass('disabled') || jQuery(this).hasClass('os-loading')) return false;
2432        var $next_btn = jQuery(this);
2433        $next_btn.addClass('os-loading');
2434        var $booking_form = jQuery(this).closest('.latepoint-form');
2435
2436        var current_step = $booking_form_element.find('.latepoint_current_step_code').val();
2437
2438        $booking_form.find('.latepoint_step_direction').val('next');
2439        var callbacks_list = [];
2440
2441        $booking_form_element.trigger('latepoint:nextStepClicked', [{
2442            current_step: current_step,
2443            callbacks_list: callbacks_list
2444        }]);
2445        latepoint_hide_prev_btn($booking_form_element);
2446
2447        try {
2448            await latepoint_process_list_of_callbacks(callbacks_list, $booking_form_element, $booking_form);
2449            await latepoint_submit_booking_form($booking_form);
2450        } catch (error) {
2451            latepoint_show_error_and_stop_loading_booking_form(error, $booking_form_element);
2452        }
2453        return false;
2454    });
2455
2456
2457    // Previous Step button Click
2458    $booking_form_element.find('.latepoint-prev-btn').on('click', function (e) {
2459        if (jQuery(this).hasClass('disabled') || jQuery(this).hasClass('os-loading')) return false;
2460
2461        let $current_step = $booking_form_element.find('.latepoint-step-content:last-child');
2462
2463
2464        // handle categories
2465        if ($current_step.hasClass('selecting-item-category')) {
2466            if ($current_step.find('.os-item-category-w .os-item-category-w.selected').length) {
2467                $current_step.find('.os-item-category-w .os-item-category-w.selected').parents('.os-item-category-w').addClass('selected').find('.os-item-category-w.selected').removeClass('selected');
2468            } else {
2469                $current_step.removeClass('selecting-item-category').find('.os-item-category-w.selected').removeClass('selected');
2470                $current_step.removeClass('selecting-item-category').find('.os-item-categories-holder.show-selected-only').removeClass('show-selected-only');
2471            }
2472            if (($booking_form_element.find('.latepoint-step-content').length <= 1) && !$current_step.hasClass('selecting-item-category')) {
2473                latepoint_hide_prev_btn($booking_form_element);
2474            }
2475            latepoint_reload_summary($booking_form_element);
2476            return false;
2477        }
2478
2479        if ($current_step.data('clear-action')) {
2480            window[$current_step.data('clear-action')]($booking_form_element);
2481        }
2482
2483        let $back_btn = jQuery(this);
2484        $back_btn.addClass('os-loading');
2485        $booking_form_element.removeClass('step-content-loaded').addClass('step-content-loading');
2486        let $new_current_step = $booking_form_element.find('.latepoint-step-content.is-hidden').last();
2487        let new_current_step_code = $new_current_step.data('step-code');
2488        let current_step_code = $current_step.data('step-code');
2489
2490
2491        let current_parent_code_name = current_step_code.split('__')[0];
2492        let new_parent_code_name = new_current_step_code.split('__')[0];
2493
2494        let active_cart_item_id = $booking_form_element.find('input[name="active_cart_item[id]"]').val();
2495
2496        latepoint_change_step_desc($booking_form_element, new_current_step_code);
2497        setTimeout(function () {
2498            $new_current_step.removeClass('is-hidden');
2499            $current_step.remove();
2500            $booking_form_element.find('.latepoint_current_step_code').val(new_current_step_code);
2501            $booking_form_element.removeClass(function (index, className) {
2502                return (className.match(/(^|\s)current-step-\S+/g) || []).join(' ');
2503            }).addClass('current-step-' + new_current_step_code);
2504            $booking_form_element.find('.latepoint-next-btn span').text($booking_form_element.find('.latepoint-next-btn').data('label'));
2505            $booking_form_element.data('next-submit-is-last', 'no');
2506
2507            latepoint_update_next_btn_label($booking_form_element);
2508            latepoint_show_next_btn($booking_form_element);
2509            $back_btn.removeClass('os-loading');
2510            if ($booking_form_element.find('.latepoint-step-content').length <= 1) {
2511                if ($new_current_step.hasClass('selecting-item-category')) {
2512
2513                }
2514                if (new_current_step_code == 'booking__services') {
2515                    var $services_step = $booking_form_element.find('.step-services-w');
2516                    if ($services_step.hasClass('selecting-item-category')) {
2517                        if ($services_step.find('.os-services > .os-item.selected').hasClass('is-preselected')) {
2518                            // if service is preselected check if there are both multiple durations and quantity selector and only then show prev button
2519                        } else {
2520                            latepoint_show_prev_btn($booking_form_element);
2521                        }
2522                    } else {
2523                        latepoint_hide_prev_btn($booking_form_element);
2524                    }
2525                } else {
2526                    if (!$new_current_step.hasClass('selecting-item-category')) {
2527                        latepoint_hide_prev_btn($booking_form_element);
2528                    }
2529                }
2530            }
2531            $booking_form_element.removeClass('step-content-loading').addClass('step-content-mid-loading');
2532
2533
2534            if (new_parent_code_name == 'booking' && current_parent_code_name != 'booking' && active_cart_item_id) {
2535
2536                // we are going back to one of the steps of a booking process, we need to remove the item that was just added to the cart and start over
2537                $booking_form_element.find('.latepoint-summary-w').addClass('os-loading');
2538                let data = {
2539                    action: latepoint_helper.route_action,
2540                    route_name: latepoint_helper.remove_cart_item_route,
2541                    params: jQuery.param({cart_item_id: active_cart_item_id}),
2542                    layout: 'none',
2543                    return_format: 'json'
2544                }
2545                jQuery.ajax({
2546                    type: "post",
2547                    dataType: "json",
2548                    url: latepoint_timestamped_ajaxurl(),
2549                    data: data,
2550                    success: function (data) {
2551                        if (data.status === "success") {
2552                            $booking_form_element.find('input[name="active_cart_item[id]"]').val('');
2553                            if ($booking_form_element.find('input[name="active_cart_item[variant]"]').val() == 'bundle') {
2554                                latepoint_update_active_cart_item_item_data($booking_form_element, 'bundle_id', '');
2555                                $booking_form_element.find('input[name="active_cart_item[variant]"]').val('');
2556                            }
2557                            latepoint_reload_summary($booking_form_element);
2558                        } else {
2559                            $booking_form_element.find('.latepoint-summary-w').removeClass('os-loading');
2560                            latepoint_show_message_inside_element
2560(data.message, $booking_form_element.find('.latepoint-body'), 'error');
2561                        }
2562                    }
2563                });
2564            } else {
2565                latepoint_reload_summary($booking_form_element);
2566            }
2567            setTimeout(function () {
2568                $booking_form_element.removeClass('step-content-mid-loading').addClass('step-content-loaded');
2569                latepoint_hide_message_inside_element($booking_form_element.find('.latepoint-body'));
2570                latepoint_scroll_to_top_of_booking_form($booking_form_element);
2571            }, 150);
2572        }, 700);
2573        return false;
2574    });
2575
2576    latepoint_init_agent_details_link($booking_form_element);
2577    $booking_form_element.trigger('latepoint:initBookingForm');
2578}
2579
2580
2581function latepoint_init_booking_form_by_trigger($trigger) {
2582    let route = latepoint_helper.booking_button_route;
2583    let params = {};
2584    let restrictions = {};
2585    let presets = {};
2586    let booking_element_styles = {};
2587    if ($trigger.data('show-service-categories')) restrictions.show_service_categories = $trigger.data('show-service-categories');
2588    if ($trigger.data('show-locations')) restrictions.show_locations = $trigger.data('show-locations');
2589    if ($trigger.data('show-services')) restrictions.show_services = $trigger.data('show-services');
2590    if ($trigger.data('show-agents')) restrictions.show_agents = $trigger.data('show-agents');
2591    if ($trigger.data('calendar-start-date')) restrictions.calendar_start_date = $trigger.data('calendar-start-date');
2592
2593    if ($trigger.data('selected-location')) presets.selected_location = $trigger.data('selected-location');
2594    if ($trigger.data('selected-agent')) presets.selected_agent = $trigger.data('selected-agent');
2595    if ($trigger.data('selected-service')) presets.selected_service = $trigger.data('selected-service');
2596    if ($trigger.data('selected-bundle')) presets.selected_bundle = $trigger.data('selected-bundle');
2597    if ($trigger.data('selected-duration')) presets.selected_duration = $trigger.data('selected-duration');
2598    if ($trigger.data('selected-total-attendees')) presets.selected_total_attendees = $trigger.data('selected-total-attendees');
2599    if ($trigger.data('selected-service-category')) presets.selected_service_category = $trigger.data('selected-service-category');
2600    if ($trigger.data('selected-start-date')) presets.selected_start_date = $trigger.data('selected-start-date');
2601    if ($trigger.data('selected-start-time')) presets.selected_start_time = $trigger.data('selected-start-time');
2602    if ($trigger.data('order-item-id')) presets.order_item_id = $trigger.data('order-item-id');
2603    if ($trigger.data('source-id')) presets.source_id = $trigger.data('source-id');
2604
2605    if ($trigger.data('hide-summary') == 'yes') booking_element_styles.hide_summary = true;
2606    if ($trigger.data('hide-side-panel') == 'yes') booking_element_styles.hide_side_panel = true;
2607
2608
2609    if (jQuery.isEmptyObject(restrictions) == false) params.restrictions = restrictions;
2610    if (jQuery.isEmptyObject(presets) == false) params.presets = presets;
2611    if (jQuery.isEmptyObject(booking_element_styles) == false) params.booking_element_styles = booking_element_styles;
2612
2613    let data = {
2614        action: latepoint_helper.route_action,
2615        route_name: route,
2616        params: params,
2617        layout: 'none',
2618        return_format: 'json'
2619    }
2620
2621    let is_inline_form = $trigger.hasClass('latepoint-book-form-wrapper');
2622    if (is_inline_form) {
2623        data.params.booking_element_type = 'inline_form';
2624    }
2625
2626    $trigger.addClass('os-loading');
2627    jQuery.ajax({
2628        type: "post",
2629        dataType: "json",
2630        url: latepoint_timestamped_ajaxurl(),
2631        data: data,
2632        success: (data) => {
2633            if (data.status === "success") {
2634                let $booking_form_element = false;
2635                if (is_inline_form) {
2636                    $trigger.html(data.message);
2637                    $booking_form_element = $trigger.find('.latepoint-booking-form-element');
2638                } else {
2639                    let lightbox_class = 'booking-form-in-lightbox';
2640                    latepoint_show_data_in_lightbox(data.message, lightbox_class, false);
2641                    $booking_form_element = jQuery('.latepoint-lightbox-w .latepoint-booking-form-element');
2642                    jQuery('body').addClass('latepoint-lightbox-active');
2643                }
2644                latepoint_init_booking_form($booking_form_element);
2645                latepoint_init_step(data.step, $booking_form_element);
2646                $trigger.removeClass('os-loading');
2647            } else {
2648                $trigger.removeClass('os-loading');
2649                // console.log(data.message);
2650            }
2651        }
2652    });
2653}
2654
2655
2656/*
2657 * Copyright (c) 2023 LatePoint LLC. All rights reserved.
2658 */
2659
2660async function latepoint_init_transaction_payment_form() {
2661    let callbacks_list = [];
2662    let $transaction_payment_form = jQuery('.latepoint-transaction-payment-form');
2663    let current_step = $transaction_payment_form.find('input[name="current_step"]').val();
2664
2665    $transaction_payment_form.on('click keydown', '.lp-option', (e) => {
2666        let $option = jQuery(e.currentTarget);
2667        if(e.type === 'keydown' && e.key !== ' ' &&  e.key !== 'Enter') return;
2668        $option.closest('.lp-options').find('.lp-option.selected').removeClass('selected');
2669        $option.addClass('selected');
2670        $transaction_payment_form.find('input[name="' + $option.data('holder') + '"]').val($option.data('value'));
2671        $transaction_payment_form.trigger('submit');
2672        return false;
2673    });
2674
2675
2676    switch (current_step) {
2677        case 'methods':
2678            break;
2679        case 'processors':
2680            break;
2681        case 'pay':
2682            $transaction_payment_form.trigger('latepoint:initOrderPaymentMethod', [{
2683                callbacks_list: callbacks_list,
2684                payment_method: $transaction_payment_form.find('input[name="payment_method"]').val(),
2685                payment_processor: $transaction_payment_form.find('input[name="payment_processor"]').val(),
2686            }]);
2687            $transaction_payment_form.addClass('os-loading');
2688
2689            try {
2690                for (const callback of callbacks_list) {
2691                    await callback.action();
2692                }
2693                $transaction_payment_form.removeClass('os-loading');
2694            } catch (error) {
2695                latepoint_show_error_and_stop_loading_booking_form(error, $transaction_payment_form);
2696            }
2697            break;
2698        case 'confirmation':
2699            break;
2700    }
2701
2702    $transaction_payment_form.on('submit', async function (e) {
2703        e.preventDefault();
2704        await latepoint_submit_transaction_payment_form(jQuery(e.target));
2705    });
2706}
2707
2708async function latepoint_submit_transaction_payment_form($transaction_payment_form) {
2709
2710    if($transaction_payment_form.hasClass('os-loading')) return false;
2711    let callbacks_list = [];
2712
2713    $transaction_payment_form.find('.latepoint-message').remove();
2714    $transaction_payment_form.addClass('os-loading');
2715    $transaction_payment_form.find('.latepoint-btn').addClass('os-loading');
2716
2717    $transaction_payment_form.trigger('latepoint:submitTransactionPaymentForm', [{
2718        callbacks_list: callbacks_list,
2719        payment_method: $transaction_payment_form.find('input[name="payment_method"]').val(),
2720        payment_processor: $transaction_payment_form.find('input[name="payment_processor"]').val(),
2721        current_step: $transaction_payment_form.find('input[name="current_step"]').val(),
2722    }]);
2723
2724    try {
2725        for (const callback of callbacks_list) {
2726            await callback.action();
2727        }
2728    } catch (error) {
2729        $transaction_payment_form.removeClass('os-loading').find('.os-loading').removeClass('os-loading');
2730        latepoint_show_message_inside_element(error.message, $transaction_payment_form.find('.lp-payment-method-content'), 'error');
2731        return false;
2732    }
2733
2734
2735    try {
2736        let response = await jQuery.ajax({
2737            type: "post",
2738            dataType: "json",
2739            processData: false,
2740            contentType: false,
2741            url: latepoint_timestamped_ajaxurl(),
2742            data: latepoint_create_form_data($transaction_payment_form, latepoint_helper.invoices_payment_form_route)
2743        });
2744
2745        $transaction_payment_form.removeClass('os-loading').find('.os-loading').removeClass('os-loading');
2746
2747        if (response.status === 'success') {
2748            $transaction_payment_form.html(response.message);
2749            return await latepoint_init_transaction_payment_form();
2750
2751        } else {
2752            latepoint_show_message_inside_element(response.message, $transaction_payment_form.find('.lp-payment-method-content'), 'error');
2753            return false;
2754        }
2755    } catch (e) {
2756
2757        $transaction_payment_form.removeClass('os-loading').find('.os-loading').removeClass('os-loading');
2758        console.log(e);
2759        alert('Error:' + e);
2760    }
2761
2762}
2763
2764function latepoint_hide_reschedule_button() {
2765    jQuery('.reschedule-confirmation-button-wrapper').hide();
2766}
2767
2768function latepoint_show_reschedule_button() {
2769    jQuery('.reschedule-confirmation-button-wrapper').show();
2770}
2771
2772function latepoint_customer_cabinet_reload_booking_tile($booking_tile) {
2773    $booking_tile.addClass('os-loading');
2774    let params = {
2775        booking_id: $booking_tile.data('id'),
2776    }
2777    let data = {
2778        action: latepoint_helper.route_action,
2779        route_name: $booking_tile.data('route-name'),
2780        params: params,
2781        layout: 'none',
2782        return_format: 'json'
2783    };
2784    jQuery.ajax({
2785        type: "post",
2786        dataType: "json",
2787        url: latepoint_timestamped_ajaxurl(),
2788        data: data,
2789        success: function (data) {
2790            $booking_tile.removeClass('os-loading')
2791            if (data.status === "success") {
2792                $booking_tile.replaceWith(data.message);
2793            } else {
2794                alert(data.message);
2795            }
2796        }
2797    });
2798}
2799
2800// show invoices -> summary_before_payment in modal
2801function show_summary_before_payment($access_key) {
2802    let data = {
2803        action: latepoint_helper.route_action,
2804        route_name: latepoint_helper.invoices_summary_before_payment_route,
2805        params: {key: $access_key},
2806        layout: 'none',
2807        return_format: 'json'
2808    }
2809
2810    jQuery.ajax({
2811        type: "post",
2812        dataType: "json",
2813        url: latepoint_timestamped_ajaxurl(),
2814        data: data,
2815        success: function (data) {
2816            latepoint_show_data_in_lightbox(data.message, 'width-500');
2817        }
2818    });
2819}
2820
2821function latepoint_reload_reschedule_calendar($reschedule_calendar_wrapper) {
2822    $reschedule_calendar_wrapper.addClass('os-loading');
2823    let $calendar_element = $reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active').last();
2824    let calendar_year = $calendar_element.data('calendar-year');
2825    let calendar_month = $calendar_element.data('calendar-month');
2826
2827    let data = {
2828        action: latepoint_helper.route_action,
2829        route_name: $reschedule_calendar_wrapper.data('route'),
2830        params: {
2831            timezone_name: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_timezone_name').val(),
2832            key: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_manage_booking_key').val(),
2833            target_date_string: `${calendar_year}-${calendar_month}-1`,
2834            booking_id: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_booking_id').val()
2835        },
2836        layout: 'none',
2837        return_format: 'json'
2838    }
2839    jQuery.ajax({
2840        type: "post",
2841        dataType: "json",
2842        url: latepoint_timestamped_ajaxurl(),
2843        data: data,
2844        success: function (data) {
2845            $reschedule_calendar_wrapper.removeClass('os-loading');
2846            if (data.status === "success") {
2847                jQuery('.latepoint-lightbox-i').html(data.message);
2848            } else {
2849                // console.log(data.message);
2850            }
2851        }
2852    });
2853    latepoint_calendar_show_or_hide_prev_next_buttons($reschedule_calendar_wrapper);
2854    return false;
2855}
2856
2857function latepoint_init_reschedule() {
2858
2859    let $reschedule_wrapper = jQuery('.reschedule-calendar-wrapper');
2860
2861    latepoint_init_timezone_picker($reschedule_wrapper);
2862
2863    $reschedule_wrapper.on('click', '.latepoint-request-reschedule-trigger', function () {
2864        let $trigger = jQuery(this)
2865        let $wrapper = $trigger.closest('.reschedule-calendar-wrapper')
2866        let booking_id = $wrapper.find('input[type="hidden"].latepoint_booking_id').val()
2867
2868        $trigger.addClass('os-loading')
2869        let params = {
2870            booking_id: booking_id,
2871            key: $wrapper.find('input[type="hidden"].latepoint_manage_booking_key').val(),
2872            start_date: $wrapper.find('input[type="hidden"].latepoint_start_date').val(),
2873            start_time: $wrapper.find('input[type="hidden"].latepoint_start_time').val(),
2874            timezone_name: $wrapper.find('input[type="hidden"].latepoint_timezone_name').val(),
2875        }
2876        let data = {
2877            action: latepoint_helper.route_action,
2878            route_name: $trigger.data('route-name'),
2879            params: params,
2880            layout: 'none',
2881            return_format: 'json'
2882        };
2883        jQuery.ajax({
2884            type: "post",
2885            dataType: "json",
2886            url: latepoint_timestamped_ajaxurl(),
2887            data: data,
2888            success: function (data) {
2889                $trigger.removeClass('os-loading')
2890                if (data.status === "success") {
2891                    jQuery('.latepoint-lightbox-content').html(data.message);
2892                    jQuery('.latepoint-lightbox-footer, .latepoint-lightbox-heading').remove();
2893                    if (jQuery('.customer-bookings-tiles').length) {
2894                        // called from customer cabinet
2895                        latepoint_customer_cabinet_reload_booking_tile(jQuery('.customer-bookings-tiles .customer-booking[data-id="' + booking_id + '"]'));
2896                    } else {
2897                        // called from manage by key
2898                        latepoint_manage_by_key_reload_booking();
2899                    }
2900                } else {
2901                    latepoint_show_message_inside_element(data.message, jQuery('.latepoint-lightbox-content'), 'error');
2902                    jQuery('.latepoint-lightbox-content').animate({scrollTop: 0}, 300);
2903                }
2904            }
2905        });
2906        return false;
2907    });
2908
2909    $reschedule_wrapper.on('click keydown', '.dp-timepicker-trigger', function (event) {
2910        if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
2911        var $reschedule_calendar_wrapper = jQuery(this).closest('.reschedule-calendar-wrapper');
2912        if (jQuery(this).hasClass('is-booked') || jQuery(this).hasClass('is-off')) {
2913            // Show error message that you cant select a booked period
2914        } else {
2915            if (jQuery(this).hasClass('selected')) {
2916                jQuery(this).removeClass('selected');
2917                jQuery(this).find('.dp-success-label').remove();
2918                $reschedule_calendar_wrapper.find('.latepoint_start_time').val('');
2919                latepoint_hide_reschedule_button();
2920            } else {
2921                $reschedule_calendar_wrapper.find('.dp-timepicker-trigger.selected').removeClass('selected').find('.dp-success-label').remove();
2922                var selected_timeslot_time = jQuery(this).find('.dp-label-time').html();
2923                jQuery(this).addClass('selected').find('.dp-label').prepend('<span class="dp-success-label">' + latepoint_helper.datepicker_timeslot_selected_label + '</span>');
2924
2925                var minutes = parseInt(jQuery(this).data('minutes'));
2926                var start_date = new Date($reschedule_calendar_wrapper.find('.os-day.selected').data('date'));
2927
2928                $reschedule_calendar_wrapper.find('.latepoint_start_date').val(start_date.toISOString().split('T')[0])
2929                $reschedule_calendar_wrapper.find('.latepoint_start_time').val(minutes);
2930                latepoint_show_reschedule_button();
2931            }
2932        }
2933        return false;
2934    });
2935
2936
2937    $reschedule_wrapper.on('click', '.os-month-next-btn', function () {
2938        var $reschedule_calendar_wrapper = jQuery(this).closest('.reschedule-calendar-wrapper');
2939        var next_month_route_name = jQuery(this).data('route');
2940        if ($reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active + .os-monthly-calendar-days-w').length) {
2941            $reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active').removeClass('active').next('.os-monthly-calendar-days-w').addClass('active');
2942            latepoint_calendar_set_month_label($reschedule_calendar_wrapper);
2943        } else {
2944            // TODO add condition to check maximum number months to call into the future
2945            if (true) {
2946                var $btn = jQuery(this);
2947                $btn.addClass('os-loading');
2948                var $calendar_element = $reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w').last();
2949                var calendar_year = $calendar_element.data('calendar-year');
2950                var calendar_month = $calendar_element.data('calendar-month');
2951                if (calendar_month == 12) {
2952                    calendar_year = calendar_year + 1;
2953                    calendar_month = 1;
2954                } else {
2955                    calendar_month = calendar_month + 1;
2956                }
2957                var data = {
2958                    action: latepoint_helper.route_action,
2959                    route_name: next_month_route_name,
2960                    params: {
2961                        timezone_name: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_timezone_name').val(),
2962                        key: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_manage_booking_key').val(),
2963                        target_date_string: `${calendar_year}-${calendar_month}-1`,
2964                        booking: {
2965                            id: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_booking_id').val()
2966                        }
2967                    },
2968                    layout: 'none',
2969                    return_format: 'json'
2970                }
2971                jQuery.ajax({
2972                    type: "post",
2973                    dataType: "json",
2974                    url: latepoint_timestamped_ajaxurl(),
2975                    data: data,
2976                    success: function (data) {
2977                        $btn.removeClass('os-loading');
2978                        if (data.status === "success") {
2979                            $reschedule_calendar_wrapper.find('.os-months').append(data.message);
2980                            $reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active').removeClass('active').next('.os-monthly-calendar-days-w').addClass('active');
2981                            latepoint_calendar_set_month_label($reschedule_calendar_wrapper);
2982                        } else {
2983                            // console.log(data.message);
2984                        }
2985                    }
2986                });
2987            }
2988        }
2989        latepoint_calendar_show_or_hide_prev_next_buttons($reschedule_calendar_wrapper);
2990        return false;
2991    });
2992
2993    $reschedule_wrapper.on('click', '.os-month-prev-btn', function () {
2994        var $reschedule_calendar_wrapper = jQuery(this).closest('.reschedule-calendar-wrapper');
2995        if ($reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active').prev('.os-monthly-calendar-days-w').length) {
2996            $reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active').removeClass('active').prev('.os-monthly-calendar-days-w').addClass('active');
2997            latepoint_calendar_set_month_label($reschedule_calendar_wrapper);
2998        }
2999        latepoint_calendar_show_or_hide_prev_next_buttons($reschedule_calendar_wrapper);
3000        return false;
3001    });
3002
3003    $reschedule_wrapper.on('click', '.os-day', function () {
3004        if (jQuery(this).hasClass('os-day-passed')) return false;
3005        if (jQuery(this).hasClass('os-not-in-allowed-period')) return false;
3006        if(jQuery(this).closest('.os-dates-and-times-w').hasClass('calendar-style-modern')){
3007            if(jQuery(this).hasClass('os-month-prev')) return false;
3008            if(jQuery(this).hasClass('os-month-next')) return false;
3009        }
3010        var $reschedule_calendar_wrapper = jQuery(this).closest('.reschedule-calendar-wrapper');
3011        if (jQuery(this).closest('.os-monthly-calendar-days-w').hasClass('hide-if-single-slot')) {
3012
3013            // HIDE TIMESLOT IF ONLY ONE TIMEPOINT
3014            if (jQuery(this).hasClass('os-not-available')) {
3015                // clicked on a day that has no available timeslots
3016                // do nothing
3017            } else {
3018                $reschedule_calendar_wrapper.find('.os-day.selected').removeClass('selected');
3019                jQuery(this).addClass('selected');
3020                // set date
3021                $reschedule_calendar_wrapper.find('.latepoint_start_date').val(jQuery(this).data('date'));
3022                if (jQuery(this).hasClass('os-one-slot-only')) {
3023                    // clicked on a day that has only one slot available
3024                    var bookable_minutes = jQuery(this).data('bookable-minutes').toString().split(':')[0];
3025                    var selected_timeslot_time = latepoint_format_minutes_to_time(Number(bookable_minutes), Number(jQuery(this).data('service-duration')));
3026                    $reschedule_calendar_wrapper.find('.latepoint_start_time').val(jQuery(this).data('bookable-minutes'));
3027                    $reschedule_calendar_wrapper.find('.time-selector-w').slideUp(200);
3028                    latepoint_show_reschedule_button()
3029                } else {
3030                    // regular day with more than 1 timeslots available
3031                    // build timeslots
3032                    latepoint_generate_day_timeslots(jQuery(this), $reschedule_calendar_wrapper, $reschedule_calendar_wrapper.find('.latepoint-lightbox-content'));
3033                    // initialize timeslots events
3034                    // clear time and hide next btn
3035                    $reschedule_calendar_wrapper.find('.latepoint_start_time').val('');
3036                }
3037            }
3038        } else {
3039
3040            // SHOW TIMESLOTS EVEN IF ONLY ONE TIMEPOINT
3041            $reschedule_calendar_wrapper.find('.latepoint_start_date').val(jQuery(this).data('date'));
3042            $reschedule_calendar_wrapper.find('.os-day.selected').removeClass('selected');
3043            jQuery(this).addClass('selected');
3044
3045            // build timeslots
3046            latepoint_generate_day_timeslots(jQuery(this), $reschedule_calendar_wrapper, $reschedule_calendar_wrapper.find('.latepoint-lightbox-content'));
3047            // initialize timeslots events
3048            // clear time and hide next btn
3049            let $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
3050            if ($booking_form_element.length) latepoint_reload_summary($booking_form_element);
3051            $reschedule_calendar_wrapper.find('.latepoint_start_time').val('');
3052            latepoint_hide_next_btn($reschedule_calendar_wrapper);
3053        }
3054
3055        return false;
3056    });
3057}
3058
3059
3060/*
3061 * Copyright (c) 2024 LatePoint LLC. All rights reserved.
3062 */
3063
3064function latepoint_reload_day_schedule($day_view){
3065  $day_view.addClass('os-loading');
3066  let data = {
3067    action: latepoint_helper.route_action,
3068    route_name: $day_view.data('route-name'),
3069    params: $day_view.find("select, textarea, input").serialize(),
3070    layout: 'none',
3071    return_format: 'json'
3072  }
3073
3074  jQuery.ajax({
3075    type: "post",
3076    dataType: "json",
3077    url: latepoint_timestamped_ajaxurl(),
3078    data: data,
3079    success: function (data) {
3080      if (data.status === "success") {
3081        $day_view.replaceWith(data.message);
3082      }
3083    }
3084  });
3085}
3086
3087function latepoint_reload_events_calendar($events_calendar){
3088  $events_calendar.addClass('os-loading');
3089  let data = {
3090    action: latepoint_helper.route_action,
3091    route_name: $events_calendar.data('route-name'),
3092    params: $events_calendar.find("select, textarea, input").serialize(),
3093    layout: 'none',
3094    return_format: 'json'
3095  }
3096
3097  jQuery.ajax({
3098    type: "post",
3099    dataType: "json",
3100    url: latepoint_timestamped_ajaxurl(),
3101    data: data,
3102    success: function (data) {
3103      if (data.status === "success") {
3104        $events_calendar.replaceWith(data.message);
3105      }
3106    }
3107  });
3108
3109}
3110
3111/*
3112 * Copyright (c) 2024 LatePoint LLC. All rights reserved.
3113 */
3114
3115class LatepointStripeConnectFront {
3116
3117    // Init
3118    constructor(stripeKey) {
3119        this.stripeKey = stripeKey;
3120        this.stripeElements = null;
3121        this.stripeCore = null;
3122        this.stripePaymentMethod = null;
3123        this.stripeContinueOrderIntentURL = null;
3124        this.stripeContinueTransactionIntentURL = null;
3125        this.stripePaymentIntentSecret = null;
3126        this.stripePaymentElement = null;
3127        this.ready();
3128    }
3129
3130    ready() {
3131        jQuery(document).ready(() => {
3132            jQuery('body').on('latepoint:submitBookingForm', '.latepoint-booking-form-element', (e, data) => {
3133                if (!latepoint_helper.demo_mode && data.is_final_submit && data.direction == 'next') {
3134                    let payment_method = jQuery(e.currentTarget).find('input[name="cart[payment_method]"]').val();
3135                    switch (payment_method) {
3136                        case 'payment_element':
3137                            latepoint_add_action(data.callbacks_list, async () => {
3138                                if (this.stripePaymentIntentSecret && this.stripeCore) {
3139                                    return await this.confirmPaymentElementPayment(jQuery(e.currentTarget));
3140                                }
3141                            });
3142                            break;
3143                    }
3144                }
3145            });
3146            jQuery('body').on('latepoint:submitTransactionPaymentForm', '.latepoint-transaction-payment-form', (e, data) => {
3147                if (data.current_step === 'pay' && data.payment_processor === 'stripe_connect' && data.payment_method === 'payment_element') {
3148                    latepoint_add_action(data.callbacks_list, async () => {
3149                        if (this.stripePaymentIntentSecret && this.stripeCore) {
3150                            return await this.confirmPaymentElementPaymentForTransaction(jQuery(e.currentTarget));
3151                        }
3152                    });
3153                }
3154            });
3155
3156            // INITIALIZE PAYMENT METHOD
3157            jQuery('body').on('latepoint:initPaymentMethod', '.latepoint-booking-form-element', (e, data) => {
3158                if (!latepoint_helper.demo_mode) {
3159                    switch (data.payment_method) {
3160                        case 'payment_element':
3161                            latepoint_add_action(data.callbacks_list, async () => {
3162                                return await this.createPaymentIntent(jQuery(e.currentTarget), data.payment_method);
3163                            });
3164                            break;
3165                    }
3166                } else {
3167                    latepoint_show_next_btn(jQuery(e.currentTarget));
3168                }
3169            });
3170            // INITIALIZE PAYMENT METHOD on order payment form
3171            jQuery('body').on('latepoint:initOrderPaymentMethod', '.latepoint-transaction-payment-form', (e, data) => {
3172                if (data.payment_processor === 'stripe_connect') {
3173                    switch (data.payment_method) {
3174                        case 'payment_element':
3175                            latepoint_add_action(data.callbacks_list, async () => {
3176                                return await this.createPaymentIntentForTransaction(jQuery(e.currentTarget));
3177                            });
3178                            break;
3179                    }
3180                }
3181            });
3182        });
3183    }
3184
3185
3186    async createPaymentIntentForTransaction($transaction_intent_form) {
3187
3188        try {
3189            this.stripeCore = Stripe(this.stripeKey, {stripeAccount: latepoint_helper.stripe_connected_account_id});
3190            this.stripeElements = this.stripeCore.elements();
3191        }catch(e){
3192            console.log(e);
3193            alert(e);
3194        }
3195
3196        let data = latepoint_create_form_data($transaction_intent_form, latepoint_helper.stripe_connect_route_create_payment_intent_for_transaction_intent);
3197
3198        let response = await jQuery.ajax({
3199            type: "post",
3200            dataType: "json",
3201            processData: false,
3202            contentType: false,
3203            url: latepoint_timestamped_ajaxurl(),
3204            data: data
3205        });
3206
3207        if (response.status === "success") {
3208            $transaction_intent_form.find('input[name="payment_token"]').val(response.payment_intent_id);
3209            this.stripePaymentIntentSecret = response.payment_intent_secret;
3210            this.stripeContinueTransactionIntentURL = response.continue_transaction_intent_url;
3211            latepoint_show_next_btn($transaction_intent_form);
3212
3213            if ($transaction_intent_form.find('.stripe-payment-element').length) {
3214                return this.initPaymentElement($transaction_intent_form);
3215            }
3216        } else {
3217            alert(response.message);
3218            throw new Error(response.message);
3219        }
3220    }
3221
3222    async createPaymentIntent($booking_form_element, payment_method) {
3223        this.stripeCore = Stripe(this.stripeKey, {stripeAccount: latepoint_helper.stripe_connected_account_id});
3224        this.stripeElements = this.stripeCore.elements();
3225
3226        let data = latepoint_create_form_data($booking_form_element.find('.latepoint-form'), latepoint_helper.stripe_connect_route_create_payment_intent, {booking_form_page_url: window.location.href});
3227
3228        let response = await jQuery.ajax({
3229            type: "post",
3230            dataType: "json",
3231            processData: false,
3232            contentType: false,
3233            url: latepoint_timestamped_ajaxurl(),
3234            data: data
3235        });
3236
3237        if (response.status === "success") {
3238            $booking_form_element.find('input[name="cart[payment_token]"]').val(response.payment_intent_id);
3239            this.stripePaymentIntentSecret = response.payment_intent_secret;
3240            this.stripeContinueOrderIntentURL = response.continue_order_intent_url;
3241            latepoint_show_next_btn($booking_form_element);
3242
3243            if ($booking_form_element.find('.stripe-payment-element').length) {
3244                return this.initPaymentElement($booking_form_element);
3245            }
3246        } else {
3247            alert(response.message);
3248            throw new Error(response.message);
3249        }
3250    }
3251
3252    async confirmPaymentElementPaymentForTransaction($transaction_intent_form) {
3253        let elements = this.stripeElements;
3254        let continue_transaction_intent_url = this.stripeContinueTransactionIntentURL;
3255        let result = await this.stripeCore.confirmPayment({
3256            elements,
3257            confirmParams: {
3258                // Return URL where the customer should be redirected after the PaymentIntent is confirmed.
3259                return_url: continue_transaction_intent_url,
3260            },
3261            redirect: "if_required",
3262        });
3263        if (result.error) {
3264            throw new Error(result.error.message);
3265        } else {
3266            $transaction_intent_form.find('input[name="payment_token"]').val(result.paymentIntent.i
3266d);
3267            return result.paymentIntent.id;
3268        }
3269    }
3270
3271    async confirmPaymentElementPayment($booking_form_element) {
3272        let elements = this.stripeElements;
3273        let continue_order_intent_url = this.stripeContinueOrderIntentURL;
3274        let result = await this.stripeCore.confirmPayment({
3275            elements,
3276            confirmParams: {
3277                // Return URL where the customer should be redirected after the PaymentIntent is confirmed.
3278                return_url: continue_order_intent_url,
3279            },
3280            redirect: "if_required",
3281        });
3282        if (result.error) {
3283            throw new Error(result.error.message);
3284        } else {
3285            $booking_form_element.find('input[name="cart[payment_token]"]').val(result.paymentIntent.id);
3286            return result.paymentIntent.id;
3287        }
3288    }
3289
3290    initPaymentElement($booking_form_element) {
3291        let appearance = {
3292            theme: 'stripe',
3293            variables: {
3294                fontFamily: 'Overpass',
3295                colorPrimary: '#1d7bff'
3296            },
3297            rules: {
3298                '.Tab': {
3299                    border: '1px solid #E0E6EB',
3300                    boxShadow: 'none',
3301                    borderRadius: '0',
3302                    marginBottom: '10px'
3303                },
3304                '.Input': {
3305                    boxShadow: 'none',
3306                    borderRadius: '0'
3307                },
3308
3309                '.Tab:hover': {
3310                    color: 'var(--colorText)',
3311                },
3312
3313                '.Tab--selected': {
3314                    borderColor: 'var(--colorPrimary)',
3315                    boxShadow: `0 0 0 1px var(--colorPrimary)`,
3316                },
3317
3318                '.Input--invalid': {
3319                    boxShadow: '0 1px 1px 0 rgba(0, 0, 0, 0.07), 0 0 0 2px var(--colorPrimary)',
3320                },
3321
3322                // See all supported class names and selector syntax below
3323            }
3324        };
3325
3326
3327        // Create an instance of the Payment Element
3328        this.stripeElements = this.stripeCore.elements({
3329            clientSecret: this.stripePaymentIntentSecret,
3330            appearance,
3331            fonts: [{cssSrc: 'https://fonts.googleapis.com/css2?family=Overpass&display=swap'}],
3332        });
3333
3334        let options = {
3335            layout: {
3336                type: 'tabs',
3337                defaultCollapsed: false,
3338            },
3339        };
3340        this.stripePaymentElement = this.stripeElements.create('payment', options);
3341
3342        return this.stripePaymentElement.mount($booking_form_element.find('.stripe-payment-element')[0]);
3343    }
3344
3345
3346}
3347
3348
3349if (latepoint_helper.is_stripe_connect_enabled) window.latepointStripeConnectFront = new LatepointStripeConnectFront(latepoint_helper.stripe_connect_key);
3350
3351/*
3352 * Copyright (c) 2022 LatePoint LLC. All rights reserved.
3353 */
3354// @codekit-prepend "bin/time.js"
3355// @codekit-prepend "bin/shared.js"
3356// @codekit-prepend "bin/notifications.js";
3357// @codekit-prepend "bin/actions.js"
3358// @codekit-prepend "bin/front/main.js"
3359// @codekit-prepend "bin/front/_customer.js"
3360// @codekit-prepend "bin/front/_events.js"
3361// @codekit-prepend "bin/front/_stripe_connect.js"
3362
3363
3364// DOCUMENT READY
3365jQuery(document).ready(function ($) {
3366
3367    latepoint_init_customer_dashboard();
3368    latepoint_init_manage_booking_by_key();
3369
3370
3371    jQuery('body').on('click', '.le-filter-trigger', function () {
3372        let $events_calendar = jQuery(this).closest('.latepoint-calendar-wrapper');
3373        if ($events_calendar.hasClass('show-filters')) {
3374            $events_calendar.removeClass('show-filters');
3375            $events_calendar.find('.latepoint-calendar-filters select').val('');
3376            latepoint_reload_events_calendar($events_calendar);
3377        } else {
3378            $events_calendar.addClass('show-filters');
3379        }
3380        return false;
3381    });
3382
3383    jQuery('body').on('click', '.le-navigation-trigger', function () {
3384        let $trigger = jQuery(this);
3385        let $events_calendar = $trigger.closest('.latepoint-calendar-wrapper');
3386        $events_calendar.find('input[name="target_date_string"]').val($trigger.data('target-date'));
3387        $trigger.addClass('os-loading');
3388        latepoint_reload_events_calendar($events_calendar);
3389        return false;
3390    });
3391
3392    jQuery('body').on('change', '.le-day-filters select', function () {
3393        let $trigger = jQuery(this);
3394        let $day_view = $trigger.closest('.le-day-view-wrapper');
3395        latepoint_reload_day_schedule($day_view);
3396        return false;
3397    });
3398
3399    jQuery('body').on('change', '.latepoint-calendar-filters select, .le-range-selector select', function () {
3400        let $trigger = jQuery(this);
3401        let $events_calendar = $trigger.closest('.latepoint-calendar-wrapper');
3402        $events_calendar.find('.le-filter').addClass('os-loading');
3403        latepoint_reload_events_calendar($events_calendar);
3404        return false;
3405    });
3406
3407    jQuery('body').on('click', '.close-calendar-types', function () {
3408        jQuery(this).closest('.add-to-calendar-wrapper').removeClass('show-types');
3409        return false;
3410    });
3411    jQuery('body').on('click', '.open-calendar-types', function () {
3412        jQuery(this).closest('.add-to-calendar-wrapper').addClass('show-types');
3413        return false;
3414    });
3415
3416    jQuery('body').on('latepoint:nextStepClicked', '.latepoint-booking-form-element', (e, data) => {
3417
3418        latepoint_add_action(data.callbacks_list, async () => {
3419            let $booking_form = jQuery(e.currentTarget).find('.latepoint-form');
3420            let errors = latepoint_validate_form($booking_form);
3421            if (errors.length) {
3422                let error_messages = errors.map(error => error.message).join(', ');
3423                throw new Error(error_messages);
3424            } else {
3425                return true;
3426            }
3427        }, 1);
3428
3429    });
3430
3431    if (latepoint_helper.start_from_order_intent_key) {
3432        $('body').append('<div class="latepoint-continue-intent-loading"></div>');
3433
3434        var data = {
3435            action: latepoint_helper.route_action,
3436            route_name: latepoint_helper.start_from_order_intent_route,
3437            params: {order_intent_key: latepoint_helper.start_from_order_intent_key},
3438            layout: 'none',
3439            return_format: 'json'
3440        }
3441
3442        $.ajax({
3443            type: "post",
3444            dataType: "json",
3445            url: latepoint_timestamped_ajaxurl(),
3446            data: data,
3447            success: function (data) {
3448                $('.latepoint-continue-intent-loading').remove();
3449                if (data.status === "success") {
3450                    var lightbox_class = '';
3451                    latepoint_show_data_in_lightbox(data.message, data.lightbox_class);
3452                    var $booking_form_element = jQuery('.latepoint-lightbox-w .latepoint-booking-form-element');
3453                    latepoint_init_booking_form($booking_form_element);
3454                    $booking_form_element.find('.latepoint-step-content').addClass('is-hidden').last().removeClass('is-hidden');
3455                    if ($booking_form_element.find('.latepoint-step-content').length > 1) latepoint_show_prev_btn($booking_form_element);
3456                    var $booking_form_element = jQuery('.latepoint-lightbox-w .latepoint-booking-form-element');
3457                    $booking_form_element.find('.latepoint-step-content').each(function () {
3458                        latepoint_init_step($(this).data('step-code'), $booking_form_element);
3459                    });
3460                    $('body').addClass('latepoint-lightbox-active');
3461                } else {
3462                    // console.log(data.message);
3463                }
3464            }
3465        });
3466    }
3467  
3468    if (latepoint_helper.start_from_transaction_access_key) {
3469        const invoice_access_key = latepoint_helper.start_from_transaction_access_key;
3470        show_summary_before_payment(invoice_access_key);
3471    }
3472
3473
3474    jQuery('body').on('click', '.latepoint-lightbox-close', function () {
3475        latepoint_lightbox_close();
3476        return false;
3477    });
3478
3479
3480    jQuery('body').on('click', '.os-step-tabs .os-step-tab', function () {
3481        jQuery(this).closest('.os-step-tabs').find('.os-step-tab').removeClass('active');
3482        jQuery(this).addClass('active');
3483        var target = jQuery(this).data('target');
3484        jQuery(this).closest('.os-step-tabs-w').find('.os-step-tab-content').hide();
3485        jQuery(target).show();
3486    });
3487
3488    jQuery('body').on('keyup', '.os-form-group .os-form-control', function () {
3489        if (jQuery(this).val()) {
3490            jQuery(this).closest('.os-form-group').addClass('has-value');
3491        } else {
3492            jQuery(this).closest('.os-form-group').removeClass('has-value');
3493        }
3494    });
3495
3496    jQuery('.latepoint-tab-triggers').on('click', '.latepoint-tab-trigger', function () {
3497        var $tabs_wrapper = jQuery(this).closest('.latepoint-tabs-w')
3498        $tabs_wrapper.find('.latepoint-tab-trigger.active').removeClass('active');
3499        $tabs_wrapper.find('.latepoint-tab-content').removeClass('active');
3500        jQuery(this).addClass('active');
3501        $tabs_wrapper.find('.latepoint-tab-content' + jQuery(this).data('tab-target')).addClass('active');
3502        return false;
3503    });
3504
3505
3506    // Main Button to trigger lightbox opening
3507    if(jQuery('.latepoint-book-form-wrapper').length){
3508        jQuery('.latepoint-book-form-wrapper').each(function(){
3509            latepoint_init_booking_form_by_trigger(jQuery(this));
3510        });
3511    }
3512
3513    jQuery('body').on('click', '.latepoint-book-button, .os_trigger_booking', function () {
3514        latepoint_init_booking_form_by_trigger(jQuery(this));
3515        return false;
3516    });
3517
3518});

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.