PluginProbe ʕ •ᴥ•ʔ
LatePoint – Calendar Booking Plugin for Appointments and Events / 5.2.0
LatePoint – Calendar Booking Plugin for Appointments and Events v5.2.0
5.6.7 5.6.6 5.6.5 5.6.4 5.6.3 5.6.2 5.6.1 5.6.0 5.5.2 5.5.1 5.5.0 5.4.2 trunk 5.1.0 5.1.1 5.1.2 5.1.3 5.1.4 5.1.5 5.1.6 5.1.7 5.1.8 5.1.9 5.1.91 5.1.92 5.1.93 5.1.94 5.2.0 5.2.1 5.2.10 5.2.11 5.2.2 5.2.3 5.2.4 5.2.5 5.2.6 5.2.7 5.2.8 5.2.9 5.3.0 5.3.1 5.3.2 5.4.0 5.4.1
latepoint / public / javascripts / front.js
latepoint / public / javascripts Last commit date
vendor 1 year ago admin.js 9 months ago admin.js.map 1 year ago front.js 9 months ago front.js.map 1 year ago vendor-admin.js 1 year ago vendor-front.js 1 year ago
front.js
3907 lines
1 function 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
34 function 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
39 function 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
45 function 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
57 function 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
66 function latepoint_am_or_pm(minutes) {
67 if (latepoint_is_army_clock()) return '';
68 return (minutes < 720 || minutes == 1440) ? 'am' : 'pm';
69 }
70
71 function 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
81 function latepoint_get_time_system() {
82 return latepoint_helper.time_system;
83 }
84
85 function latepoint_is_army_clock() {
86 return (latepoint_get_time_system() == '24');
87 }
88
89 function 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
106 function 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
118 function 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
125 function 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
166 function 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
176 function 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
214 function latepoint_mask_timefield($elem) {
215 if (jQuery().inputmask) {
216 $elem.inputmask({
217 'mask': '99:99',
218 'placeholder': 'HH:MM'
219 });
220 }
221 }
222
223 function 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
240 function 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
254 function 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
268 function 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
284 function 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
293 function 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
301 function 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
371 function latepoint_show_booking_end_time() {
372 return (latepoint_helper.show_booking_end_time == 'yes');
373 }
374
375 function 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
381 function latepoint_get_cookie(name) {
382 let cookie = document.cookie.match('(^|;) ?' + name + '=([^;]*)(;|$)');
383 return cookie ? cookie[2] : null;
384 }
385
386 function latepoint_has_cookie(name) {
387 return latepoint_get_cookie(name) !== null;
388 }
389
390 function latepoint_delete_cookie(name) {
391 latepoint_set_cookie(name, '', -1);
392 }
393
394 function 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
404 function 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
414 function 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
427 function 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
433 function latepoint_clear_form_messages($form){
434 $form.find('.os-form-message-w').remove();
435 }
436
437 function 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
450 function 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
466 jQuery(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 jQuery('body.latepoint').on('change', 'select[data-os-on-change]', function(e){
493 let $this = jQuery(this);
494
495 let func_name = $this.data('os-on-change');
496 if(func_name.includes('.')){
497 let func_arr = func_name.split('.');
498 if(typeof window[func_arr[0]][func_arr[1]] !== 'function'){
499 console.log(func_name + ' is undefined');
500 }else{
501 window[func_arr[0]][func_arr[1]]($this);
502 }
503 }else{
504 if(typeof window[func_name] !== 'function'){
505 console.log(func_name + ' is undefined');
506 }else{
507 window[func_name]($this);
508 }
509 }
510 });
511
512 /*
513 Ajax buttons action
514 */
515 $('.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){
516 var $this = jQuery(this);
517 if($this.data('os-prompt') && !confirm($this.data('os-prompt'))) return false;
518 var params = $this.data('os-params');
519 if($this.data('os-source-of-params')){
520 var form_data = latepoint_create_form_data_from_non_form_element($($this.data('os-source-of-params')));
521 params = latepoint_formdata_to_url_encoded_string(form_data);
522 }
523 var return_format = $this.data('os-return-format') ? $this.data('os-return-format') : 'json'
524 var data = { action: 'latepoint_route_call', route_name: $this.data('os-action'), params: params, return_format: return_format }
525 $this.addClass('os-loading');
526 if($this.data('os-output-target') == 'side-panel'){
527 $('.latepoint-side-panel-w').remove();
528 let css_classes = $this.data('os-lightbox-classes') ? $this.data('os-lightbox-classes') : '';
529 $('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>');
530 }else if($this.data('os-output-target') == 'full-panel'){
531 $('.latepoint-full-panel-w').remove();
532 $('body').append('<div class="latepoint-full-panel-w os-loading"></div>');
533 }
534 $.ajax({
535 type : "post",
536 dataType : "json",
537 url : latepoint_timestamped_ajaxurl(),
538 data : data,
539 success: function(response){
540 if(response.status === "success"){
541 if($this.data('os-output-target') == 'lightbox'){
542 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'));
543 }else if($this.data('os-output-target') == 'side-panel'){
544 $('.latepoint-side-panel-i').html(response.message);
545 jQuery('.latepoint-side-panel-i').find('.os-form-header .latepoint-side-panel-close').remove();
546 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>');
547 setTimeout(function(){
548 $('.latepoint-side-panel-w').removeClass('os-loading');
549 }, 100);
550 }else if($this.data('os-output-target') == 'full-panel'){
551 $('.latepoint-full-panel-w').html(response.message);
552 setTimeout(function(){
553 $('.latepoint-full-panel-w').removeClass('os-loading');
554 }, 100);
555 }else if($this.data('os-success-action') == 'reload'){
556 latepoint_add_notification(response.message);
557 location.reload();
558 return;
559 }else if($this.data('os-success-action') == 'redirect'){
560 if($this.data('os-redirect-to')){
561 latepoint_add_notification(response.message);
562 window.location.replace($this.data('os-redirect-to'));
563 }else{
564 window.location.replace(response.message);
565 }
566 return;
567 }else if($this.data('os-output-target') && $($this.data('os-output-target')).length){
568 if($this.data('os-output-target-do') == 'append') {
569 $($this.data('os-output-target')).append(response.message);
570 }else if($this.data('os-output-target-do') == 'prepend'){
571 $($this.data('os-output-target')).prepend(response.message);
572 }else{
573 $($this.data('os-output-target')).html(response.message);
574 }
575 }else{
576 switch($this.data('os-before-after')){
577 case 'before':
578 $this.before(response.message);
579 break;
580 case 'after':
581 $this.after(response.message);
582 break;
583 case 'replace':
584 $this.replaceWith(response.message);
585 break;
586 case 'none':
587 break;
588 default:
589 latepoint_add_notification(response.message);
590 }
591 }
592 if($this.data('os-after-call')){
593 var func_name = $this.data('os-after-call');
594 var callback = false;
595 if(func_name.includes('.')){
596 var func_arr = func_name.split('.');
597 if(typeof window[func_arr[0]][func_arr[1]] !== 'function'){
598 console.log(func_name + ' is undefined');
599 }
600 if($this.data('os-pass-this') && $this.data('os-pass-response')){
601 window[func_arr[0]][func_arr[1]]($this, response);
602 }else if($this.data('os-pass-this')){
603 window[func_arr[0]][func_arr[1]]($this);
604 }else if($this.data('os-pass-response')){
605 window[func_arr[0]][func_arr[1]](response);
606 }else{
607 window[func_arr[0]][func_arr[1]]();
608 }
609 }else{
610 if(typeof window[func_name] !== 'function'){
611 console.log(func_name + ' is undefined');
612 }
613 if($this.data('os-pass-this') && $this.data('os-pass-response')){
614 window[func_name]($this, response);
615 }else if($this.data('os-pass-this')){
616 window[func_name]($this);
617 }else if($this.data('os-pass-response')){
618 window[func_name](response);
619 }else{
620 window[func_name]();
621 }
622 }
623 }
624 $this.removeClass('os-loading');
625 }else{
626 $this.removeClass('os-loading');
627 if($this.data('os-output-target') && $($this.data('os-output-target')).length){
628 $($this.data('os-output-target')).prepend(latepoint_generate_form_message_html(response.message, 'error'));
629 }else{
630 alert(response.message);
631 }
632 if($this.data('os-after-call-error')){
633 var func_name = $this.data('os-after-call-error');
634 var callback = false;
635 if(func_name.includes('.')){
636 var func_arr = func_name.split('.');
637 if(typeof window[func_arr[0]][func_arr[1]] !== 'function'){
638 console.log(func_name + ' is undefined');
639 }
640 if($this.data('os-pass-this') && $this.data('os-pass-response')){
641 window[func_arr[0]][func_arr[1]]($this, response);
642 }else if($this.data('os-pass-this')){
643 window[func_arr[0]][func_arr[1]]($this);
644 }else if($this.data('os-pass-response')){
645 window[func_arr[0]][func_arr[1]](response);
646 }else{
647 window[func_arr[0]][func_arr[1]]();
648 }
649 }else{
650 if(typeof window[func_name] !== 'function'){
651 console.log(func_name + ' is undefined');
652 }
653 if($this.data('os-pass-this') && $this.data('os-pass-response')){
654 window[func_name]($this, response);
655 }else if($this.data('os-pass-this')){
656 window[func_name]($this);
657 }else if($this.data('os-pass-response')){
658 window[func_name](response);
659 }else{
660 window[func_name]();
661 }
662 }
663 }
664 }
665 }
666 });
667 return false;
668 });
669
670
671 $('.latepoint').on('click', 'form[data-os-action] button[type="submit"]', function(e){
672 $(this).addClass('os-loading');
673 });
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691 /*
692 Form ajax submit action
693 */
694 $('.latepoint').on('submit', 'form[data-os-action]', function(e){
695 e.preventDefault(); // prevent native submit
696 var $form = $(this);
697 var form_data = new FormData($form[0]);
698
699 if (('lp_intlTelInputGlobals' in window) && ('lp_intlTelInputUtils' in window)) {
700 // Get e164 formatted number from phone fields when form is submitted
701 $form.find('input.os-mask-phone').each(function () {
702 let telInstance = window.lp_intlTelInputGlobals.getInstance(this);
703 if(telInstance){
704 const phoneInputName = this.getAttribute('name');
705 const phoneInputValue = window.lp_intlTelInputGlobals.getInstance(this).getNumber(window.lp_intlTelInputUtils.numberFormat.E164);
706 form_data.set(phoneInputName, phoneInputValue);
707 }
708 });
709 }
710
711 let data = latepoint_create_form_data($form, $(this).data('os-action'));
712
713 // var data = { action: 'latepoint_route_call', route_name: $(this).data('os-action'), params: latepoint_formdata_to_url_encoded_string(form_data), return_format: 'json' }
714 $form.find('button[type="submit"]').addClass('os-loading');
715 $.ajax({
716 type : "post",
717 dataType : "json",
718 processData: false,
719 contentType: false,
720 url : latepoint_timestamped_ajaxurl(),
721 data : data,
722 success: function(response){
723 $form.find('button[type="submit"].os-loading').removeClass('os-loading');
724 latepoint_clear_form_messages($form);
725 if(response.status === "success"){
726 if($form.data('os-success-action') == 'reload'){
727 latepoint_add_notification(response.message);
728 location.reload();
729 return;
730 }else if($form.data('os-success-action') == 'redirect'){
731 if($form.data('os-redirect-to')){
732 latepoint_add_notification(response.message);
733 window.location.replace($form.data('os-redirect-to'));
734 }else{
735 window.location.replace(response.message);
736 }
737 return;
738 }else if($form.data('os-output-target') && $($form.data('os-output-target')).length){
739 $($form.data('os-output-target')).html(response.message);
740 }else{
741 if(response.message == 'redirect'){
742 window.location.replace(response.url);
743 }else{
744 latepoint_add_notification(response.message);
745 }
746 }
747 if($form.data('os-record-id-holder') && response.record_id){
748 $form.find('[name="' + $form.data('os-record-id-holder') + '"]').val(response.record_id)
749 }
750 if($form.data('os-after-call')){
751
752 var func_name = $form.data('os-after-call');
753 var callback = false;
754 if(func_name.includes('.')){
755 var func_arr = func_name.split('.');
756 if(typeof window[func_arr[0]][func_arr[1]] !== 'function'){
757 console.log(func_name + ' is undefined');
758 }
759 if($form.data('os-pass-this') && $form.data('os-pass-response')){
760 window[func_arr[0]][func_arr[1]]($form, response);
761 }else if($form.data('os-pass-this')){
762 window[func_arr[0]][func_arr[1]]($form);
763 }else if($form.data('os-pass-response')){
764 window[func_arr[0]][func_arr[1]](response);
765 }else{
766 window[func_arr[0]][func_arr[1]]();
767 }
768 }else{
769 if(typeof window[func_name] !== 'function'){
770 console.log(func_name + ' is undefined');
771 }
772 if($form.data('os-pass-this') && $form.data('os-pass-response')){
773 window[func_name]($form, response);
774 }else if($form.data('os-pass-this')){
775 window[func_name]($form);
776 }else if($form.data('os-pass-response')){
777 window[func_name](response);
778 }else{
779 window[func_name]();
780 }
781 }
782 }
783 $('button.os-loading').removeClass('os-loading');
784 }else{
785 $('button.os-loading').removeClass('os-loading');
786 if($form.data('os-show-errors-as-notification')){
787 latepoint_add_notification(response.message, 'error');
788 }else{
789 latepoint_add_notification(response.message, 'error');
790 $([document.documentElement, document.body]).animate({
791 scrollTop: ($form.find(".os-form-message-w").offset().top - 30)
792 }, 200);
793 }
794 }
795 if(response.form_values_to_update){
796 $.each(response.form_values_to_update, function(name, value){
797 $form.find('[name="'+ name +'"]').val(value);
798 });
799 }
800 }
801 });
802 return false;
803 });
804 });
805
806 /*
807 * Copyright (c) 2022 LatePoint LLC. All rights reserved.
808 */
809
810
811 function latepoint_init_order_summary_lightbox() {
812 let $lightbox = jQuery('.customer-dashboard-order-summary-lightbox');
813 latepoint_init_qr_trigger($lightbox);
814 latepoint_init_item_details_popup($lightbox);
815 }
816
817 function latepoint_init_qr_trigger($lightbox) {
818 $lightbox.on('click', '.qr-show-trigger', function () {
819 jQuery(this).closest('.summary-box-wrapper').find('.qr-code-on-full-summary').toggleClass('show-vevent-qr-code');
820 return false;
821 });
822 }
823
824 function latepoint_init_item_details_popup($lightbox) {
825
826 $lightbox.on('click', '.os-item-details-popup-close', function () {
827 var $ligthbox = jQuery(this).closest('.latepoint-lightbox-content');
828 $ligthbox.find('.os-item-details-popup.open').remove();
829 $ligthbox.find('.full-summary-wrapper').show();
830 $ligthbox.find('.booking-status-info-wrapper').show();
831 return false;
832 });
833
834 $lightbox.on('click', '.os-trigger-item-details-popup', function () {
835 var $ligthbox = jQuery(this).closest('.latepoint-lightbox-content');
836 $ligthbox.find('.full-summary-wrapper').hide();
837 $ligthbox.find('.booking-status-info-wrapper').hide();
838 $ligthbox.find('.os-item-details-popup.open').remove();
839 var $popup = $ligthbox.find('#' + jQuery(this).data('item-details-popup-id')).clone();
840 $popup.addClass('open').appendTo($ligthbox);
841 return false;
842 });
843 }
844
845 function latepoint_init_bundle_scheduling_summary() {
846
847 }
848
849
850 function latepoint_manage_by_key_reload_booking() {
851 let $wrapper = jQuery('.manage-booking-wrapper');
852 $wrapper.addClass('os-loading')
853 let params = {
854 key: $wrapper.data('key')
855 }
856 let data = {
857 action: latepoint_helper.route_action,
858 route_name: $wrapper.data('route-name'),
859 params: params,
860 layout: 'none',
861 return_format: 'json'
862 };
863
864 jQuery.ajax({
865 type: "post",
866 dataType: "json",
867 url: latepoint_timestamped_ajaxurl(),
868 data: data,
869 success: function (data) {
870 $wrapper.removeClass('os-loading')
871 if (data.status === "success") {
872 $wrapper.replaceWith(data.message);
873 } else {
874 latepoint_show_message_inside_element(data.message, $wrapper, 'error');
875 }
876 }
877 });
878 }
879
880 function latepoint_init_manage_booking_by_key() {
881 let $wrapper = jQuery('.manage-booking-wrapper');
882 if (!$wrapper.length) return;
883 jQuery('.latepoint-w').on('change', '.change-booking-status-trigger', function () {
884 $wrapper.addClass('os-loading')
885 let params = {
886 key: $wrapper.data('key'),
887 status: jQuery(this).val()
888 }
889 let data = {
890 action: latepoint_helper.route_action,
891 route_name: jQuery(this).closest('.change-booking-status-trigger-wrapper').data('route-name'),
892 params: params,
893 layout: 'none',
894 return_format: 'json'
895 };
896
897 jQuery.ajax({
898 type: "post",
899 dataType: "json",
900 url: latepoint_timestamped_ajaxurl(),
901 data: data,
902 success: function (data) {
903 $wrapper.removeClass('os-loading')
904 if (data.status === "success") {
905 latepoint_manage_by_key_reload_booking();
906 } else {
907 latepoint_show_message_inside_element(data.message, $wrapper, 'error');
908 }
909 }
910 });
911 return false;
912 });
913
914
915 $wrapper.on('click', '.qr-show-trigger', function () {
916 jQuery(this).closest('.manage-booking-wrapper').find('.qr-code-on-full-summary').addClass('show-vevent-qr-code');
917 return false;
918 });
919 $wrapper.on('click', '.os-item-details-popup-close', function () {
920 var $wrapper = jQuery(this).closest('.manage-booking-wrapper');
921 $wrapper.find('.os-item-details-popup.open').remove();
922 $wrapper.find('.manage-booking-inner, .manage-booking-controls').show();
923 return false;
924 });
925
926 $wrapper.on('click', '.os-trigger-item-details-popup', function () {
927 var $wrapper = jQuery(this).closest('.manage-booking-wrapper');
928 $wrapper.find('.manage-booking-inner, .manage-booking-controls').hide();
929 $wrapper.find('.os-item-details-popup.open').remove();
930 var $popup = $wrapper.find('#' + jQuery(this).data('item-details-popup-id')).clone();
931 $popup.addClass('open').appendTo($wrapper);
932 return false;
933 });
934 }
935
936 function latepoint_init_form_masks() {
937 if (('lp_intlTelInput' in window) && ('lp_intlTelInputGlobals' in window)) {
938 jQuery('.os-mask-phone').each(function () {
939 latepoint_mask_phone(jQuery(this));
940 });
941 }
942 }
943
944 function latepoint_scroll_to_top_of_booking_form($booking_form_element) {
945 // if it's a form shortcode (not lightbox), scroll to top of the form
946 if ($booking_form_element.parent().hasClass('latepoint-inline-form')) {
947 $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
948 }
949 // if lightbox - scroll body of lightbox to top
950 if ($booking_form_element.parent().hasClass('latepoint-lightbox-i')) {
951 $booking_form_element.find('.latepoint-body').scrollTop(0);
952 }
953 }
954
955 async function latepoint_init_payment_method_actions($booking_form_element, payment_method) {
956 let callbacks_list = [];
957 let is_last_step = $booking_form_element.data('next-submit-is-last') == 'yes';
958 $booking_form_element.trigger('latepoint:initPaymentMethod', [{
959 payment_method: payment_method,
960 callbacks_list: callbacks_list,
961 is_last_step: is_last_step
962 }]);
963 $booking_form_element.removeClass('step-content-loaded').addClass('step-content-loading');
964
965
966 try {
967 for (const callback of callbacks_list) {
968 await callback.action();
969 }
970 $booking_form_element.removeClass('step-content-loading').addClass('step-content-loaded').find('.lp-payment-method-content[data-payment-method="' + payment_method + '"]').show();
971 } catch (error) {
972 latepoint_show_error_and_stop_loading_booking_form(error, $booking_form_element);
973 }
974 }
975
976 function latepoint_lightbox_close() {
977 jQuery('body').removeClass('latepoint-lightbox-active');
978 jQuery('.latepoint-lightbox-w').remove();
979 }
980
981 function latepoint_show_next_btn($booking_form_element) {
982 $booking_form_element.find('.latepoint-next-btn').removeClass('disabled');
983 $booking_form_element.removeClass('hidden-buttons');
984 }
985
986 function latepoint_clear_step_services($booking_form_element) {
987 }
988
989 function latepoint_clear_step_service_extras($booking_form_element) {
990 }
991
992 function latepoint_clear_step_locations($booking_form_element) {
993 }
994
995 function latepoint_clear_step_agents($booking_form_element) {
996 }
997
998 function latepoint_clear_step_datepicker($booking_form_element) {
999 }
1000
1001 function latepoint_hide_next_btn($booking_form_element) {
1002 $booking_form_element.find('.latepoint-next-btn').addClass('disabled');
1003 if ($booking_form_element.find('.latepoint-prev-btn.disabled').length) $booking_form_element.addClass('hidden-buttons');
1004 }
1005
1006
1007 function latepoint_show_prev_btn($booking_form_element) {
1008 $booking_form_element.find('.latepoint-prev-btn').removeClass('disabled');
1009 $booking_form_element.removeClass('hidden-buttons');
1010 }
1011
1012 function latepoint_hide_prev_btn($booking_form_element) {
1013 $booking_form_element.find('.latepoint-prev-btn').addClass('disabled');
1014 if ($booking_form_element.find('.latepoint-next-btn.disabled').length) $booking_form_element.addClass('hidden-buttons');
1015 }
1016
1017
1018 function latepoint_remove_cart_item($trigger) {
1019 let $booking_form_element = $trigger.closest('.latepoint-booking-form-element');
1020 let cart_item_id = $trigger.data('cart-item-id');
1021
1022
1023 $trigger.addClass('os-loading');
1024 let data = {
1025 action: latepoint_helper.route_action,
1026 route_name: $trigger.data('route'),
1027 params: jQuery.param({cart_item_id: cart_item_id}),
1028 layout: 'none',
1029 return_format: 'json'
1030 }
1031 jQuery.ajax({
1032 type: "post",
1033 dataType: "json",
1034 url: latepoint_timestamped_ajaxurl(),
1035 data: data,
1036 success: function (data) {
1037 if (data.status === "success") {
1038 if (cart_item_id != $booking_form_element.find('input[name="active_cart_item[id]"]').val()) {
1039 // cart has other items - just reload the summary/step
1040 if ($trigger.closest('.latepoint-summary-w').length) {
1041 // removed by clicking on summary side panel
1042 latepoint_reload_summary($booking_form_element);
1043 } else {
1044 // remove by clicking on cart item on verify step
1045 latepoint_reload_step($booking_form_element);
1046 }
1047 } else {
1048 // this was a last item, need to go back to the start of a booking process
1049 latepoint_restart_booking_process($booking_form_element);
1050 }
1051 } else {
1052 $trigger.removeClass('os-loading');
1053 latepoint_show_message_inside_element(data.message, $booking_form_element.find('.latepoint-body'), 'error');
1054 }
1055 }
1056 });
1057 }
1058
1059 function latepoint_apply_coupon($elem) {
1060 var $booking_form_element = $elem.closest('.latepoint-booking-form-element');
1061
1062 var $coupon_input = $elem;
1063 $coupon_input.closest('.coupon-code-input-w').addClass('os-loading');
1064 var form_data = new FormData($booking_form_element.find('.latepoint-form')[0]);
1065 var data = {
1066 action: latepoint_helper.route_action,
1067 route_name: $elem.data('route'),
1068 params: latepoint_formdata_to_url_encoded_string(form_data),
1069 layout: 'none',
1070 return_format: 'json'
1071 }
1072 jQuery.ajax({
1073 type: "post",
1074 dataType: "json",
1075 url: latepoint_timestamped_ajaxurl(),
1076 data: data,
1077 success: function (data) {
1078 $coupon_input.closest('.coupon-code-input-w').removeClass('os-loading');
1079 if (data.status === "success") {
1080 latepoint_show_message_inside_element(data.message, $booking_form_element.find('.latepoint-body'), 'success');
1081 $booking_form_element.find('.step-payment-w input[name="cart[payment_method]"]').val('');
1082 $booking_form_element.find('input[name="cart[payment_token]"]').val('');
1083 $booking_form_element.find('input[name="cart[payment_portion]"]').val('');
1084 latepoint_reload_step($booking_form_element);
1085 } else {
1086 latepoint_show_message_inside_element(data.message, $booking_form_element.find('.latepoint-body'), 'error');
1087 }
1088 }
1089 });
1090 }
1091
1092 function latepoint_remove_coupon($elem) {
1093 $elem.closest('.applied-coupon-code-wrapper').fadeOut();
1094 var $booking_form_element = $elem.closest('.latepoint-booking-form-element');
1095 let $coupon_input = $booking_form_element.find('input[name="coupon_code"]');
1096 $coupon_input.val('');
1097 latepoint_apply_coupon($coupon_input);
1098 }
1099
1100 function latepoint_restart_booking_process($booking_form_element) {
1101 // first first step
1102 let first_step_code = $booking_form_element.find('.latepoint-step-content').first().data('step-code');
1103 latepoint_reload_step($booking_form_element, first_step_code);
1104 return false;
1105 }
1106
1107 function latepoint_reload_step($booking_form_element, step_code = false) {
1108
1109 if (step_code) {
1110 $booking_form_element.find('.latepoint_current_step_code').val(step_code);
1111 $booking_form_element.removeClass(function (index, className) {
1112 return (className.match(/(^|\s)current-step-\S+/g) || []).join(' ');
1113 }).addClass('current-step-' + step_code);
1114 if ($booking_form_element.find('.latepoint-step-content[data-step-code="' + step_code + '"]')) {
1115 $booking_form_element.find('.latepoint-step-content[data-step-code="' + step_code + '"]').nextAll('.latepoint-step-content').remove();
1116 $booking_form_element.find('.latepoint-step-content[data-step-code="' + step_code + '"]').remove();
1117 }
1118 }
1119
1120 $booking_form_element.data('next-submit-is-last', 'no');
1121 $booking_form_element.find('.latepoint_step_direction').val('specific');
1122 latepoint_submit_booking_form($booking_form_element.find('.latepoint-form'));
1123
1124 return false;
1125 }
1126
1127
1128 function latepoint_reset_password_from_booking_init() {
1129 jQuery('.os-step-existing-customer-login-w').hide();
1130 jQuery('.os-password-reset-form-holder').on('click', '.password-reset-back-to-login', function () {
1131 jQuery('.os-password-reset-form-holder').html('');
1132 jQuery('.os-step-existing-customer-login-w').show();
1133 return false;
1134 });
1135 }
1136
1137 function latepoint_bundle_selected($item) {
1138 let $booking_form_element = $item.closest('.latepoint-booking-form-element');
1139 $booking_form_element.find('input[name="active_cart_item[variant]"]').val('bundle');
1140 $booking_form_element.find('input[name="booking[service_id]"]').val('');
1141 }
1142
1143 function latepoint_service_selected($item) {
1144 let $booking_form_element = $item.closest('.latepoint-booking-form-element');
1145 $booking_form_element.find('input[name="active_cart_item[variant]"]').val('booking');
1146 }
1147
1148 async function latepoint_reload_summary($booking_form_element) {
1149 let $summary_panel = $booking_form_element.closest('.latepoint-with-summary');
1150 if (!$summary_panel.length) return;
1151
1152 if ($booking_form_element.hasClass('is-bundle-scheduling')) {
1153 return;
1154 }
1155
1156 let current_step = $booking_form_element.find('.latepoint_current_step_code').val();
1157
1158 $booking_form_element.find('.latepoint-summary-w').addClass('os-loading');
1159 let $booking_form = $booking_form_element.find('.latepoint-form');
1160 let form_data = new FormData($booking_form[0]);
1161 let data = {
1162 action: latepoint_helper.route_action,
1163 route_name: latepoint_helper.reload_booking_form_summary_route,
1164 params: latepoint_formdata_to_url_encoded_string(form_data),
1165 layout: 'none',
1166 return_format: 'json'
1167 }
1168
1169 let response = await jQuery.ajax({
1170 type: "post",
1171 dataType: "json",
1172 url: latepoint_timestamped_ajaxurl(),
1173 data: data
1174 });
1175 if (response.status === 'success') {
1176 $booking_form_element.find('.os-summary-contents').html(response.message);
1177 $booking_form_element.find('.latepoint-summary-w').removeClass('os-loading');
1178 // hide on verify and confirmation steps
1179 if (current_step && !['verify', 'confirmation'].includes(current_step) && response.message) {
1180 $summary_panel.addClass('latepoint-summary-is-open');
1181 } else {
1182 $summary_panel.removeClass('latepoint-summary-is-open');
1183 }
1184 latepoint_init_booking_summary_panel($booking_form_element);
1185 } else {
1186 throw new Error(response.message ? response.message : 'Error reloading summary');
1187 }
1188 }
1189
1190 function latepoint_init_booking_summary_panel($booking_form_element) {
1191 let $summary_panel = $booking_form_element.find('.latepoint-summary-w');
1192 if (!$summary_panel.length) return;
1193
1194 $summary_panel.find('.load-customer-step-trigger').on('click', function () {
1195 jQuery(this).addClass('os-loading');
1196 latepoint_reload_step($booking_form_element, 'customer');
1197 return false;
1198 });
1199
1200 $summary_panel.find('.price-breakdown-unfold').on('click', function () {
1201 jQuery(this).closest('.summary-price-breakdown-wrapper').removeClass('compact-summary');
1202 return false;
1203 });
1204
1205 $summary_panel.find('.os-remove-item-from-cart').on('click keydown', function (event) {
1206 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1207 latepoint_remove_cart_item(jQuery(this));
1208 return false;
1209 });
1210 }
1211
1212 function latepoint_password_changed_show_login(response) {
1213 jQuery('.os-step-existing-customer-login-w').show();
1214 jQuery('.os-password-reset-form-holder').html('');
1215 latepoint_show_message_inside_element(response.message, jQuery('.os-step-existing-customer-login-w'), 'success');
1216 }
1217
1218 function latepoint_hide_message_inside_element($elem = jQuery('.latepoint-body')) {
1219 if ($elem.length && $elem.find('.latepoint-message').length) {
1220 $elem.find('.latepoint-message').remove();
1221 }
1222 }
1223
1224 function latepoint_show_message_inside_element(message, $elem = jQuery('.latepoint-body'), message_type = 'error') {
1225 message = message || 'Error. Please try again.';
1226 if ($elem.length) {
1227 if ($elem.find('.latepoint-message').length) {
1228 $elem.find('.latepoint-message').removeClass('latepoint-message-success').removeClass('latepoint-message-error').addClass('latepoint-message-' + message_type + '').html(message).show();
1229 } else {
1230 $elem.prepend('<div class="latepoint-message latepoint-message-' + message_type + '">' + message + '</div>');
1231 }
1232 // scroll errors into view
1233 if (message_type == 'error') $elem.find('.latepoint-message')[0].scrollIntoView({block: "nearest", behavior: 'smooth'});
1234 }
1235 }
1236
1237 function latepoint_add_action(callbacks_list, action, priority = 10) {
1238 callbacks_list.push({priority: priority, action: action});
1239 callbacks_list.sort((a, b) => a.priority - b.priority);
1240 return callbacks_list;
1241 }
1242
1243 function latepoint_update_next_btn_label($booking_form_element) {
1244 let btn_label = $booking_form_element.find('.latepoint-step-content').last().data('next-btn-label')
1245 if (btn_label) {
1246 $booking_form_element.find('.latepoint-next-btn span').text(btn_label);
1247 }
1248 }
1249
1250 function latepoint_init_step(step_code, $booking_form_element) {
1251 latepoint_init_step_selectable_items($booking_form_element);
1252 latepoint_init_step_category_items(step_code);
1253 switch (step_code) {
1254 case 'customer':
1255 latepoint_init_step_customer($booking_form_element);
1256 break;
1257 case 'booking__datepicker':
1258 latepoint_init_step_datepicker($booking_form_element);
1259 break;
1260 case 'booking__agents':
1261 latepoint_init_step_agents();
1262 break;
1263 case 'booking__locations':
1264 latepoint_init_step_locations();
1265 break;
1266 case 'booking__services':
1267 latepoint_init_step_services();
1268 break;
1269 case 'payment__methods':
1270 latepoint_init_step_payment__methods($booking_form_element);
1271 break;
1272 case 'payment__times':
1273 latepoint_init_step_payment__times($booking_form_element);
1274 break;
1275 case 'payment__portions':
1276 latepoint_init_step_payment__portions($booking_form_element);
1277 break;
1278 case 'payment__pay':
1279 latepoint_init_step_payment__pay($booking_form_element);
1280 break;
1281 case 'verify':
1282 latepoint_init_step_verify($booking_form_element);
1283 break;
1284 case 'confirmation':
1285 latepoint_init_step_confirmation($booking_form_element);
1286 break;
1287 }
1288
1289 $booking_form_element.trigger("latepoint:initStep", [{step_code: step_code}]);
1290 $booking_form_element.trigger("latepoint:initStep:" + step_code);
1291 }
1292
1293
1294 async function latepoint_generate_day_timeslots($day, $wrapper_element = false, $scrollable_wrapper = false) {
1295 if (!$wrapper_element) $wrapper_element = $day.closest('.latepoint-booking-form-element');
1296
1297 $day.addClass('selected');
1298
1299 var service_duration = $day.data('service-duration');
1300 var interval = $day.data('interval');
1301 var work_start_minutes = $day.data('work-start-time');
1302 var work_end_minutes = $day.data('work-end-time');
1303 var total_work_minutes = $day.data('total-work-minutes');
1304 var bookable_minutes = [];
1305 var available_capacities_of_bookable_minute = [];
1306 if ($day.attr('data-bookable-minutes')) {
1307 if ($day.data('bookable-minutes').toString().indexOf(':') > -1) {
1308 // has capacity information embedded into bookable minutes string
1309 let bookable_minutes_with_capacity = $day.data('bookable-minutes').toString().split(',');
1310 for (let i = 0; i < bookable_minutes_with_capacity.length; i++) {
1311 bookable_minutes.push(parseInt(bookable_minutes_with_capacity[i].split(':')[0]));
1312 available_capacities_of_bookable_minute.push(parseInt(bookable_minutes_with_capacity[i].split(':')[1]));
1313 }
1314 } else {
1315 bookable_minutes = $day.data('bookable-minutes').toString().split(',').map(Number);
1316 }
1317 }
1318 var work_minutes = $day.data('work-minutes').toString().split(',').map(Number);
1319
1320 var $timeslots = $wrapper_element.find('.timeslots');
1321 $timeslots.html('');
1322
1323 if (total_work_minutes > 0 && bookable_minutes.length && work_minutes.length) {
1324 var prev_minutes = false;
1325 work_minutes.forEach(function (current_minutes) {
1326 var ampm = latepoint_am_or_pm(current_minutes);
1327
1328 var timeslot_class = 'dp-timepicker-trigger';
1329 var timeslot_available_capacity = 0;
1330 if (latepoint_helper.time_pick_style == 'timeline') {
1331 timeslot_class += ' dp-timeslot';
1332 } else {
1333 timeslot_class += ' dp-timebox';
1334 }
1335
1336 if (prev_minutes !== false && ((current_minutes - prev_minutes) > service_duration)) {
1337 // show interval that is off between two work periods
1338 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);
1339 var off_width = (((current_minutes - prev_minutes - service_duration) / total_work_minutes) * 100);
1340 $timeslots.append('<div class="' + timeslot_class + ' is-off" style="max-width:' + off_width + '%; width:' + off_width + '%"><span class="dp-label">' + off_label + '</span></div>');
1341 }
1342
1343 if (!bookable_minutes.includes(current_minutes)) {
1344 timeslot_class += ' is-booked';
1345 } else {
1346 if (available_capacities_of_bookable_minute.length) timeslot_available_capacity = available_capacities_of_bookable_minute[bookable_minutes.indexOf(current_minutes)];
1347 }
1348 var tick_html = '';
1349 var capacity_label = '';
1350 var capacity_label_html = '';
1351 var capacity_internal_label_html = '';
1352
1353 if (((current_minutes % 60) == 0) || (interval >= 60)) {
1354 timeslot_class += ' with-tick';
1355 tick_html = '<span class="dp-tick"><strong>' + latepoint_minutes_to_hours_preferably(current_minutes) + '</strong>' + ' ' + ampm + '</span>';
1356 }
1357 var timeslot_label = latepoint_minutes_to_hours_and_minutes(current_minutes) + ' ' + ampm;
1358 if (latepoint_show_booking_end_time()) {
1359 var end_minutes = current_minutes + service_duration;
1360 if (end_minutes > 1440) end_minutes = end_minutes - 1440;
1361 var end_minutes_ampm = latepoint_am_or_pm(end_minutes);
1362 timeslot_label += ' - <span class="dp-label-end-time">' + latepoint_minutes_to_hours_and_minutes(end_minutes) + ' ' + end_minutes_ampm + '</span>';
1363 }
1364 if (timeslot_available_capacity) {
1365 var spaces_message = timeslot_available_capacity > 1 ? latepoint_helper.many_spaces_message : latepoint_helper.single_space_message;
1366 capacity_label = timeslot_available_capacity + ' ' + spaces_message;
1367 capacity_label_html = '<span class="dp-capacity">' + capacity_label + '</span>';
1368 capacity_internal_label_html = '<span class="dp-label-capacity">' + capacity_label + '</span>';
1369 }
1370 timeslot_label = timeslot_label.trim();
1371 $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>');
1372 prev_minutes = current_minutes;
1373 });
1374 } else {
1375 // No working hours this day
1376 $timeslots.addClass('slots-not-available').append('<div class="not-working-message">' + latepoint_helper.msg_not_available + "</div>");
1377 }
1378 jQuery('.times-header-label span').text($day.data('nice-date'));
1379 $wrapper_element.find('.time-selector-w').slideDown(200, function () {
1380 if (!$scrollable_wrapper) $scrollable_wrapper = $wrapper_element.find('.latepoint-body');
1381 $scrollable_wrapper.stop();
1382 $wrapper_element.find('.time-selector-w')[0].scrollIntoView({block: "nearest", behavior: 'smooth'});
1383 });
1384 }
1385
1386
1387 function latepoint_recurring_option_clicked(event) {
1388 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1389 let $btn = jQuery(this);
1390 let $booking_form_element = $btn.closest('.latepoint-booking-form-element');
1391 $booking_form_element.find('.latepoint_is_recurring').val($btn.data('value'));
1392 latepoint_trigger_next_btn($booking_form_element);
1393 $booking_form_element.find('.step-datepicker-w').removeClass('show-recurring-prompt');
1394 return false;
1395 }
1396
1397 function latepoint_timeslot_clicked(event) {
1398 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1399 event.preventDefault();
1400 let $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1401 let $trigger = jQuery(this);
1402 if ($trigger.hasClass('is-booked') || jQuery(this).hasClass('is-off')) {
1403 // Show error message that you cant select a booked period
1404 } else {
1405 if ($trigger.hasClass('selected')) {
1406 $trigger.removeClass('selected');
1407 $trigger.find('.dp-success-label').remove();
1408 $booking_form_element.find('.latepoint_start_time').val('');
1409 latepoint_hide_next_btn($booking_form_element);
1410 latepoint_reload_summary($booking_form_element);
1411 } else {
1412 $booking_form_element.find('.dp-timepicker-trigger.selected').removeClass('selected').find('.dp-success-label').remove();
1413 let selected_timeslot_time = $trigger.find('.dp-label-time').html();
1414 $trigger.addClass('selected').find('.dp-label').prepend('<span class="dp-success-label">' + latepoint_helper.datepicker_timeslot_selected_label + '</span>');
1415
1416 let start_minutes = parseInt($trigger.data('minutes'));
1417 let start_date = $trigger.closest('.os-dates-and-times-w').find('.os-day.selected').data('date');
1418
1419 if ($booking_form_element.find('.recurring-bookings-preview-wrapper').length && $booking_form_element.find('.os-recurrence-rules').length) {
1420 // recurring datepicker
1421 if ($booking_form_element.find('.recurring-bookings-preview-wrapper .recurring-booking-preview.is-editing').length) {
1422 // editing one of timeslots, not the recurrence settings
1423 let $recurring_bookings_fields = $booking_form_element.find('.os-recurrence-selection-fields-wrapper');
1424 let $edited_booking = $booking_form_element.find('.recurring-bookings-preview-wrapper .recurring-booking-preview.is-editing');
1425
1426 $recurring_bookings_fields.find('input[name="recurrence[overrides][' + $edited_booking.data('stamp') + '][custom_day]"]').val(start_date);
1427 $recurring_bookings_fields.find('input[name="recurrence[overrides][' + $edited_booking.data('stamp') + '][custom_minutes]"]').val(start_minutes);
1428
1429 return window.latepointRecurringBookingsFrontFeature.reload_recurrence_rules($booking_form_element, false);
1430 } else {
1431 // editing recurrence start date/time
1432 $booking_form_element.find('.latepoint_start_date').val(start_date);
1433 $booking_form_element.find('.latepoint_start_time').val(start_minutes);
1434 return window.latepointRecurringBookingsFrontFeature.reload_recurrence_rules($booking_form_element, true);
1435 }
1436 } else {
1437 $booking_form_element.find('.latepoint_start_date').val(start_date);
1438 $booking_form_element.find('.latepoint_start_time').val(start_minutes);
1439 if ($trigger.closest('.os-dates-and-times-w').data('allow-recurring') === 'yes') {
1440 $booking_form_element.find('.step-datepicker-w').addClass('show-recurring-prompt');
1441 $booking_form_element.find('.os-recurring-suggestion-wrapper')[0].scrollIntoView({block: "nearest", behavior: 'smooth'});
1442 latepoint_hide_next_btn($booking_form_element);
1443 latepoint_hide_prev_btn($booking_form_element);
1444 } else {
1445 latepoint_trigger_next_btn($booking_form_element);
1446 }
1447 }
1448 }
1449 }
1450 return false;
1451 }
1452
1453 function latepoint_init_timeslots($booking_form_element = false) {
1454 if (!$booking_form_element) return;
1455 $booking_form_element.off('click', '.dp-timepicker-trigger', latepoint_timeslot_clicked);
1456 $booking_form_element.on('click', '.dp-timepicker-trigger', latepoint_timeslot_clicked);
1457 $booking_form_element.off('keydown', '.dp-timepicker-trigger', latepoint_timeslot_clicked);
1458 $booking_form_element.on('keydown', '.dp-timepicker-trigger', latepoint_timeslot_clicked);
1459
1460 $booking_form_element.off('click', '.os-recurring-suggestion-option', latepoint_recurring_option_clicked);
1461 $booking_form_element.on('click', '.os-recurring-suggestion-option', latepoint_recurring_option_clicked);
1462 $booking_form_element.off('keydown', '.os-recurring-suggestion-option', latepoint_recurring_option_clicked);
1463 $booking_form_element.on('keydown', '.os-recurring-suggestion-option', latepoint_recurring_option_clicked);
1464 }
1465
1466 async function latepoint_monthly_calendar_load_next_month($booking_form_element) {
1467 try {
1468
1469 if ($booking_form_element.find('.os-monthly-calendar-days-w.active + .os-monthly-calendar-days-w').length) {
1470 $booking_form_element.find('.os-monthly-calendar-days-w.active').removeClass('active').next('.os-monthly-calendar-days-w').addClass('active');
1471 $booking_form_element.find('.os-month-prev-btn').removeClass('disabled');
1472 latepoint_calendar_set_month_label($booking_form_element);
1473 return true;
1474 } else {
1475 let $btn = $booking_form_element.find('.os-month-next-btn');
1476 let next_month_route_name = $btn.data('route');
1477 $btn.addClass('os-loading');
1478 let $calendar_element = $booking_form_element.find('.os-monthly-calendar-days-w').last();
1479 let calendar_year = $calendar_element.data('calendar-year');
1480 let calendar_month = $calendar_element.data('calendar-month');
1481 if (calendar_month == 12) {
1482 calendar_year = calendar_year + 1;
1483 calendar_month = 1;
1484 } else {
1485 calendar_month = calendar_month + 1;
1486 }
1487 let form_data = new FormData($booking_form_element.find('.latepoint-form')[0]);
1488 form_data.set('target_date_string', `${calendar_year}-${calendar_month}-1`);
1489 let params = latepoint_formdata_to_url_encoded_string(form_data);
1490 let data = {
1491 action: latepoint_helper.route_action,
1492 route_name: next_month_route_name,
1493 params: params,
1494 layout: 'none',
1495 return_format: 'json'
1496 }
1497 let response = await jQuery.ajax({
1498 type: "post",
1499 dataType: "json",
1500 url: latepoint_timestamped_ajaxurl(),
1501 data: data,
1502 success: function (data) {
1503 }
1504 });
1505 $btn.removeClass('os-loading');
1506 if (response.status === "success") {
1507 $booking_form_element.find('.os-months').append(response.message);
1508 $booking_form_element.find('.os-monthly-calendar-days-w.active').removeClass('active').next('.os-monthly-calendar-days-w').addClass('active');
1509 latepoint_calendar_set_month_label($booking_form_element);
1510 latepoint_calendar_show_or_hide_prev_next_buttons($booking_form_element);
1511 return true;
1512 } else {
1513 console.log(response.message);
1514 return false;
1515 }
1516
1517 }
1518 } catch (e) {
1519 console.log(e);
1520 alert('Error:' + e);
1521 return false;
1522 }
1523 }
1524
1525 function latepoint_init_monthly_calendar_navigation($booking_form_element = false) {
1526 if (!$booking_form_element) return;
1527 $booking_form_element.find('.os-month-next-btn').on('click', async function () {
1528 let $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1529 return latepoint_monthly_calendar_load_next_month($booking_form_element);
1530 });
1531 $booking_form_element.find('.os-month-prev-btn').on('click', function () {
1532 var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1533 if ($booking_form_element.find('.os-monthly-calendar-days-w.active').prev('.os-monthly-calendar-days-w').length) {
1534 $booking_form_element.find('.os-monthly-calendar-days-w.active').removeClass('active').prev('.os-monthly-calendar-days-w').addClass('active');
1535 latepoint_calendar_set_month_label($booking_form_element);
1536 }
1537 latepoint_calendar_show_or_hide_prev_next_buttons($booking_form_element);
1538 return false;
1539 });
1540 }
1541
1542 function latepoint_calendar_set_month_label($booking_form_element) {
1543 $booking_form_element.find('.os-current-month-label .current-year').text($booking_form_element.find('.os-monthly-calendar-days-w.active').data('calendar-year'));
1544 $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'));
1545 }
1546
1547
1548 function latepoint_calendar_show_or_hide_prev_next_buttons($booking_form_element) {
1549 $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'));
1550 $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'));
1551
1552 if ($booking_form_element.find('.os-monthly-calendar-days-w.active').prev('.os-monthly-calendar-days-w').length) {
1553 $booking_form_element.find('.os-month-prev-btn').removeClass('disabled');
1554 } else {
1555 $booking_form_element.find('.os-month-prev-btn').addClass('disabled');
1556 }
1557 }
1558
1559 function latepoint_format_minutes_to_time(minutes, service_duration) {
1560 var ampm = latepoint_am_or_pm(minutes);
1561 var formatted_time = latepoint_minutes_to_hours_and_minutes(minutes) + ' ' + ampm;
1562 if (latepoint_show_booking_end_time()) {
1563 var end_minutes = minutes + service_duration;
1564 var end_minutes_ampm = latepoint_am_or_pm(end_minutes);
1565 formatted_time += ' - ' + latepoint_minutes_to_hours_and_minutes(end_minutes) + ' ' + end_minutes_ampm;
1566 }
1567 formatted_time = formatted_time.trim();
1568 return formatted_time;
1569 }
1570
1571 function latepoint_monthly_calendar_day_clicked(event) {
1572 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1573 let $day = jQuery(this);
1574 if ($day.hasClass('os-day-passed')) return false;
1575 if ($day.hasClass('os-not-in-allowed-period')) return false;
1576 if ($day.closest('.os-dates-and-times-w').hasClass('calendar-style-modern')) {
1577 if ($day.hasClass('os-month-prev')) return false;
1578 if ($day.hasClass('os-month-next')) return false;
1579 }
1580 var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1581 if ($day.closest('.os-recurrence-datepicker-wrapper').length) {
1582 // recurrent datepicker
1583 $day.closest('.os-recurrence-datepicker-wrapper').find('.os-day.selected').removeClass('selected');
1584 $day.addClass('selected');
1585 if ($day.closest('.os-dates-and-times-w').hasClass('days-only')) {
1586 $day.closest('.step-recurring-bookings-w').find('input[name="recurrence[rules][repeat_end_date]"]').val($day.data('date'));
1587 window.latepointRecurringBookingsFrontFeature.reload_recurrence_rules($booking_form_element, true);
1588 } else {
1589 latepoint_generate_day_timeslots($day);
1590 $day.closest('.os-recurrence-datepicker-wrapper').find('.time-selector-w')[0].scrollIntoView({block: "nearest", behavior: 'smooth'});
1591 }
1592 } else {
1593 // regular datepicker
1594 if ($day.closest('.os-monthly-calendar-days-w').hasClass('hide-if-single-slot')) {
1595 // HIDE TIMESLOT IF ONLY ONE TIMEPOINT
1596 if ($day.hasClass('os-not-available')) {
1597 // clicked on a day that has no available timeslots
1598 // do nothing
1599 } else {
1600 $booking_form_element.find('.os-day.selected').removeClass('selected');
1601 $day.addClass('selected');
1602 // set date
1603 $booking_form_element.find('.latepoint_start_date').val($day.data('date'));
1604 if ($day.hasClass('os-one-slot-only')) {
1605 // clicked on a day that has only one slot available
1606 var bookable_minutes = $day.data('bookable-minutes').toString().split(':')[0];
1607 var selected_timeslot_time = latepoint_format_minutes_to_time(Number(bookable_minutes), Number($day.data('service-duration')));
1608 $booking_form_element.find('.latepoint_start_time').val($day.data('bookable-minutes'));
1609 latepoint_show_next_btn($booking_form_element);
1610 $booking_form_element.find('.time-selector-w').slideUp(200);
1611 } else {
1612 // regular day with more than 1 timeslots available
1613 // build timeslots
1614 latepoint_generate_day_timeslots($day);
1615 // clear time and hide next btn
1616 $booking_form_element.find('.latepoint_start_time').val('');
1617 latepoint_hide_next_btn($booking_form_element);
1618 }
1619 latepoint_reload_summary($booking_form_element);
1620 }
1621 } else {
1622
1623 // SHOW TIMESLOTS EVEN IF ONLY ONE TIMEPOINT
1624 $booking_form_element.find('.latepoint_start_date').val($day.data('date'));
1625 $booking_form_element.find('.os-day.selected').removeClass('selected');
1626 $day.addClass('selected');
1627
1628 // build timeslots
1629 latepoint_generate_day_timeslots($day);
1630 // clear time and hide next btn
1631 latepoint_reload_summary($booking_form_element);
1632 $booking_form_element.find('.latepoint_start_time').val('');
1633 latepoint_hide_next_btn($booking_form_element);
1634 }
1635 }
1636
1637
1638 return false;
1639 }
1640
1641 async function latepoint_init_step_datepicker($booking_form_element = false) {
1642 if (!$booking_form_element) return true;
1643 latepoint_init_timeslots($booking_form_element);
1644 latepoint_init_monthly_calendar_navigation($booking_form_element);
1645 $booking_form_element.off('click', '.os-months .os-day', latepoint_monthly_calendar_day_clicked);
1646 $booking_form_element.on('click', '.os-months .os-day', latepoint_monthly_calendar_day_clicked);
1647 $booking_form_element.off('keydown', '.os-months .os-day', latepoint_monthly_calendar_day_clicked);
1648 $booking_form_element.on('keydown', '.os-months .os-day', latepoint_monthly_calendar_day_clicked);
1649
1650 if ($booking_form_element.find('input[name="booking[start_date]"]').val()) {
1651 $booking_form_element.find('.os-day[data-date="' + $booking_form_element.find('input[name="booking[start_date]"]').val() + '"]').trigger('click');
1652 } else {
1653 if ($booking_form_element.find('.os-dates-and-times-w').hasClass('auto-search')) {
1654 let max_number_of_months_to_check = 24;
1655 let current_year = new Date().getFullYear();
1656 for (let i = 0; i < max_number_of_months_to_check; i++) {
1657 let $active_month = $booking_form_element.find('.os-monthly-calendar-days-w.active');
1658 let searching_month_label = $active_month.data('calendar-month-label');
1659 if ($active_month.data('calendar-year') != current_year) searching_month_label += ' ' + $active_month.data('calendar-year');
1660 $booking_form_element.find('.os-calendar-searching-info span').text(searching_month_label);
1661 // check if active month has any days available for booking
1662 let $first_available = $active_month.find('.os-day.os-month-current').not('.os-not-available').first();
1663 if ($first_available.length) {
1664 break;
1665 } else {
1666 await latepoint_monthly_calendar_load_next_month($booking_form_element);
1667 }
1668 }
1669 }
1670 }
1671 $booking_form_element.find('.os-dates-and-times-w').removeClass('is-searching');
1672 return true;
1673 }
1674
1675
1676 function latepoint_init_step_verify($booking_form_element = false) {
1677 if (!$booking_form_element) return;
1678 $booking_form_element.closest('.latepoint-summary-is-open').removeClass('latepoint-summary-is-open');
1679
1680 $booking_form_element.find('.latepoint-body .load-customer-step-trigger').on('click', function () {
1681 jQuery(this).addClass('os-loading');
1682 latepoint_reload_step($booking_form_element, 'customer');
1683 return false;
1684 });
1685
1686 $booking_form_element.find('.coupon-code-wrapper-on-verify .coupon-code-trigger-on-verify-w a').on('click', function (e) {
1687 jQuery(this).closest('.coupon-code-wrapper-on-verify').addClass('entering-coupon').find('.coupon-code-input').trigger('focus');
1688 return false;
1689 });
1690 $booking_form_element.find('.coupon-code-wrapper-on-verify .coupon-code-input-cancel').on('click', function (e) {
1691 jQuery(this).closest('.coupon-code-wrapper-on-verify').removeClass('entering-coupon');
1692 return false;
1693 });
1694
1695 $booking_form_element.find('.coupon-code-wrapper-on-verify .coupon-code-input-submit').on('click', function (e) {
1696 latepoint_apply_coupon(jQuery(this).closest('.coupon-code-input-w').find('.coupon-code-input'));
1697 return false;
1698 });
1699
1700 $booking_form_element.find('.os-remove-item-from-cart').on('click keydown', function (event) {
1701 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1702 //make sure to clear active cart item so it doesn't add it again on reload!
1703 if (confirm(jQuery(this).data('confirm-text'))) {
1704 latepoint_remove_cart_item(jQuery(this));
1705 }
1706 return false;
1707 });
1708
1709 $booking_form_element.find('.coupon-code-wrapper-on-verify .coupon-code-clear').on('click', function (e) {
1710 latepoint_remove_coupon(jQuery(this));
1711 return false;
1712 });
1713
1714 $booking_form_element.find('.coupon-code-wrapper-on-verify input.coupon-code-input').on('keyup', function (e) {
1715 if (e.which === 13) {
1716 latepoint_apply_coupon(jQuery(this));
1717 return false;
1718 }
1719 });
1720 }
1721
1722
1723 function latepoint_init_step_payment__pay($booking_form_element = false) {
1724 var selected_payment_method = $booking_form_element.find('input[name="cart[payment_method]"]').val();
1725 latepoint_init_payment_method_actions($booking_form_element, selected_payment_method);
1726 }
1727
1728 function latepoint_init_step_payment__portions($booking_form_element = false) {
1729 // Selecting Payment Time
1730 $booking_form_element.find('.lp-payment-trigger-payment-portion-selector').on('click keydown', function (event) {
1731 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1732 var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1733 $booking_form_element.find('input[name="' + jQuery(this).data('holder') + '"]').val(jQuery(this).data('value'));
1734 latepoint_show_prev_btn($booking_form_element);
1735 latepoint_trigger_next_btn($booking_form_element);
1736 return false;
1737 });
1738 }
1739
1740 function latepoint_init_step_payment__times($booking_form_element = false) {
1741 // Selecting Payment Time
1742 $booking_form_element.find('.lp-payment-trigger-payment-time-selector').on('click keydown', function (event) {
1743 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1744 var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1745 $booking_form_element.find('input[name="' + jQuery(this).data('holder') + '"]').val(jQuery(this).data('value'));
1746 latepoint_show_prev_btn($booking_form_element);
1747 latepoint_trigger_next_btn($booking_form_element);
1748 return false;
1749 });
1750 }
1751
1752
1753 function latepoint_init_step_payment__methods($booking_form_element = false) {
1754 // Selecting Payment Time
1755 $booking_form_element.find('.lp-payment-trigger-payment-method-selector').on('click', function (e) {
1756 var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1757 $booking_form_element.find('input[name="' + jQuery(this).data('holder') + '"]').val(jQuery(this).data('value'));
1758 latepoint_show_prev_btn($booking_form_element);
1759 latepoint_trigger_next_btn($booking_form_element);
1760 return false;
1761 });
1762 }
1763
1764 function latepoint_category_item_clicked(event) {
1765 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1766 let $item = jQuery(event.target);
1767
1768 let $booking_form_element = $item.closest('.latepoint-booking-form-element');
1769 latepoint_show_prev_btn($booking_form_element);
1770 $item.closest('.latepoint-step-content').addClass('selecting-item-category');
1771 let $category_wrapper = $item.closest('.os-item-category-w');
1772 let $main_parent = $item.closest('.os-item-categories-main-parent');
1773 if ($category_wrapper.hasClass('selected')) {
1774 $category_wrapper.removeClass('selected');
1775 if ($category_wrapper.parent().closest('.os-item-category-w').length) {
1776 $category_wrapper.parent().closest('.os-item-category-w').addClass('selected');
1777 } else {
1778 $main_parent.removeClass('show-selected-only');
1779 }
1780 } else {
1781 $main_parent.find('.os-item-category-w.selected').removeClass('selected');
1782 $main_parent.addClass('show-selected-only');
1783 $category_wrapper.addClass('selected');
1784 }
1785 return false;
1786 }
1787
1788 function latepoint_init_step_category_items(step_code) {
1789 let $category_items = jQuery('.latepoint-step-content[data-step-code="' + step_code + '"] .os-item-category-info');
1790 $category_items.on('click', latepoint_category_item_clicked);
1791 $category_items.on('keydown', latepoint_category_item_clicked);
1792 }
1793
1794
1795 function latepoint_init_step_selectable_items($booking_form_element) {
1796 $booking_form_element.off('click', '.os-selectable-items .os-selectable-item', latepoint_selectable_item_clicked);
1797 $booking_form_element.on('click', '.os-selectable-items .os-selectable-item', latepoint_selectable_item_clicked);
1798
1799 $booking_form_element.off('click', '.os-selectable-items .os-selectable-item .item-quantity-selector-input', latepoint_selectable_item_quantity_keyup);
1800 $booking_form_element.on('click', '.os-selectable-items .os-selectable-item .item-quantity-selector-input', latepoint_selectable_item_quantity_keyup);
1801
1802
1803 $booking_form_element.off('keydown', '.os-selectable-items .os-selectable-item', latepoint_selectable_item_clicked);
1804 $booking_form_element.on('keydown', '.os-selectable-items .os-selectable-item', latepoint_selectable_item_clicked);
1805 }
1806
1807
1808 function latepoint_update_quantity_for_selectable_items($item) {
1809 var ids = $item.closest('.os-selectable-items')
1810 .find('.os-selectable-item.selected')
1811 .map(function () {
1812 if (jQuery(this).hasClass('has-quantity')) {
1813 return jQuery(this).data('item-id') + ':' + jQuery(this).find('input.item-quantity-selector-input').val();
1814 } else {
1815 return jQuery(this).data('item-id');
1816 }
1817 }).get();
1818 $item.closest('.latepoint-booking-form-element').find($item.data('id-holder')).val(ids);
1819 }
1820
1821 function latepoint_selectable_item_quantity_keyup(event) {
1822 var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1823 var $item = jQuery(this).closest('.os-selectable-item');
1824 var new_value = jQuery(this).val();
1825 if (new_value && new_value.match(/^\d+$/)) {
1826 var max_quantity = $item.data('max-quantity');
1827 if (max_quantity && (new_value > max_quantity)) new_value = max_quantity;
1828 } else {
1829 new_value = 0;
1830 }
1831 jQuery(this).val(new_value);
1832
1833 if (($item.hasClass('selected') && (new_value > 0)) || (!$item.hasClass('selected') && (new_value == 0))) {
1834 latepoint_update_quantity_for_selectable_items($item);
1835 latepoint_reload_summary($booking_form_element);
1836 return false;
1837 } else {
1838 $item.trigger('click');
1839 }
1840 }
1841
1842 function latepoint_selectable_item_clicked(event) {
1843 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
1844 event.stopPropagation();
1845 event.stopImmediatePropagation();
1846 var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1847 if (jQuery(this).hasClass('has-quantity')) {
1848 if (jQuery(event.target).hasClass('item-quantity-selector')) {
1849 var current_value = parseInt(jQuery(this).find('input.item-quantity-selector-input').val());
1850 var new_value = (jQuery(event.target).data('sign') == 'minus') ? current_value - 1 : current_value + 1;
1851 var max_quantity = jQuery(this).data('max-quantity');
1852 if (new_value < 0) new_value = 0;
1853 if (max_quantity && (new_value > max_quantity)) new_value = max_quantity;
1854 jQuery(this).find('input.item-quantity-selector-input').val(new_value);
1855 if ((jQuery(this).hasClass('selected') && (new_value > 0)) || (!jQuery(this).hasClass('selected') && (new_value == 0))) {
1856 latepoint_update_quantity_for_selectable_items(jQuery(this));
1857 latepoint_reload_summary($booking_form_element);
1858 return false;
1859 }
1860 }
1861 if (jQuery(event.target).hasClass('item-quantity-selector-input')) {
1862 latepoint_update_quantity_for_selectable_items(jQuery(this));
1863 latepoint_reload_summary($booking_form_element);
1864 return false;
1865 }
1866 }
1867 var summary_value = '';
1868 if (jQuery(this).hasClass('os-allow-multiselect')) {
1869 if (jQuery(this).hasClass('selected')) {
1870 jQuery(this).removeClass('selected');
1871 if (jQuery(this).hasClass('has-quantity')) jQuery(this).find('input.item-quantity-selector-input').val(0);
1872 } else {
1873 jQuery(this).addClass('selected');
1874 if (jQuery(this).hasClass('has-quantity') && !(jQuery(this).find('input.item-quantity-selector-input').val() > 0)) {
1875 jQuery(this).find('input.item-quantity-selector-input').val(1);
1876 }
1877 }
1878 latepoint_update_quantity_for_selectable_items(jQuery(this));
1879 latepoint_reload_summary($booking_form_element);
1880 latepoint_show_next_btn($booking_form_element);
1881 } else {
1882 if (!jQuery(this).hasClass('os-duration-item')) jQuery(this).closest('.os-item-categories-main-parent').find('.os-selectable-item.selected').removeClass('selected');
1883 jQuery(this).closest('.os-selectable-items').find('.os-selectable-item.selected').removeClass('selected');
1884 jQuery(this).addClass('selected');
1885 $booking_form_element.find(jQuery(this).data('id-holder')).val(jQuery(this).data('item-id'));
1886 if (jQuery(this).data('cart-item-item-data-key')) {
1887 latepoint_update_active_cart_item_item_data($booking_form_element, jQuery(this).data('cart-item-item-data-key'), jQuery(this).data('item-id'));
1888 }
1889 if (jQuery(this).data('os-call-func')) {
1890 window[jQuery(this).data('os-call-func')](jQuery(this));
1891 }
1892 if (jQuery(this).data('activate-sub-step')) {
1893 window[jQuery(this).data('activate-sub-step')](jQuery(this));
1894 } else {
1895 latepoint_trigger_next_btn($booking_form_element);
1896 }
1897 }
1898 return false;
1899 }
1900
1901 function latepoint_update_active_cart_item_item_data($booking_form_element, key, value) {
1902 let item_data_json = $booking_form_element.find('input[name="active_cart_item[item_data]"]').val();
1903 let item_data = item_data_json ? JSON.parse($booking_form_element.find('input[name="active_cart_item[item_data]"]').val()) : {};
1904 item_data[key] = value;
1905 $booking_form_element.find('input[name="active_cart_item[item_data]"]').val(JSON.stringify(item_data));
1906 }
1907
1908 function latepoint_format_price(price) {
1909 // replace default decimal separator dot with comma if it's in settings
1910 if (latepoint_helper.decimal_separator == ',') price = String(price).replace('.', ',');
1911 return latepoint_helper.currency_symbol_before + String(price) + latepoint_helper.currency_symbol_after;
1912 }
1913
1914
1915 function latepoint_init_step_services() {
1916 }
1917
1918
1919 function latepoint_trigger_next_btn($booking_form_element) {
1920 $booking_form_element.find('.latepoint_step_direction').val('next');
1921 latepoint_submit_booking_form($booking_form_element.find('.latepoint-form'));
1922 }
1923
1924 function latepoint_init_step_locations() {
1925 }
1926
1927 function latepoint_init_agent_details_link($booking_form_element) {
1928 $booking_form_element.on('click', '.os-trigger-item-details-popup', function () {
1929 $booking_form_element.find('.os-item-details-popup.open').remove();
1930 var $popup = $booking_form_element.find('#' + jQuery(this).data('item-details-popup-id')).first().clone().attr('id', '');
1931 $booking_form_element.find('.latepoint-form-w').addClass('showing-item-details-popup');
1932 $popup.addClass('open').appendTo($booking_form_element.find('.latepoint-body'));
1933 return false;
1934 });
1935 $booking_form_element.on('click', '.os-item-details-popup.open .os-item-details-popup-close', function () {
1936 $booking_form_element.find('.latepoint-form-w').removeClass('showing-item-details-popup');
1937 jQuery(this).closest('.os-item-details-popup.open').remove();
1938 return false;
1939 });
1940 }
1941
1942 function latepoint_init_step_agents() {
1943 }
1944
1945
1946 function latepoint_init_booking_summary_lightbox() {
1947 let $lightbox = jQuery('.customer-dashboard-booking-summary-lightbox');
1948 latepoint_init_qr_trigger($lightbox);
1949 latepoint_init_item_details_popup($lightbox);
1950 }
1951
1952 function latepoint_init_step_confirmation($booking_form_element = false) {
1953 if (!$booking_form_element) return;
1954 $booking_form_element.on('click', '.set-customer-password-btn', function () {
1955 let $btn = jQuery(this);
1956 let $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1957
1958 $btn.addClass('os-loading');
1959 let params = {
1960 password_nonce: jQuery('input[name="new_password_nonce"]').val(),
1961 password: jQuery('input[name="customer[password]"]').val()
1962 }
1963 let data = {
1964 action: latepoint_helper.route_action,
1965 route_name: jQuery(this).data('btn-action'),
1966 params: jQuery.param(params),
1967 layout: 'none',
1968 return_format: 'json'
1969 }
1970 jQuery.ajax({
1971 type: "post",
1972 dataType: "json",
1973 url: latepoint_timestamped_ajaxurl(),
1974 data: data,
1975 success: function (data) {
1976 $btn.removeClass('os-loading');
1977 if (data.status === "success") {
1978 $booking_form_element.find('.step-confirmation-set-password').html('').hide();
1979 $booking_form_element.find('.confirmation-cabinet-info').show();
1980 } else {
1981 latepoint_show_message_inside_element(data.message, $booking_form_element.find('.step-confirmation-set-password'), 'error');
1982 }
1983 }
1984 });
1985 return false;
1986 });
1987
1988 $booking_form_element.on('click', '.qr-show-trigger', function () {
1989 var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1990 $booking_form_element.find('.qr-code-on-full-summary').addClass('show-vevent-qr-code');
1991 return false;
1992 });
1993
1994 $booking_form_element.on('click', '.show-set-password-fields', function () {
1995 var $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
1996
1997 $booking_form_element.find('.step-confirmation-set-password').show();
1998 $booking_form_element.find('#customer_password').trigger('focus');
1999 jQuery(this).closest('.info-box').hide();
2000 return false;
2001 });
2002 }
2003
2004 function latepoint_init_customer_login_form(){
2005 if(jQuery('.latepoint-login-form-w').length){
2006 latepoint_init_auth_form(jQuery('.latepoint-login-form-w'));
2007 }
2008 }
2009
2010 function latepoint_init_customer_dashboard() {
2011 latepoint_init_form_masks();
2012 jQuery('.latepoint-customer-timezone-selector-w select').on('change', function (e) {
2013 var $select_box = jQuery(this);
2014 $select_box.closest('.latepoint-customer-timezone-selector-w').addClass('os-loading');
2015 var data = {
2016 action: latepoint_helper.route_action,
2017 route_name: jQuery(this).closest('.latepoint-customer-timezone-selector-w').data('route-name'),
2018 params: {timezone_name: jQuery(this).val()},
2019 layout: 'none',
2020 return_format: 'json'
2021 }
2022 jQuery.ajax({
2023 type: "post",
2024 dataType: "json",
2025 url: latepoint_timestamped_ajaxurl(),
2026 data: data,
2027 success: function (data) {
2028 $select_box.closest('.latepoint-customer-timezone-selector-w').removeClass('os-loading');
2029 if (data.status === "success") {
2030 location.reload();
2031 } else {
2032
2033 }
2034 }
2035 });
2036 });
2037
2038
2039 jQuery('.latepoint-request-booking-cancellation').on('click', function () {
2040 if (!confirm(latepoint_helper.cancel_booking_prompt)) return false;
2041 var $this = jQuery(this);
2042 var $booking_box = $this.closest('.customer-booking');
2043
2044 var route = jQuery(this).data('route');
2045 var params = {id: $booking_box.data('id')};
2046
2047 var data = {
2048 action: latepoint_helper.route_action,
2049 route_name: route,
2050 params: params,
2051 layout: 'none',
2052 return_format: 'json'
2053 }
2054 $this.addClass('os-loading');
2055 jQuery.ajax({
2056 type: "post",
2057 dataType: "json",
2058 url: latepoint_timestamped_ajaxurl(),
2059 data: data,
2060 success: function (data) {
2061 if (data.status === "success") {
2062 $this.remove();
2063 location.reload();
2064 } else {
2065 $this.removeClass('os-loading');
2066 }
2067 }
2068 });
2069 return false;
2070 });
2071
2072 }
2073
2074
2075 function latepoint_get_customer_name($wrapper) {
2076 var customer_name = '';
2077 var first_name = $wrapper.find('input[name="customer[first_name]"]').val();
2078 var last_name = $wrapper.find('input[name="customer[last_name]"]').val();
2079 if (first_name) customer_name += first_name;
2080 if (last_name) customer_name += ' ' + last_name;
2081 return customer_name.trim();
2082 }
2083
2084 function latepoint_init_customer_otp_code_verify_form($booking_form_element) {
2085 let $verify_otp_btn = $booking_form_element.find('.latepoint-verify-otp-button');
2086 let $verify_otp_input = $booking_form_element.find('.os-otp-code-field');
2087
2088 if ($verify_otp_btn.length && $verify_otp_input.length) {
2089 $verify_otp_input.trigger('focus');
2090
2091 // trigger on enter/space to emulate a button
2092 $verify_otp_btn.on('keydown', function (e) {
2093 if (e.key === 'Enter' || e.key === ' ') {
2094 e.preventDefault();
2095 jQuery(this).trigger('click'); // Triggers the click handler above
2096 }
2097 });
2098 // close
2099 $booking_form_element.find('.latepoint-customer-otp-resend').on('click', function (e) {
2100 e.preventDefault();
2101 latepoint_request_customer_otp_code($booking_form_element, jQuery(this));
2102 });
2103
2104 $booking_form_element.find('.latepoint-customer-otp-close').on('click', function () {
2105 $booking_form_element.find('.latepoint-customer-otp-input-container').html('');
2106 $booking_form_element.find('.hide-when-entering-otp').removeClass('os-hidden');
2107 latepoint_show_booking_form_footer($booking_form_element);
2108 return false;
2109 });
2110
2111 // input in code field
2112 $verify_otp_input.on('input', function () {
2113 // Remove non-numeric characters
2114 const cleanValue = jQuery(this).val().replace(/[^0-9]/g, '');
2115 jQuery(this).val(cleanValue);
2116 if (cleanValue.length === 6) {
2117 $verify_otp_btn.trigger('click');
2118 }
2119 });
2120 $verify_otp_input.on('keydown', function (e) {
2121 if (e.key === 'Enter') {
2122 e.preventDefault();
2123 $verify_otp_btn.trigger('click');
2124 }
2125 });
2126
2127 $verify_otp_btn.on('click', function (e) {
2128 return latepoint_verify_customer_otp_code($booking_form_element);
2129 });
2130 }
2131
2132 }
2133
2134 async function latepoint_verify_customer_otp_code($booking_form_element) {
2135 let $verify_otp_btn = $booking_form_element.find('.latepoint-verify-otp-button');
2136 let $verify_otp_input = $booking_form_element.find('.os-otp-code-field');
2137
2138 $verify_otp_input.removeClass('os-invalid');
2139 if ($verify_otp_input.val().length !== 6) {
2140 $verify_otp_input.addClass('os-invalid').addClass('os-shake');
2141 return false;
2142 }
2143
2144 $verify_otp_btn.addClass('os-loading');
2145
2146 let form_data = new FormData($booking_form_element.find('.latepoint-form')[0]);
2147
2148 let data = {
2149 action: 'latepoint_route_call',
2150 route_name: $verify_otp_btn.data('route'),
2151 params: latepoint_formdata_to_url_encoded_string(form_data),
2152 layout: 'none',
2153 return_format: 'json'
2154 }
2155 try {
2156 let response = await jQuery.ajax({
2157 type: "post",
2158 dataType: "json",
2159 url: latepoint_timestamped_ajaxurl(),
2160 data: data
2161 });
2162 $verify_otp_btn.removeClass('os-loading');
2163 if (response.status === 'success') {
2164 if($booking_form_element.data('success-action') === 'reload'){
2165 location.reload();
2166 }else{
2167 latepoint_hide_message_inside_element($booking_form_element.find('.latepoint-customer-otp-input-code-wrapper'));
2168 $booking_form_element.find('input[name="customer_contact_verification_token"]').val(response.message);
2169 latepoint_reload_step($booking_form_element);
2170 }
2171 } else {
2172 latepoint_show_message_inside_element(response.message, $booking_form_element.find('.latepoint-customer-otp-input-code-wrapper'), 'error');
2173 }
2174 return false;
2175 } catch (e) {
2176 latepoint_show_message_inside_element('Error generating OTP', $booking_form_element.find('.latepoint-customer-otp-input-code-wrapper'), 'error');
2177 throw e;
2178 }
2179 }
2180
2181 async function latepoint_show_verify_contact_form_with_otp_code($booking_form_element, form_html) {
2182 $booking_form_element.find('.latepoint-customer-otp-input-container').html(form_html);
2183 $booking_form_element.find('.hide-when-entering-otp').addClass('os-hidden');
2184 $booking_form_element.removeClass('step-content-loading').addClass('step-content-loaded');
2185 $booking_form_element.find('.latepoint-next-btn, .latepoint-prev-btn').removeClass('os-loading');
2186 latepoint_init_customer_otp_code_verify_form($booking_form_element);
2187 latepoint_hide_booking_form_footer($booking_form_element);
2188 }
2189
2190 function latepoint_hide_booking_form_footer($booking_form_element) {
2191 $booking_form_element.addClass('hidden-buttons');
2192 }
2193
2194 function latepoint_show_booking_form_footer($booking_form_element) {
2195 $booking_form_element.removeClass('hidden-buttons');
2196 }
2197
2198 async function latepoint_request_customer_otp_code($booking_form_element, $request_otp_button) {
2199 $request_otp_button.addClass('os-loading');
2200
2201 let form_data = new FormData($booking_form_element.find('.latepoint-form')[0]);
2202
2203
2204 // get values from phone number fields
2205 if (('lp_intlTelInputGlobals' in window) && ('lp_intlTelInputUtils' in window)) {
2206 $booking_form_element.find('input.os-mask-phone').each(function () {
2207 const phoneInputName = this.getAttribute('name');
2208 const phoneInputValue = window.lp_intlTelInputGlobals.getInstance(this).getNumber(window.lp_intlTelInputUtils.numberFormat.E164);
2209 // override value generated automatically by formdata with a formatted value of a phone field with country code
2210 form_data.set(phoneInputName, phoneInputValue);
2211 });
2212 }
2213
2214 let data = {
2215 action: 'latepoint_route_call',
2216 route_name: $request_otp_button.data('otp-request-route'),
2217 params: latepoint_formdata_to_url_encoded_string(form_data),
2218 layout: 'none',
2219 return_format: 'json'
2220 }
2221 try {
2222 let response = await jQuery.ajax({
2223 type: "post",
2224 dataType: "json",
2225 url: latepoint_timestamped_ajaxurl(),
2226 data: data
2227 });
2228 $request_otp_button.removeClass('os-loading');
2229 if (response.status === 'success') {
2230 latepoint_hide_message_inside_element($booking_form_element.find('.hide-when-entering-otp'));
2231 $booking_form_element.find('.hide-when-entering-otp').addClass('os-hidden');
2232 $booking_form_element.find('.latepoint-customer-otp-input-container').html(response.message);
2233 latepoint_init_customer_otp_code_verify_form($booking_form_element);
2234 } else {
2235 latepoint_show_message_inside_element(response.message, $booking_form_element.find('.hide-when-entering-otp'), 'error');
2236 }
2237 return false;
2238 } catch (e) {
2239 latepoint_show_message_inside_element('Error generating OTP', $booking_form_element.find('.hide-when-entering-otp'), 'error');
2240 throw e;
2241 }
2242 }
2243
2244 function latepoint_customer_auth_load_next_step($booking_form_element) {
2245
2246 }
2247
2248 function latepoint_init_customer_otp_code_request_form($booking_form_element) {
2249 let $request_otp_button = $booking_form_element.find('.latepoint-request-otp-button');
2250 if ($request_otp_button.length > 0) {
2251 $request_otp_button.on('click', function (e) {
2252 return latepoint_request_customer_otp_code($booking_form_element, jQuery(this));
2253 });
2254 // trigger on enter/space to emulate a button
2255 $request_otp_button.on('keydown', function (e) {
2256 if (e.key === 'Enter' || e.key === ' ') {
2257 e.preventDefault();
2258 jQuery(this).trigger('click'); // Triggers the click handler above
2259 }
2260 });
2261 $booking_form_element.find('.latepoint-customer-otp-request-wrapper').find('input, select').on('keydown', function (e) {
2262 if (e.key === 'Enter') {
2263 e.preventDefault();
2264 $request_otp_button.trigger('click');
2265 }
2266 });
2267 }
2268 }
2269
2270 async function latepoint_login_customer($booking_form_element, $request_otp_button) {
2271
2272 $request_otp_button.addClass('os-loading');
2273 let $form = $booking_form_element.find('.latepoint-form');
2274
2275 let form_data = new FormData($form[0]);
2276
2277 // get values from phone number fields
2278 if (('lp_intlTelInputGlobals' in window) && ('lp_intlTelInputUtils' in window)) {
2279 $form.find('input.os-mask-phone').each(function () {
2280 const phoneInputName = this.getAttribute('name');
2281 const phoneInputValue = window.lp_intlTelInputGlobals.getInstance(this).getNumber(window.lp_intlTelInputUtils.numberFormat.E164);
2282 // override value generated automatically by formdata with a formatted value of a phone field with country code
2283 form_data.set(phoneInputName, phoneInputValue);
2284 });
2285 }
2286
2287 let data = {
2288 action: 'latepoint_route_call',
2289 route_name: $request_otp_button.data('password-login-route'),
2290 params: latepoint_formdata_to_url_encoded_string(form_data),
2291 layout: 'none',
2292 return_format: 'json'
2293 }
2294 try {
2295 let response = await jQuery.ajax({
2296 type: "post",
2297 dataType: "json",
2298 url: latepoint_timestamped_ajaxurl(),
2299 data: data
2300 });
2301 $request_otp_button.removeClass('os-loading');
2302
2303 if (response.status === "success") {
2304 if($booking_form_element.data('success-action') === 'reload'){
2305 location.reload();
2306 return true;
2307 }else {
2308 return latepoint_reload_step($booking_form_element);
2309 }
2310 } else {
2311 throw new Error(response.message);
2312 }
2313 } catch (e) {
2314 latepoint_show_message_inside_element(e.message, $booking_form_element.find('.os-step-existing-customer-login-w'));
2315 }
2316
2317 }
2318
2319
2320 function latepoint_init_auth_form($wrapper){
2321
2322 latepoint_init_customer_otp_code_request_form($wrapper);
2323 latepoint_init_customer_otp_code_verify_form($wrapper);
2324
2325
2326 $wrapper.find('.login-with-password-toggle').on('change', function () {
2327 if (jQuery(this).hasClass('os-opposite')) {
2328 if (jQuery(this).is(':checked')) {
2329 $wrapper.find('.os-customer-otp-notice').hide();
2330 $wrapper.find('.os-customer-login-password-fields-w').show();
2331 } else {
2332 $wrapper.find('.os-customer-otp-notice').show();
2333 $wrapper.find('.os-customer-login-password-fields-w').hide();
2334 }
2335 } else {
2336 if (jQuery(this).is(':checked')) {
2337 $wrapper.find('.os-customer-otp-notice').show();
2338 $wrapper.find('.os-customer-login-password-fields-w').hide();
2339 } else {
2340 $wrapper.find('.os-customer-otp-notice').hide();
2341 $wrapper.find('.os-customer-login-password-fields-w').show();
2342 }
2343 }
2344 });
2345
2346 $wrapper.find('.alternative-login-option').on('click', function () {
2347 let login_method = jQuery(this).data('auth-via');
2348 let delivery_method = jQuery(this).data('otp-delivery-method');
2349 $wrapper.find('input[name="auth[contact_type]"]').val(login_method);
2350 $wrapper.find('input[name="auth[delivery_method]"]').val(delivery_method);
2351 $wrapper.find('.customer-login-method-wrapper').addClass('os-hidden');
2352 $wrapper.find('.customer-login-method-wrapper[data-login-method="' + login_method + '"]').removeClass('os-hidden');
2353 $wrapper.find('.alternative-login-option').removeClass('os-hidden');
2354 jQuery(this).addClass('os-hidden');
2355 return false;
2356 });
2357
2358 $wrapper.find('.login-options-via-wrapper .login-option').on('click', function () {
2359 let $wrapper = jQuery(this).closest('.latepoint-customer-auth-options-wrapper');
2360 let login_method = jQuery(this).data('login-method');
2361 let delivery_method = jQuery(this).data('otp-delivery-method');
2362
2363 let otp_enabled = jQuery(this).data('is-otp-enabled');
2364 if(otp_enabled === 'no'){
2365 if($wrapper.find('.login-with-password-toggle.os-opposite:not(:checked)').length){
2366 $wrapper.find('.login-with-password-toggle.os-opposite').trigger('click');
2367 }
2368 $wrapper.find('.latepoint-customer-otp-option').hide();
2369 $wrapper.find('.step-login-existing-customer-btn').addClass('latepoint-btn-block');
2370 }else{
2371 if($wrapper.find('.latepoint-customer-otp-option').length){
2372 $wrapper.find('.latepoint-customer-otp-option').show();
2373 $wrapper.find('.step-login-existing-customer-btn').removeClass('latepoint-btn-block');
2374 }
2375 }
2376 $wrapper.find('input[name="auth[contact_type]"]').val(login_method);
2377 $wrapper.find('input[name="auth[delivery_method]"]').val(delivery_method);
2378 jQuery(this).closest('.login-options-via-wrapper').find('.login-option.os-selected').removeClass('os-selected')
2379 jQuery(this).addClass('os-selected');
2380 jQuery('.customer-login-method-wrapper').addClass('os-hidden');
2381 jQuery('.customer-login-method-wrapper[data-login-method="' + login_method + '"]').removeClass('os-hidden');
2382
2383 return false;
2384 });
2385
2386 // Init Login Existing Customer Button
2387 $wrapper.find('.os-customer-login-w input').on('keyup', function (e) {
2388 if (e.keyCode === 13) {
2389 e.preventDefault();
2390 $wrapper.find('.step-login-existing-customer-btn').trigger('click');
2391 return false;
2392 }
2393 });
2394
2395 $wrapper.find('.step-login-existing-customer-btn').on('click', function (e) {
2396 e.preventDefault();
2397 let via = $wrapper.find('input[name="auth[via]"]').val();
2398 if ($wrapper.find('input[name="auth[via]"]').is(':checkbox')) {
2399 let is_checked = $wrapper.find('input[name="auth[via]"]').is(':checked');
2400 if (via === 'otp' && !is_checked) {
2401 via = 'password';
2402 } else if (via === 'password' && !is_checked) {
2403 via = 'otp';
2404 }
2405 }
2406 if (via === 'otp') {
2407 latepoint_request_customer_otp_code($wrapper, jQuery(this));
2408 } else if (via === 'password') {
2409 latepoint_login_customer($wrapper, jQuery(this));
2410 }
2411
2412 });
2413 }
2414
2415 function latepoint_init_step_customer($booking_form_element) {
2416 latepoint_init_form_masks();
2417
2418 latepoint_init_auth_form($booking_form_element);
2419
2420 // Init Logout button
2421 $booking_form_element.find('.step-customer-logout-btn').on('click', function () {
2422 let $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
2423 $booking_form_element.find('input[name="customer_contact_verification_token"]').val('');
2424 let data = {
2425 action: latepoint_helper.route_action,
2426 route_name: jQuery(this).data('btn-action'),
2427 layout: 'none',
2428 return_format: 'json'
2429 }
2430 latepoint_step_content_change_start($booking_form_element);
2431 jQuery.ajax({
2432 type: "post",
2433 dataType: "json",
2434 url: latepoint_timestamped_ajaxurl(),
2435 data: data,
2436 success: function (data) {
2437 $booking_form_element.find('input[name="auth[action]"]').val('logout');
2438 latepoint_reload_step($booking_form_element);
2439 }
2440 });
2441 return false;
2442 });
2443
2444
2445 }
2446
2447 function latepoint_step_content_change_start($booking_form_element) {
2448 $booking_form_element.removeClass('step-content-loaded').addClass('step-content-loading');
2449 }
2450
2451 // TODO
2452 function latepoint_step_content_change_end(new_content, $booking_form_element) {
2453 if (new_content) $booking_form_element.find('.latepoint-body .latepoint-step-content').replaceWith(new_content);
2454 $booking_form_element.removeClass('step-content-loading').addClass('step-content-mid-loading');
2455 setTimeout(function () {
2456 $booking_form_element.removeClass('step-content-mid-loading').addClass('step-content-loaded');
2457 }, 50);
2458 }
2459
2460
2461 function latepoint_change_step_desc($booking_form_element, step_code) {
2462 $booking_form_element.removeClass('step-changed').addClass('step-changing');
2463 setTimeout(function () {
2464 // Progress bar
2465 var $step_progress = $booking_form_element.find('.latepoint-progress li[data-step-code="' + step_code + '"]');
2466 $step_progress.addClass('active').addClass('complete').prevAll().addClass('complete').removeClass('active');
2467 $step_progress.nextAll().removeClass('complete').removeClass('active');
2468 // Side panel
2469 var side_panel_desc = $booking_form_element.find('.latepoint-step-desc-library[data-step-code="' + step_code + '"]').html();
2470 $booking_form_element.find('.latepoint-step-desc').html(side_panel_desc);
2471
2472 // Top header
2473 var top_header_desc = $booking_form_element.find('.os-heading-text-library[data-step-code="' + step_code + '"]').html();
2474 $booking_form_element.find('.os-heading-text').html(top_header_desc);
2475 setTimeout(function () {
2476 $booking_form_element.removeClass('step-changing').addClass('step-changed');
2477 }, 50);
2478 }, 500);
2479 }
2480
2481
2482 function latepoint_progress_prev($booking_form_element, step_code) {
2483 var $step_progress = $booking_form_element.find('.latepoint-progress li[data-step-code="' + step_code + '"]');
2484 $step_progress.addClass('active').addClass('complete').prevAll().addClass('complete').removeClass('active');
2485 $step_progress.nextAll().removeClass('complete').removeClass('active');
2486 }
2487
2488
2489 function latepoint_progress_next($booking_form_element, step_code) {
2490 var $step_progress = $booking_form_element.find('.latepoint-progress li[data-step-code="' + step_code + '"]');
2491 $step_progress.addClass('active').addClass('complete').prevAll().addClass('complete').removeClass('active');
2492 $step_progress.nextAll().removeClass('complete').removeClass('active');
2493 }
2494
2495
2496 function latepoint_next_step_description($booking_form_element, step_code) {
2497 $booking_form_element.removeClass('step-changed').addClass('step-changing');
2498 setTimeout(function () {
2499 $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());
2500 $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());
2501 setTimeout(function () {
2502 $booking_form_element.removeClass('step-changing').addClass('step-changed');
2503 }, 50);
2504 }, 500);
2505 }
2506
2507 function latepoint_prev_step_description($booking_form_element, step_code) {
2508 $booking_form_element.removeClass('step-changed').addClass('step-changing');
2509 setTimeout(function () {
2510 $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());
2511 $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());
2512 setTimeout(function () {
2513 $booking_form_element.removeClass('step-changing').addClass('step-changed');
2514 }, 50);
2515 }, 500);
2516 }
2517
2518
2519 function latepoint_validate_fields($fields) {
2520 var is_valid = true;
2521 $fields.each(function (index) {
2522 if (jQuery(this).val() == '') {
2523 is_valid = false;
2524 return false;
2525 }
2526 });
2527 return is_valid;
2528 }
2529
2530
2531 async function latepoint_submit_booking_form($booking_form) {
2532 let $booking_form_element = $booking_form.closest('.latepoint-booking-form-element');
2533
2534 let current_step = $booking_form_element.find('.latepoint_current_step_code').val();
2535 let callbacks_list = [];
2536 if (latepoint_check_if_booking_form_is_final_submit($booking_form_element)) {
2537 // check if order intent is still bookable
2538 latepoint_add_action(callbacks_list, async () => {
2539 return await latepoint_check_if_order_intent_still_bookable($booking_form_element);
2540 }, 1);
2541 }
2542 $booking_form_element.trigger('latepoint:submitBookingForm', [{
2543 current_step: current_step,
2544 callbacks_list: callbacks_list,
2545 is_final_submit: latepoint_check_if_booking_form_is_final_submit($booking_form_element),
2546 direction: $booking_form_element.find('.latepoint_step_direction').val()
2547 }]);
2548 try {
2549 latepoint_hide_prev_btn($booking_form_element);
2550 await latepoint_process_list_of_callbacks(callbacks_list, $booking_form_element, $booking_form);
2551 } catch (error) {
2552 latepoint_show_prev_btn($booking_form_element);
2553 latepoint_show_error_and_stop_loading_booking_form(error, $booking_form_element);
2554 return false;
2555 }
2556
2557
2558 $booking_form_element.removeClass('step-content-loaded').addClass('step-content-loading');
2559 latepoint_hide_prev_btn($booking_form_element);
2560 try {
2561 latepoint_hide_message_inside_element($booking_form_element.find('.latepoint-body'));
2562 let response = await jQuery.ajax({
2563 type: "post",
2564 dataType: "json",
2565 processData: false,
2566 contentType: false,
2567 url: latepoint_timestamped_ajaxurl(),
2568 data: latepoint_create_form_data($booking_form)
2569 });
2570
2571 $booking_form.find('.latepoint_step_direction').val('next');
2572 if (response.status === 'success') {
2573 if (response.fields_to_update) {
2574 for (const [key, value] of Object.entries(response.fields_to_update)) {
2575 $booking_form_element.find('input[name="' + key + '"]').val(value)
2576 }
2577 }
2578 if ($booking_form_element.data('flash-error')) {
2579 latepoint_show_message_inside_element($booking_form_element.data('flash-error'), $booking_form_element.find('.latepoint-body'));
2580 $booking_form_element.data('flash-error', '');
2581 }
2582 $booking_form_element.find('.latepoint_current_step_code').val(response.step_code);
2583 $booking_form_element.removeClass(function (index, className) {
2584 return (className.match(/(^|\s)current-step-\S+/g) || []).join(' ');
2585 }).addClass('current-step-' + response.step_code);
2586 setTimeout(function () {
2587 $booking_form_element.removeClass('step-content-loading').addClass('step-content-mid-loading');
2588 $booking_form_element.find('.latepoint-body').find('.latepoint-step-content').addClass('is-hidden');
2589 if ($booking_form_element.find('.latepoint-step-content[data-step-code="' + response.step_code + '"]')) {
2590 $booking_form_element.find('.latepoint-step-content[data-step-code="' + response.step_code + '"]').remove();
2591 }
2592 $booking_form_element.find('.latepoint-body').append(response.message);
2593
2594
2595 latepoint_update_next_btn_label($booking_form_element);
2596 latepoint_init_step(response.step_code, $booking_form_element);
2597 setTimeout(function () {
2598 $booking_form_element.removeClass('step-content-mid-loading').addClass('step-content-loaded');
2599 $booking_form_element.find('.latepoint-next-btn, .latepoint-prev-btn').removeClass('os-loading');
2600 latepoint_scroll_to_top_of_booking_form($booking_form_element);
2601 }, 50);
2602 }, 500);
2603
2604 if (response.is_pre_last_step) {
2605 $booking_form_element.data('next-submit-is-last', 'yes');
2606 } else {
2607 $booking_form_element.data('next-submit-is-last', 'no');
2608 }
2609 if (response.is_last_step) {
2610 $booking_form_element.addClass('hidden-buttons').find('.latepoint-footer').remove();
2611 $booking_form_element.find('.latepoint-progress').css('opacity', 0);
2612 $booking_form_element.closest('.latepoint-summary-is-open').removeClass('latepoint-summary-is-open');
2613 $booking_form_element.closest('.latepoint-show-side-panel').removeClass('latepoint-show-side-panel').addClass('latepoint-hide-side-panel');
2614 $booking_form_element.addClass('is-final-step');
2615 } else {
2616 if (response.show_next_btn === true) {
2617 latepoint_show_next_btn($booking_form_element);
2618 } else {
2619 latepoint_hide_next_btn($booking_form_element);
2620 }
2621 if (response.show_prev_btn === true) {
2622 latepoint_show_prev_btn($booking_form_element);
2623 } else {
2624 latepoint_hide_prev_btn($booking_form_element);
2625 }
2626 }
2627 latepoint_change_step_desc($booking_form_element, response.step_code);
2628 latepoint_reload_summary($booking_form_element);
2629 } else {
2630 if (response.fields_to_update) {
2631 for (const [key, value] of Object.entries(response.fields_to_update)) {
2632 $booking_form_element.find('input[name="' + key + '"]').val(value)
2633 }
2634 }
2635 if (response.callback) {
2636 let func_name = response.callback;
2637 if (func_name.includes('.')) {
2638 let func_arr = func_name.split('.');
2639 if (typeof window[func_arr[0]][func_arr[1]] !== 'function') {
2640 console.log(func_name + ' is undefined');
2641 }
2642 if (response.callback_data) {
2643 window[func_arr[0]][func_arr[1]]($booking_form_element, response.callback_data);
2644 } else {
2645 window[func_arr[0]][func_arr[1]]($booking_form_element);
2646 }
2647 } else {
2648 if (typeof window[func_name] !== 'function') {
2649 console.log(func_name + ' is undefined');
2650 }
2651 if (response.callback_data) {
2652 window[func_name]($booking_form_element, response.callback_data);
2653 } else {
2654 window[func_name]($booking_form_element);
2655 }
2656 }
2657 } else {
2658 if (response.send_to_step && response.send_to_step === 'resubmit') {
2659 let current_resubmit_count = parseInt($booking_form.data('resubmit-count')) ? parseInt($booking_form.data('resubmit-count')) : 1;
2660 $booking_form.data('resubmit-count', current_resubmit_count + 1);
2661 if (current_resubmit_count > 6) {
2662 latepoint_show_message_inside_element(response.message, $booking_form_element.find('.latepoint-body'));
2663 } else {
2664 // resubmission probably caused by order intent still being processed, since
2665 // order intent is still processing, give it a little more time and try again
2666 await latepoint_sleep(2000);
2667 return latepoint_submit_booking_form($booking_form);
2668 }
2669 } else {
2670 $booking_form_element.removeClass('step-content-loading').addClass('step-content-loaded');
2671 $booking_form_element.find('.latepoint-next-btn, .latepoint-prev-btn').removeClass('os-loading');
2672 if (response.send_to_step && $booking_form_element.find('.latepoint-step-content[data-step-code="' + response.send_to_step + '"]').length) {
2673 $booking_form_element.data('flash-error', response.message);
2674 latepoint_reload_step($booking_form_element, response.send_to_step);
2675 } else {
2676 latepoint_show_message_inside_element(response.message, $booking_form_element.find('.latepoint-body'));
2677 latepoint_show_prev_btn($booking_form_element);
2678 }
2679 }
2680 }
2681 }
2682 } catch (e) {
2683 console.log(e);
2684 alert('Error:' + e);
2685 }
2686 }
2687
2688 function latepoint_sleep(ms) {
2689 return new Promise(resolve => setTimeout(resolve, ms));
2690 }
2691
2692 function latepoint_show_error_and_stop_loading_booking_form(error, $booking_form_element) {
2693 if (error.send_to_step && $booking_form_element.find('.latepoint-step-content[data-step-code="' + error.send_to_step + '"]').length) {
2694 latepoint_reload_step($booking_form_element, error.send_to_step);
2695 $booking_form_element.data('flash-error', error.message);
2696 } else {
2697 latepoint_show_message_inside_element(error.message, $booking_form_element.find('.latepoint-body'), 'error');
2698
2699 if ($booking_form_element.hasClass('step-content-loading')) $booking_form_element.removeClass('step-content-loading').addClass('step-content-loaded');
2700 $booking_form_element.find('.latepoint-next-btn').removeClass('os-loading');
2701
2702 // if previous step exists - show prev button
2703 if ($booking_form_element.find('.latepoint-step-content:last-child').prev('.latepoint-step-content').length) latepoint_show_prev_btn($booking_form_element);
2704 latepoint_scroll_to_top_of_booking_form($booking_form_element);
2705 }
2706 }
2707
2708 function latepoint_reset_active_cart_item($booking_form_element) {
2709 $booking_form_element.find('input[name="active_cart_item[id]"]').val('');
2710 $booking_form_element.find('input[name="active_cart_item[variant]"]').val('');
2711 $booking_form_element.find('input[name="active_cart_item[item_data]"]').val('');
2712 }
2713
2714 function latepoint_check_if_booking_form_is_final_submit($booking_form_element) {
2715 return ($booking_form_element.data('next-submit-is-last') == 'yes');
2716 }
2717
2718
2719 async function latepoint_check_if_order_intent_still_bookable($booking_form_element) {
2720 let response = await jQuery.ajax({
2721 type: "post",
2722 dataType: "json",
2723 processData: false,
2724 contentType: false,
2725 url: latepoint_timestamped_ajaxurl(),
2726 data: latepoint_create_form_data($booking_form_element.find('.latepoint-form'), latepoint_helper.check_order_intent_bookable_route)
2727 });
2728 if (response.status === 'success') {
2729 return true;
2730 } else {
2731 throw new Error(response.message);
2732 }
2733 }
2734
2735 async function latepoint_process_list_of_callbacks(callbacks, $booking_form_element, $booking_form) {
2736 for (const callback of callbacks) {
2737 await callback.action();
2738 }
2739 }
2740
2741 function latepoint_clear_presets($booking_form_element) {
2742 $booking_form_element.find('.clear_for_new_item').val('');
2743 }
2744
2745 function latepoint_init_booking_form($booking_form_element) {
2746 $booking_form_element.on('click keydown', '.checkout-from-summary-panel-btn', function (event) {
2747 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
2748 latepoint_reload_step($booking_form_element, jQuery(this).data('step'));
2749 jQuery(this).closest('.latepoint-w').removeClass('show-summary-on-mobile');
2750 return false;
2751 });
2752
2753 $booking_form_element.on('click keydown', '.latepoint-add-another-item-trigger', function (event) {
2754 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
2755
2756 if (latepoint_helper.reset_presets_when_adding_new_item) {
2757 latepoint_clear_presets($booking_form_element);
2758 }
2759 latepoint_reset_active_cart_item($booking_form_element);
2760 latepoint_reload_step($booking_form_element, jQuery(this).data('step'));
2761 return false;
2762 });
2763 $booking_form_element.find('.latepoint-form').on('submit', function (e) {
2764 e.preventDefault();
2765 let $booking_form = jQuery(this);
2766 latepoint_submit_booking_form($booking_form);
2767 });
2768
2769 latepoint_init_booking_summary_panel($booking_form_element);
2770
2771 $booking_form_element.on('click keydown', '.latepoint-lightbox-summary-trigger', function (event) {
2772 event.preventDefault();
2773 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
2774 let $wrapper = jQuery(this).closest('.latepoint-w');
2775 $wrapper.toggleClass('show-summary-on-mobile');
2776 return false;
2777 });
2778
2779 $booking_form_element.find('.latepoint-lightbox-close').on('click', function () {
2780
2781 let params = new URLSearchParams(location.search);
2782 if (params.has('latepoint_order_intent_key')) {
2783 params.delete('latepoint_order_intent_key');
2784 history.replaceState(null, '', '?' + params + location.hash);
2785 }
2786
2787 jQuery('body').removeClass('latepoint-lightbox-active');
2788 jQuery('.latepoint-lightbox-w').remove();
2789 return false;
2790 });
2791
2792
2793 $booking_form_element.on('click keydown', '.lp-option', function (event) {
2794 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
2795 jQuery(this).closest('.lp-options').find('.lp-option.selected').removeClass('selected');
2796 jQuery(this).addClass('selected');
2797 });
2798
2799
2800 // Next Step button Click
2801 $booking_form_element.find('.latepoint-next-btn').on('click', async function (e) {
2802 e.preventDefault();
2803 if (jQuery(this).hasClass('disabled') || jQuery(this).hasClass('os-loading')) return false;
2804 var $next_btn = jQuery(this);
2805 $next_btn.addClass('os-loading');
2806 var $booking_form = jQuery(this).closest('.latepoint-form');
2807
2808 var current_step = $booking_form_element.find('.latepoint_current_step_code').val();
2809
2810 $booking_form.find('.latepoint_step_direction').val('next');
2811 var callbacks_list = [];
2812
2813 $booking_form_element.trigger('latepoint:nextStepClicked', [{
2814 current_step: current_step,
2815 callbacks_list: callbacks_list
2816 }]);
2817 latepoint_hide_prev_btn($booking_form_element);
2818
2819 try {
2820 await latepoint_process_list_of_callbacks(callbacks_list, $booking_form_element, $booking_form);
2821 await latepoint_submit_booking_form($booking_form);
2822 } catch (error) {
2823 latepoint_show_error_and_stop_loading_booking_form(error, $booking_form_element);
2824 }
2825 return false;
2826 });
2827
2828
2829 // Previous Step button Click
2830 $booking_form_element.find('.latepoint-prev-btn').on('click', function (e) {
2831 if (jQuery(this).hasClass('disabled') || jQuery(this).hasClass('os-loading')) return false;
2832
2833 let $current_step = $booking_form_element.find('.latepoint-step-content:last-child');
2834
2835
2836 // handle categories
2837 if ($current_step.hasClass('selecting-item-category')) {
2838 if ($current_step.find('.os-item-category-w .os-item-category-w.selected').length) {
2839 $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');
2840 } else {
2841 $current_step.removeClass('selecting-item-category').find('.os-item-category-w.selected').removeClass('selected');
2842 $current_step.removeClass('selecting-item-category').find('.os-item-categories-holder.show-selected-only').removeClass('show-selected-only');
2843 }
2844 if (($booking_form_element.find('.latepoint-step-content').length <= 1) && !$current_step.hasClass('selecting-item-category')) {
2845 latepoint_hide_prev_btn($booking_form_element);
2846 }
2847 latepoint_reload_summary($booking_form_element);
2848 return false;
2849 }
2850
2851 if ($current_step.data('clear-action')) {
2852 window[$current_step.data('clear-action')]($booking_form_element);
2853 }
2854
2855 let $back_btn = jQuery(this);
2856 $back_btn.addClass('os-loading');
2857 $booking_form_element.removeClass('step-content-loaded').addClass('step-content-loading');
2858 let $new_current_step = $booking_form_element.find('.latepoint-step-content.is-hidden').last();
2859 let new_current_step_code = $new_current_step.data('step-code');
2860 let current_step_code = $current_step.data('step-code');
2861
2862
2863 let current_parent_code_name = current_step_code.split('__')[0];
2864 let new_parent_code_name = new_current_step_code.split('__')[0];
2865
2866 let active_cart_item_id = $booking_form_element.find('input[name="active_cart_item[id]"]').val();
2867
2868 latepoint_change_step_desc($booking_form_element, new_current_step_code);
2869 setTimeout(function () {
2870 $new_current_step.removeClass('is-hidden');
2871 $current_step.remove();
2872 $booking_form_element.find('.latepoint_current_step_code').val(new_current_step_code);
2873 $booking_form_element.removeClass(function (index, className) {
2874 return (className.match(/(^|\s)current-step-\S+/g) || []).join(' ');
2875 }).addClass('current-step-' + new_current_step_code);
2876 $booking_form_element.find('.latepoint-next-btn span').text($booking_form_element.find('.latepoint-next-btn').data('label'));
2877 $booking_form_element.data('next-submit-is-last', 'no');
2878
2879 latepoint_update_next_btn_label($booking_form_element);
2880 latepoint_show_next_btn($booking_form_element);
2881 $back_btn.removeClass('os-loading');
2882 if ($booking_form_element.find('.latepoint-step-content').length <= 1) {
2883 if ($new_current_step.hasClass('selecting-item-category')) {
2884
2885 }
2886 if (new_current_step_code == 'booking__services') {
2887 var $services_step = $booking_form_element.find('.step-services-w');
2888 if ($services_step.hasClass('selecting-item-category')) {
2889 if ($services_step.find('.os-services > .os-item.selected').hasClass('is-preselected')) {
2890 // if service is preselected check if there are both multiple durations and quantity selector and only then show prev button
2891 } else {
2892 latepoint_show_prev_btn($booking_form_element);
2893 }
2894 } else {
2895 latepoint_hide_prev_btn($booking_form_element);
2896 }
2897 } else {
2898 if (!$new_current_step.hasClass('selecting-item-category')) {
2899 latepoint_hide_prev_btn($booking_form_element);
2900 }
2901 }
2902 }
2903 $booking_form_element.removeClass('step-content-loading').addClass('step-content-mid-loading');
2904
2905
2906 if (new_parent_code_name == 'booking' && current_parent_code_name != 'booking' && active_cart_item_id) {
2907
2908 // 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
2909 $booking_form_element.find('.latepoint-summary-w').addClass('os-loading');
2910 let data = {
2911 action: latepoint_helper.route_action,
2912 route_name: latepoint_helper.remove_cart_item_route,
2913 params: jQuery.param({cart_item_id: active_cart_item_id}),
2914 layout: 'none',
2915 return_format: 'json'
2916 }
2917 jQuery.ajax({
2918 type: "post",
2919 dataType: "json",
2920 url: latepoint_timestamped_ajaxurl(),
2921 data: data,
2922 success: function (data) {
2923 if (data.status === "success") {
2924 $booking_form_element.find('input[name="active_cart_item[id]"]').val('');
2925 if ($booking_form_element.find('input[name="active_cart_item[variant]"]').val() == 'bundle') {
2926 latepoint_update_active_cart_item_item_data($booking_form_element, 'bundle_id', '');
2927 $booking_form_element.find('input[name="active_cart_item[variant]"]').val('');
2928 }
2929 latepoint_reload_summary($booking_form_element);
2930 } else {
2931 $booking_form_element.find('.latepoint-summary-w').removeClass('os-loading');
2932 latepoint_show_message_inside_element(data.message, $booking_form_element.find('.latepoint-body'), 'error');
2933 }
2934 }
2935 });
2936 } else {
2937 latepoint_reload_summary($booking_form_element);
2938 }
2939 setTimeout(function () {
2940 $booking_form_element.removeClass('step-content-mid-loading').addClass('step-content-loaded');
2941 latepoint_hide_message_inside_element($booking_form_element.find('.latepoint-body'));
2942 latepoint_scroll_to_top_of_booking_form($booking_form_element);
2943
2944 let callbacks_list = [];
2945 $booking_form_element.trigger('latepoint:prevStepReInit', [{
2946 current_step: new_current_step_code,
2947 callbacks_list: callbacks_list
2948 }]);
2949 }, 150);
2950 }, 700);
2951 return false;
2952 });
2953
2954 latepoint_init_agent_details_link($booking_form_element);
2955 $booking_form_element.trigger('latepoint:initBookingForm');
2956 }
2957
2958
2959 function latepoint_init_booking_form_by_trigger($trigger) {
2960 let route = latepoint_helper.booking_button_route;
2961 let params = {};
2962 let restrictions = {};
2963 let presets = {};
2964 let booking_element_styles = {};
2965 if ($trigger.data('show-service-categories')) restrictions.show_service_categories = $trigger.data('show-service-categories');
2966 if ($trigger.data('show-locations')) restrictions.show_locations = $trigger.data('show-locations');
2967 if ($trigger.data('show-services')) restrictions.show_services = $trigger.data('show-services');
2968 if ($trigger.data('show-agents')) restrictions.show_agents = $trigger.data('show-agents');
2969 if ($trigger.data('calendar-start-date')) restrictions.calendar_start_date = $trigger.data('calendar-start-date');
2970
2971 if ($trigger.data('selected-location')) presets.selected_location = $trigger.data('selected-location');
2972 if ($trigger.data('selected-agent')) presets.selected_agent = $trigger.data('selected-agent');
2973 if ($trigger.data('selected-service')) presets.selected_service = $trigger.data('selected-service');
2974 if ($trigger.data('selected-bundle')) presets.selected_bundle = $trigger.data('selected-bundle');
2975 if ($trigger.data('selected-duration')) presets.selected_duration = $trigger.data('selected-duration');
2976 if ($trigger.data('selected-total-attendees')) presets.selected_total_attendees = $trigger.data('selected-total-attendees');
2977 if ($trigger.data('selected-service-category')) presets.selected_service_category = $trigger.data('selected-service-category');
2978 if ($trigger.data('selected-start-date')) presets.selected_start_date = $trigger.data('selected-start-date');
2979 if ($trigger.data('selected-start-time')) presets.selected_start_time = $trigger.data('selected-start-time');
2980 if ($trigger.data('order-item-id')) presets.order_item_id = $trigger.data('order-item-id');
2981 if ($trigger.data('source-id')) presets.source_id = $trigger.data('source-id');
2982
2983 if ($trigger.data('hide-summary') == 'yes') booking_element_styles.hide_summary = true;
2984 if ($trigger.data('hide-side-panel') == 'yes') booking_element_styles.hide_side_panel = true;
2985
2986
2987 if (jQuery.isEmptyObject(restrictions) == false) params.restrictions = restrictions;
2988 if (jQuery.isEmptyObject(presets) == false) params.presets = presets;
2989 if (jQuery.isEmptyObject(booking_element_styles) == false) params.booking_element_styles = booking_element_styles;
2990
2991 let data = {
2992 action: latepoint_helper.route_action,
2993 route_name: route,
2994 params: params,
2995 layout: 'none',
2996 return_format: 'json'
2997 }
2998
2999 let is_inline_form = $trigger.hasClass('latepoint-book-form-wrapper');
3000 if (is_inline_form) {
3001 data.params.booking_element_type = 'inline_form';
3002 }
3003
3004 $trigger.addClass('os-loading');
3005 jQuery.ajax({
3006 type: "post",
3007 dataType: "json",
3008 url: latepoint_timestamped_ajaxurl(),
3009 data: data,
3010 success: (data) => {
3011 if (data.status === "success") {
3012 let $booking_form_element = false;
3013 if (is_inline_form) {
3014 $trigger.html(data.message);
3015 $booking_form_element = $trigger.find('.latepoint-booking-form-element');
3016 } else {
3017 let lightbox_class = 'booking-form-in-lightbox';
3018 latepoint_show_data_in_lightbox(data.message, lightbox_class, false);
3019 $booking_form_element = jQuery('.latepoint-lightbox-w .latepoint-booking-form-element');
3020 jQuery('body').addClass('latepoint-lightbox-active');
3021 }
3022 latepoint_init_booking_form($booking_form_element);
3023 latepoint_init_step(data.step, $booking_form_element);
3024 $trigger.removeClass('os-loading');
3025 } else {
3026 $trigger.removeClass('os-loading');
3027 // console.log(data.message);
3028 }
3029 }
3030 });
3031 }
3032
3033
3034 /*
3035 * Copyright (c) 2023 LatePoint LLC. All rights reserved.
3036 */
3037
3038 async function latepoint_init_transaction_payment_form() {
3039 let callbacks_list = [];
3040 let $transaction_payment_form = jQuery('.latepoint-transaction-payment-form');
3041 let current_step = $transaction_payment_form.find('input[name="current_step"]').val();
3042
3043 $transaction_payment_form.on('click keydown', '.lp-option', (e) => {
3044 let $option = jQuery(e.currentTarget);
3045 if(e.type === 'keydown' && e.key !== ' ' && e.key !== 'Enter') return;
3046 $option.closest('.lp-options').find('.lp-option.selected').removeClass('selected');
3047 $option.addClass('selected');
3048 $transaction_payment_form.find('input[name="' + $option.data('holder') + '"]').val($option.data('value'));
3049 $transaction_payment_form.trigger('submit');
3050 return false;
3051 });
3052
3053
3054 switch (current_step) {
3055 case 'methods':
3056 break;
3057 case 'processors':
3058 break;
3059 case 'pay':
3060 $transaction_payment_form.trigger('latepoint:initOrderPaymentMethod', [{
3061 callbacks_list: callbacks_list,
3062 payment_method: $transaction_payment_form.find('input[name="payment_method"]').val(),
3063 payment_processor: $transaction_payment_form.find('input[name="payment_processor"]').val(),
3064 }]);
3065 $transaction_payment_form.addClass('os-loading');
3066
3067 try {
3068 for (const callback of callbacks_list) {
3069 await callback.action();
3070 }
3071 $transaction_payment_form.removeClass('os-loading');
3072 } catch (error) {
3073 latepoint_show_error_and_stop_loading_booking_form(error, $transaction_payment_form);
3074 }
3075 break;
3076 case 'confirmation':
3077 break;
3078 }
3079
3080 $transaction_payment_form.on('submit', async function (e) {
3081 e.preventDefault();
3082 await latepoint_submit_transaction_payment_form(jQuery(e.target));
3083 });
3084 }
3085
3086 async function latepoint_submit_transaction_payment_form($transaction_payment_form) {
3087
3088 if($transaction_payment_form.hasClass('os-loading')) return false;
3089 let callbacks_list = [];
3090
3091 $transaction_payment_form.find('.latepoint-message').remove();
3092 $transaction_payment_form.addClass('os-loading');
3093 $transaction_payment_form.find('.latepoint-btn').addClass('os-loading');
3094
3095 $transaction_payment_form.trigger('latepoint:submitTransactionPaymentForm', [{
3096 callbacks_list: callbacks_list,
3097 payment_method: $transaction_payment_form.find('input[name="payment_method"]').val(),
3098 payment_processor: $transaction_payment_form.find('input[name="payment_processor"]').val(),
3099 current_step: $transaction_payment_form.find('input[name="current_step"]').val(),
3100 }]);
3101
3102 try {
3103 for (const callback of callbacks_list) {
3104 await callback.action();
3105 }
3106 } catch (error) {
3107 $transaction_payment_form.removeClass('os-loading').find('.os-loading').removeClass('os-loading');
3108 latepoint_show_message_inside_element(error.message, $transaction_payment_form.find('.lp-payment-method-content'), 'error');
3109 return false;
3110 }
3111
3112
3113 try {
3114 let response = await jQuery.ajax({
3115 type: "post",
3116 dataType: "json",
3117 processData: false,
3118 contentType: false,
3119 url: latepoint_timestamped_ajaxurl(),
3120 data: latepoint_create_form_data($transaction_payment_form, latepoint_helper.invoices_payment_form_route)
3121 });
3122
3123 $transaction_payment_form.removeClass('os-loading').find('.os-loading').removeClass('os-loading');
3124
3125 if (response.status === 'success') {
3126 $transaction_payment_form.html(response.message);
3127 return await latepoint_init_transaction_payment_form();
3128
3129 } else {
3130 latepoint_show_message_inside_element(response.message, $transaction_payment_form.find('.lp-payment-method-content'), 'error');
3131 return false;
3132 }
3133 } catch (e) {
3134
3135 $transaction_payment_form.removeClass('os-loading').find('.os-loading').removeClass('os-loading');
3136 console.log(e);
3137 alert('Error:' + e);
3138 }
3139
3140 }
3141
3142 function latepoint_hide_reschedule_button() {
3143 jQuery('.reschedule-confirmation-button-wrapper').hide();
3144 }
3145
3146 function latepoint_show_reschedule_button() {
3147 jQuery('.reschedule-confirmation-button-wrapper').show();
3148 }
3149
3150 function latepoint_customer_cabinet_reload_booking_tile($booking_tile) {
3151 $booking_tile.addClass('os-loading');
3152 let params = {
3153 booking_id: $booking_tile.data('id'),
3154 }
3155 let data = {
3156 action: latepoint_helper.route_action,
3157 route_name: $booking_tile.data('route-name'),
3158 params: params,
3159 layout: 'none',
3160 return_format: 'json'
3161 };
3162 jQuery.ajax({
3163 type: "post",
3164 dataType: "json",
3165 url: latepoint_timestamped_ajaxurl(),
3166 data: data,
3167 success: function (data) {
3168 $booking_tile.removeClass('os-loading')
3169 if (data.status === "success") {
3170 $booking_tile.replaceWith(data.message);
3171 } else {
3172 alert(data.message);
3173 }
3174 }
3175 });
3176 }
3177
3178 // show invoices -> summary_before_payment in modal
3179 function show_summary_before_payment($access_key) {
3180 let data = {
3181 action: latepoint_helper.route_action,
3182 route_name: latepoint_helper.invoices_summary_before_payment_route,
3183 params: {key: $access_key},
3184 layout: 'none',
3185 return_format: 'json'
3186 }
3187
3188 jQuery.ajax({
3189 type: "post",
3190 dataType: "json",
3191 url: latepoint_timestamped_ajaxurl(),
3192 data: data,
3193 success: function (data) {
3194 latepoint_show_data_in_lightbox(data.message, 'width-500');
3195 }
3196 });
3197 }
3198
3199 function latepoint_reload_reschedule_calendar($reschedule_calendar_wrapper) {
3200 $reschedule_calendar_wrapper.addClass('os-loading');
3201 let $calendar_element = $reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active').last();
3202 let calendar_year = $calendar_element.data('calendar-year');
3203 let calendar_month = $calendar_element.data('calendar-month');
3204
3205 let data = {
3206 action: latepoint_helper.route_action,
3207 route_name: $reschedule_calendar_wrapper.data('route'),
3208 params: {
3209 timezone_name: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_timezone_name').val(),
3210 key: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_manage_booking_key').val(),
3211 target_date_string: `${calendar_year}-${calendar_month}-1`,
3212 booking_id: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_booking_id').val()
3213 },
3214 layout: 'none',
3215 return_format: 'json'
3216 }
3217 jQuery.ajax({
3218 type: "post",
3219 dataType: "json",
3220 url: latepoint_timestamped_ajaxurl(),
3221 data: data,
3222 success: function (data) {
3223 $reschedule_calendar_wrapper.removeClass('os-loading');
3224 if (data.status === "success") {
3225 jQuery('.latepoint-lightbox-i').html(data.message);
3226 } else {
3227 // console.log(data.message);
3228 }
3229 }
3230 });
3231 latepoint_calendar_show_or_hide_prev_next_buttons($reschedule_calendar_wrapper);
3232 return false;
3233 }
3234
3235 function latepoint_init_reschedule() {
3236
3237 let $reschedule_wrapper = jQuery('.reschedule-calendar-wrapper');
3238
3239 latepoint_init_timezone_picker($reschedule_wrapper);
3240
3241 $reschedule_wrapper.on('click', '.latepoint-request-reschedule-trigger', function () {
3242 let $trigger = jQuery(this)
3243 let $wrapper = $trigger.closest('.reschedule-calendar-wrapper')
3244 let booking_id = $wrapper.find('input[type="hidden"].latepoint_booking_id').val()
3245
3246 $trigger.addClass('os-loading')
3247 let params = {
3248 booking_id: booking_id,
3249 key: $wrapper.find('input[type="hidden"].latepoint_manage_booking_key').val(),
3250 start_date: $wrapper.find('input[type="hidden"].latepoint_start_date').val(),
3251 start_time: $wrapper.find('input[type="hidden"].latepoint_start_time').val(),
3252 timezone_name: $wrapper.find('input[type="hidden"].latepoint_timezone_name').val(),
3253 }
3254 let data = {
3255 action: latepoint_helper.route_action,
3256 route_name: $trigger.data('route-name'),
3257 params: params,
3258 layout: 'none',
3259 return_format: 'json'
3260 };
3261 jQuery.ajax({
3262 type: "post",
3263 dataType: "json",
3264 url: latepoint_timestamped_ajaxurl(),
3265 data: data,
3266 success: function (data) {
3267 $trigger.removeClass('os-loading')
3268 if (data.status === "success") {
3269 jQuery('.latepoint-lightbox-content').html(data.message);
3270 jQuery('.latepoint-lightbox-footer, .latepoint-lightbox-heading').remove();
3271 if (jQuery('.customer-bookings-tiles').length) {
3272 // called from customer cabinet
3273 latepoint_customer_cabinet_reload_booking_tile(jQuery('.customer-bookings-tiles .customer-booking[data-id="' + booking_id + '"]'));
3274 } else {
3275 // called from manage by key
3276 latepoint_manage_by_key_reload_booking();
3277 }
3278 } else {
3279 latepoint_show_message_inside_element(data.message, jQuery('.latepoint-lightbox-content'), 'error');
3280 jQuery('.latepoint-lightbox-content').animate({scrollTop: 0}, 300);
3281 }
3282 }
3283 });
3284 return false;
3285 });
3286
3287 $reschedule_wrapper.on('click keydown', '.dp-timepicker-trigger', function (event) {
3288 if (event.type === 'keydown' && event.key !== ' ' && event.key !== 'Enter') return;
3289 var $reschedule_calendar_wrapper = jQuery(this).closest('.reschedule-calendar-wrapper');
3290 if (jQuery(this).hasClass('is-booked') || jQuery(this).hasClass('is-off')) {
3291 // Show error message that you cant select a booked period
3292 } else {
3293 if (jQuery(this).hasClass('selected')) {
3294 jQuery(this).removeClass('selected');
3295 jQuery(this).find('.dp-success-label').remove();
3296 $reschedule_calendar_wrapper.find('.latepoint_start_time').val('');
3297 latepoint_hide_reschedule_button();
3298 } else {
3299 $reschedule_calendar_wrapper.find('.dp-timepicker-trigger.selected').removeClass('selected').find('.dp-success-label').remove();
3300 var selected_timeslot_time = jQuery(this).find('.dp-label-time').html();
3301 jQuery(this).addClass('selected').find('.dp-label').prepend('<span class="dp-success-label">' + latepoint_helper.datepicker_timeslot_selected_label + '</span>');
3302
3303 var minutes = parseInt(jQuery(this).data('minutes'));
3304 var start_date = new Date($reschedule_calendar_wrapper.find('.os-day.selected').data('date'));
3305
3306 $reschedule_calendar_wrapper.find('.latepoint_start_date').val(start_date.toISOString().split('T')[0])
3307 $reschedule_calendar_wrapper.find('.latepoint_start_time').val(minutes);
3308 latepoint_show_reschedule_button();
3309 }
3310 }
3311 return false;
3312 });
3313
3314
3315 $reschedule_wrapper.on('click', '.os-month-next-btn', function () {
3316 var $reschedule_calendar_wrapper = jQuery(this).closest('.reschedule-calendar-wrapper');
3317 var next_month_route_name = jQuery(this).data('route');
3318 if ($reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active + .os-monthly-calendar-days-w').length) {
3319 $reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active').removeClass('active').next('.os-monthly-calendar-days-w').addClass('active');
3320 latepoint_calendar_set_month_label($reschedule_calendar_wrapper);
3321 } else {
3322 // TODO add condition to check maximum number months to call into the future
3323 if (true) {
3324 var $btn = jQuery(this);
3325 $btn.addClass('os-loading');
3326 var $calendar_element = $reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w').last();
3327 var calendar_year = $calendar_element.data('calendar-year');
3328 var calendar_month = $calendar_element.data('calendar-month');
3329 if (calendar_month == 12) {
3330 calendar_year = calendar_year + 1;
3331 calendar_month = 1;
3332 } else {
3333 calendar_month = calendar_month + 1;
3334 }
3335 var data = {
3336 action: latepoint_helper.route_action,
3337 route_name: next_month_route_name,
3338 params: {
3339 timezone_name: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_timezone_name').val(),
3340 key: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_manage_booking_key').val(),
3341 target_date_string: `${calendar_year}-${calendar_month}-1`,
3342 booking: {
3343 id: $reschedule_calendar_wrapper.find('input[type="hidden"].latepoint_booking_id').val()
3344 }
3345 },
3346 layout: 'none',
3347 return_format: 'json'
3348 }
3349 jQuery.ajax({
3350 type: "post",
3351 dataType: "json",
3352 url: latepoint_timestamped_ajaxurl(),
3353 data: data,
3354 success: function (data) {
3355 $btn.removeClass('os-loading');
3356 if (data.status === "success") {
3357 $reschedule_calendar_wrapper.find('.os-months').append(data.message);
3358 $reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active').removeClass('active').next('.os-monthly-calendar-days-w').addClass('active');
3359 latepoint_calendar_set_month_label($reschedule_calendar_wrapper);
3360 } else {
3361 // console.log(data.message);
3362 }
3363 }
3364 });
3365 }
3366 }
3367 latepoint_calendar_show_or_hide_prev_next_buttons($reschedule_calendar_wrapper);
3368 return false;
3369 });
3370
3371 $reschedule_wrapper.on('click', '.os-month-prev-btn', function () {
3372 var $reschedule_calendar_wrapper = jQuery(this).closest('.reschedule-calendar-wrapper');
3373 if ($reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active').prev('.os-monthly-calendar-days-w').length) {
3374 $reschedule_calendar_wrapper.find('.os-monthly-calendar-days-w.active').removeClass('active').prev('.os-monthly-calendar-days-w').addClass('active');
3375 latepoint_calendar_set_month_label($reschedule_calendar_wrapper);
3376 }
3377 latepoint_calendar_show_or_hide_prev_next_buttons($reschedule_calendar_wrapper);
3378 return false;
3379 });
3380
3381 $reschedule_wrapper.on('click', '.os-day', function () {
3382 if (jQuery(this).hasClass('os-day-passed')) return false;
3383 if (jQuery(this).hasClass('os-not-in-allowed-period')) return false;
3384 if(jQuery(this).closest('.os-dates-and-times-w').hasClass('calendar-style-modern')){
3385 if(jQuery(this).hasClass('os-month-prev')) return false;
3386 if(jQuery(this).hasClass('os-month-next')) return false;
3387 }
3388 var $reschedule_calendar_wrapper = jQuery(this).closest('.reschedule-calendar-wrapper');
3389 if (jQuery(this).closest('.os-monthly-calendar-days-w').hasClass('hide-if-single-slot')) {
3390
3391 // HIDE TIMESLOT IF ONLY ONE TIMEPOINT
3392 if (jQuery(this).hasClass('os-not-available')) {
3393 // clicked on a day that has no available timeslots
3394 // do nothing
3395 } else {
3396 $reschedule_calendar_wrapper.find('.os-day.selected').removeClass('selected');
3397 jQuery(this).addClass('selected');
3398 // set date
3399 $reschedule_calendar_wrapper.find('.latepoint_start_date').val(jQuery(this).data('date'));
3400 if (jQuery(this).hasClass('os-one-slot-only')) {
3401 // clicked on a day that has only one slot available
3402 var bookable_minutes = jQuery(this).data('bookable-minutes').toString().split(':')[0];
3403 var selected_timeslot_time = latepoint_format_minutes_to_time(Number(bookable_minutes), Number(jQuery(this).data('service-duration')));
3404 $reschedule_calendar_wrapper.find('.latepoint_start_time').val(jQuery(this).data('bookable-minutes'));
3405 $reschedule_calendar_wrapper.find('.time-selector-w').slideUp(200);
3406 latepoint_show_reschedule_button()
3407 } else {
3408 // regular day with more than 1 timeslots available
3409 // build timeslots
3410 latepoint_generate_day_timeslots(jQuery(this), $reschedule_calendar_wrapper, $reschedule_calendar_wrapper.find('.latepoint-lightbox-content'));
3411 // initialize timeslots events
3412 // clear time and hide next btn
3413 $reschedule_calendar_wrapper.find('.latepoint_start_time').val('');
3414 }
3415 }
3416 } else {
3417
3418 // SHOW TIMESLOTS EVEN IF ONLY ONE TIMEPOINT
3419 $reschedule_calendar_wrapper.find('.latepoint_start_date').val(jQuery(this).data('date'));
3420 $reschedule_calendar_wrapper.find('.os-day.selected').removeClass('selected');
3421 jQuery(this).addClass('selected');
3422
3423 // build timeslots
3424 latepoint_generate_day_timeslots(jQuery(this), $reschedule_calendar_wrapper, $reschedule_calendar_wrapper.find('.latepoint-lightbox-content'));
3425 // initialize timeslots events
3426 // clear time and hide next btn
3427 let $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
3428 if ($booking_form_element.length) latepoint_reload_summary($booking_form_element);
3429 $reschedule_calendar_wrapper.find('.latepoint_start_time').val('');
3430 latepoint_hide_next_btn($reschedule_calendar_wrapper);
3431 }
3432
3433 return false;
3434 });
3435 }
3436
3437
3438 /*
3439 * Copyright (c) 2024 LatePoint LLC. All rights reserved.
3440 */
3441
3442 function latepoint_reload_day_schedule($day_view){
3443 $day_view.addClass('os-loading');
3444 let data = {
3445 action: latepoint_helper.route_action,
3446 route_name: $day_view.data('route-name'),
3447 params: $day_view.find("select, textarea, input").serialize(),
3448 layout: 'none',
3449 return_format: 'json'
3450 }
3451
3452 jQuery.ajax({
3453 type: "post",
3454 dataType: "json",
3455 url: latepoint_timestamped_ajaxurl(),
3456 data: data,
3457 success: function (data) {
3458 if (data.status === "success") {
3459 $day_view.replaceWith(data.message);
3460 }
3461 }
3462 });
3463 }
3464
3465 function latepoint_reload_events_calendar($events_calendar){
3466 $events_calendar.addClass('os-loading');
3467 let data = {
3468 action: latepoint_helper.route_action,
3469 route_name: $events_calendar.data('route-name'),
3470 params: $events_calendar.find("select, textarea, input").serialize(),
3471 layout: 'none',
3472 return_format: 'json'
3473 }
3474
3475 jQuery.ajax({
3476 type: "post",
3477 dataType: "json",
3478 url: latepoint_timestamped_ajaxurl(),
3479 data: data,
3480 success: function (data) {
3481 if (data.status === "success") {
3482 $events_calendar.replaceWith(data.message);
3483 }
3484 }
3485 });
3486
3487 }
3488
3489 /*
3490 * Copyright (c) 2024 LatePoint LLC. All rights reserved.
3491 */
3492
3493 class LatepointStripeConnectFront {
3494
3495 // Init
3496 constructor(stripeKey) {
3497 this.stripeKey = stripeKey;
3498 this.stripeElements = null;
3499 this.stripeCore = null;
3500 this.stripePaymentMethod = null;
3501 this.stripeContinueOrderIntentURL = null;
3502 this.stripeContinueTransactionIntentURL = null;
3503 this.stripePaymentIntentSecret = null;
3504 this.stripePaymentElement = null;
3505 this.ready();
3506 }
3507
3508 ready() {
3509 jQuery(document).ready(() => {
3510 jQuery('body').on('latepoint:submitBookingForm', '.latepoint-booking-form-element', (e, data) => {
3511 if (!latepoint_helper.demo_mode && data.is_final_submit && data.direction == 'next') {
3512 let payment_method = jQuery(e.currentTarget).find('input[name="cart[payment_method]"]').val();
3513 switch (payment_method) {
3514 case 'payment_element':
3515 latepoint_add_action(data.callbacks_list, async () => {
3516 if (this.stripePaymentIntentSecret && this.stripeCore) {
3517 return await this.confirmPaymentElementPayment(jQuery(e.currentTarget));
3518 }
3519 });
3520 break;
3521 }
3522 }
3523 });
3524 jQuery('body').on('latepoint:submitTransactionPaymentForm', '.latepoint-transaction-payment-form', (e, data) => {
3525 if (data.current_step === 'pay' && data.payment_processor === 'stripe_connect' && data.payment_method === 'payment_element') {
3526 latepoint_add_action(data.callbacks_list, async () => {
3527 if (this.stripePaymentIntentSecret && this.stripeCore) {
3528 return await this.confirmPaymentElementPaymentForTransaction(jQuery(e.currentTarget));
3529 }
3530 });
3531 }
3532 });
3533
3534 // INITIALIZE PAYMENT METHOD
3535 jQuery('body').on('latepoint:initPaymentMethod', '.latepoint-booking-form-element', (e, data) => {
3536 if (!latepoint_helper.demo_mode) {
3537 switch (data.payment_method) {
3538 case 'payment_element':
3539 latepoint_add_action(data.callbacks_list, async () => {
3540 return await this.createPaymentIntent(jQuery(e.currentTarget), data.payment_method);
3541 });
3542 break;
3543 }
3544 } else {
3545 latepoint_show_next_btn(jQuery(e.currentTarget));
3546 }
3547 });
3548 // INITIALIZE PAYMENT METHOD on order payment form
3549 jQuery('body').on('latepoint:initOrderPaymentMethod', '.latepoint-transaction-payment-form', (e, data) => {
3550 if (data.payment_processor === 'stripe_connect') {
3551 switch (data.payment_method) {
3552 case 'payment_element':
3553 latepoint_add_action(data.callbacks_list, async () => {
3554 return await this.createPaymentIntentForTransaction(jQuery(e.currentTarget));
3555 });
3556 break;
3557 }
3558 }
3559 });
3560 });
3561 }
3562
3563
3564 async createPaymentIntentForTransaction($transaction_intent_form) {
3565
3566 try {
3567 this.stripeCore = Stripe(this.stripeKey, {stripeAccount: latepoint_helper.stripe_connected_account_id});
3568 this.stripeElements = this.stripeCore.elements();
3569 }catch(e){
3570 console.log(e);
3571 alert(e);
3572 }
3573
3574 let data = latepoint_create_form_data($transaction_intent_form, latepoint_helper.stripe_connect_route_create_payment_intent_for_transaction_intent);
3575
3576 let response = await jQuery.ajax({
3577 type: "post",
3578 dataType: "json",
3579 processData: false,
3580 contentType: false,
3581 url: latepoint_timestamped_ajaxurl(),
3582 data: data
3583 });
3584
3585 if (response.status === "success") {
3586 $transaction_intent_form.find('input[name="payment_token"]').val(response.payment_intent_id);
3587 this.stripePaymentIntentSecret = response.payment_intent_secret;
3588 this.stripeContinueTransactionIntentURL = response.continue_transaction_intent_url;
3589 latepoint_show_next_btn($transaction_intent_form);
3590
3591 if ($transaction_intent_form.find('.stripe-payment-element').length) {
3592 return this.initPaymentElement($transaction_intent_form);
3593 }
3594 } else {
3595 alert(response.message);
3596 throw new Error(response.message);
3597 }
3598 }
3599
3600 async createPaymentIntent($booking_form_element, payment_method) {
3601 this.stripeCore = Stripe(this.stripeKey, {stripeAccount: latepoint_helper.stripe_connected_account_id});
3602 this.stripeElements = this.stripeCore.elements();
3603
3604 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});
3605
3606 let response = await jQuery.ajax({
3607 type: "post",
3608 dataType: "json",
3609 processData: false,
3610 contentType: false,
3611 url: latepoint_timestamped_ajaxurl(),
3612 data: data
3613 });
3614
3615 if (response.status === "success") {
3616 $booking_form_element.find('input[name="cart[payment_token]"]').val(response.payment_intent_id);
3617 this.stripePaymentIntentSecret = response.payment_intent_secret;
3618 this.stripeContinueOrderIntentURL = response.continue_order_intent_url;
3619 latepoint_show_next_btn($booking_form_element);
3620
3621 if ($booking_form_element.find('.stripe-payment-element').length) {
3622 return this.initPaymentElement($booking_form_element);
3623 }
3624 } else {
3625 alert(response.message);
3626 throw new Error(response.message);
3627 }
3628 }
3629
3630 async confirmPaymentElementPaymentForTransaction($transaction_intent_form) {
3631 let elements = this.stripeElements;
3632 let continue_transaction_intent_url = this.stripeContinueTransactionIntentURL;
3633 let result = await this.stripeCore.confirmPayment({
3634 elements,
3635 confirmParams: {
3636 // Return URL where the customer should be redirected after the PaymentIntent is confirmed.
3637 return_url: continue_transaction_intent_url,
3638 },
3639 redirect: "if_required",
3640 });
3641 if (result.error) {
3642 throw new Error(result.error.message);
3643 } else {
3644 $transaction_intent_form.find('input[name="payment_token"]').val(result.paymentIntent.id);
3645 return result.paymentIntent.id;
3646 }
3647 }
3648
3649 async confirmPaymentElementPayment($booking_form_element) {
3650 let elements = this.stripeElements;
3651 let continue_order_intent_url = this.stripeContinueOrderIntentURL;
3652 let result = await this.stripeCore.confirmPayment({
3653 elements,
3654 confirmParams: {
3655 // Return URL where the customer should be redirected after the PaymentIntent is confirmed.
3656 return_url: continue_order_intent_url,
3657 },
3658 redirect: "if_required",
3659 });
3660 if (result.error) {
3661 throw new Error(result.error.message);
3662 } else {
3663 $booking_form_element.find('input[name="cart[payment_token]"]').val(result.paymentIntent.id);
3664 return result.paymentIntent.id;
3665 }
3666 }
3667
3668 initPaymentElement($booking_form_element) {
3669 let appearance = {
3670 theme: 'stripe',
3671 variables: {
3672 fontFamily: 'Overpass',
3673 colorPrimary: '#1d7bff'
3674 },
3675 rules: {
3676 '.Tab': {
3677 border: '1px solid #E0E6EB',
3678 boxShadow: 'none',
3679 borderRadius: '0',
3680 marginBottom: '10px'
3681 },
3682 '.Input': {
3683 boxShadow: 'none',
3684 borderRadius: '0'
3685 },
3686
3687 '.Tab:hover': {
3688 color: 'var(--colorText)',
3689 },
3690
3691 '.Tab--selected': {
3692 borderColor: 'var(--colorPrimary)',
3693 boxShadow: `0 0 0 1px var(--colorPrimary)`,
3694 },
3695
3696 '.Input--invalid': {
3697 boxShadow: '0 1px 1px 0 rgba(0, 0, 0, 0.07), 0 0 0 2px var(--colorPrimary)',
3698 },
3699
3700 // See all supported class names and selector syntax below
3701 }
3702 };
3703
3704
3705 // Create an instance of the Payment Element
3706 this.stripeElements = this.stripeCore.elements({
3707 clientSecret: this.stripePaymentIntentSecret,
3708 appearance,
3709 fonts: [{cssSrc: 'https://fonts.googleapis.com/css2?family=Overpass&display=swap'}],
3710 });
3711
3712 let options = {
3713 layout: {
3714 type: 'tabs',
3715 defaultCollapsed: false,
3716 },
3717 };
3718 this.stripePaymentElement = this.stripeElements.create('payment', options);
3719
3720 return this.stripePaymentElement.mount($booking_form_element.find('.stripe-payment-element')[0]);
3721 }
3722
3723
3724 }
3725
3726
3727 if (latepoint_helper.is_stripe_connect_enabled) window.latepointStripeConnectFront = new LatepointStripeConnectFront(latepoint_helper.stripe_connect_key);
3728
3729 /*
3730 * Copyright (c) 2022 LatePoint LLC. All rights reserved.
3731 */
3732 // @codekit-prepend "bin/time.js"
3733 // @codekit-prepend "bin/shared.js"
3734 // @codekit-prepend "bin/notifications.js";
3735 // @codekit-prepend "bin/actions.js"
3736 // @codekit-prepend "bin/front/main.js"
3737 // @codekit-prepend "bin/front/_customer.js"
3738 // @codekit-prepend "bin/front/_events.js"
3739 // @codekit-prepend "bin/front/_stripe_connect.js"
3740
3741
3742 // DOCUMENT READY
3743 jQuery(document).ready(function ($) {
3744
3745 latepoint_init_customer_login_form();
3746 latepoint_init_customer_dashboard();
3747 latepoint_init_manage_booking_by_key();
3748
3749 jQuery('body').on('click', '.le-filter-trigger', function () {
3750 let $events_calendar = jQuery(this).closest('.latepoint-calendar-wrapper');
3751 if ($events_calendar.hasClass('show-filters')) {
3752 $events_calendar.removeClass('show-filters');
3753 $events_calendar.find('.latepoint-calendar-filters select').val('');
3754 latepoint_reload_events_calendar($events_calendar);
3755 } else {
3756 $events_calendar.addClass('show-filters');
3757 }
3758 return false;
3759 });
3760
3761 jQuery('body').on('click', '.le-navigation-trigger', function () {
3762 let $trigger = jQuery(this);
3763 let $events_calendar = $trigger.closest('.latepoint-calendar-wrapper');
3764 $events_calendar.find('input[name="target_date_string"]').val($trigger.data('target-date'));
3765 $trigger.addClass('os-loading');
3766 latepoint_reload_events_calendar($events_calendar);
3767 return false;
3768 });
3769
3770 jQuery('body').on('change', '.le-day-filters select', function () {
3771 let $trigger = jQuery(this);
3772 let $day_view = $trigger.closest('.le-day-view-wrapper');
3773 latepoint_reload_day_schedule($day_view);
3774 return false;
3775 });
3776
3777 jQuery('body').on('change', '.latepoint-calendar-filters select, .le-range-selector select', function () {
3778 let $trigger = jQuery(this);
3779 let $events_calendar = $trigger.closest('.latepoint-calendar-wrapper');
3780 $events_calendar.find('.le-filter').addClass('os-loading');
3781 latepoint_reload_events_calendar($events_calendar);
3782 return false;
3783 });
3784
3785 jQuery('body').on('click', '.close-calendar-types', function () {
3786 jQuery(this).closest('.add-to-calendar-wrapper').removeClass('show-types');
3787 return false;
3788 });
3789 jQuery('body').on('click', '.open-calendar-types', function () {
3790 jQuery(this).closest('.add-to-calendar-wrapper').addClass('show-types');
3791 return false;
3792 });
3793
3794 jQuery('body').on('latepoint:nextStepClicked', '.latepoint-booking-form-element', (e, data) => {
3795
3796 latepoint_add_action(data.callbacks_list, async () => {
3797 let $booking_form = jQuery(e.currentTarget).find('.latepoint-form');
3798 let errors = latepoint_validate_form($booking_form);
3799 if (errors.length) {
3800 let error_messages = errors.map(error => error.message).join(', ');
3801 throw new Error(error_messages);
3802 } else {
3803 return true;
3804 }
3805 }, 1);
3806
3807 });
3808
3809 if (latepoint_helper.start_from_order_intent_key) {
3810 $('body').append('<div class="latepoint-continue-intent-loading"></div>');
3811
3812 var data = {
3813 action: latepoint_helper.route_action,
3814 route_name: latepoint_helper.start_from_order_intent_route,
3815 params: {order_intent_key: latepoint_helper.start_from_order_intent_key},
3816 layout: 'none',
3817 return_format: 'json'
3818 }
3819
3820 $.ajax({
3821 type: "post",
3822 dataType: "json",
3823 url: latepoint_timestamped_ajaxurl(),
3824 data: data,
3825 success: function (data) {
3826 $('.latepoint-continue-intent-loading').remove();
3827 if (data.status === "success") {
3828 var lightbox_class = '';
3829 latepoint_show_data_in_lightbox(data.message, data.lightbox_class);
3830 var $booking_form_element = jQuery('.latepoint-lightbox-w .latepoint-booking-form-element');
3831 latepoint_init_booking_form($booking_form_element);
3832 $booking_form_element.find('.latepoint-step-content').addClass('is-hidden').last().removeClass('is-hidden');
3833 if ($booking_form_element.find('.latepoint-step-content').length > 1) latepoint_show_prev_btn($booking_form_element);
3834 var $booking_form_element = jQuery('.latepoint-lightbox-w .latepoint-booking-form-element');
3835 $booking_form_element.find('.latepoint-step-content').each(function () {
3836 latepoint_init_step($(this).data('step-code'), $booking_form_element);
3837 });
3838 $('body').addClass('latepoint-lightbox-active');
3839 } else {
3840 // console.log(data.message);
3841 }
3842 }
3843 });
3844 }
3845
3846 if (latepoint_helper.start_from_transaction_access_key) {
3847 const invoice_access_key = latepoint_helper.start_from_transaction_access_key;
3848 show_summary_before_payment(invoice_access_key);
3849 }
3850
3851
3852 jQuery('body').on('click', '.latepoint-lightbox-close', function () {
3853 latepoint_lightbox_close();
3854 return false;
3855 });
3856
3857
3858 jQuery('body').on('click', '.os-step-tabs .os-step-tab', function () {
3859 let $booking_form_element = jQuery(this).closest('.latepoint-booking-form-element');
3860 jQuery(this).closest('.os-step-tabs').find('.os-step-tab').removeClass('active');
3861 jQuery(this).addClass('active');
3862 var target = jQuery(this).data('target');
3863 jQuery(this).closest('.os-step-tabs-w').find('.os-step-tab-content').hide();
3864 if(jQuery(this).data('next-btn') === 'show'){
3865 latepoint_show_next_btn($booking_form_element);
3866 }else{
3867 latepoint_hide_next_btn($booking_form_element);
3868 }
3869
3870 if(jQuery(this).data('auth-action')){
3871 $booking_form_element.find('input[name="auth[action]"]').val(jQuery(this).data('auth-action'));
3872 }
3873 jQuery(target).show();
3874 });
3875
3876 jQuery('body').on('keyup', '.os-form-group .os-form-control', function () {
3877 if (jQuery(this).val()) {
3878 jQuery(this).closest('.os-form-group').addClass('has-value');
3879 } else {
3880 jQuery(this).closest('.os-form-group').removeClass('has-value');
3881 }
3882 });
3883
3884 jQuery('.latepoint-tab-triggers').on('click', '.latepoint-tab-trigger', function () {
3885 var $tabs_wrapper = jQuery(this).closest('.latepoint-tabs-w')
3886 $tabs_wrapper.find('.latepoint-tab-trigger.active').removeClass('active');
3887 $tabs_wrapper.find('.latepoint-tab-content').removeClass('active');
3888 jQuery(this).addClass('active');
3889 $tabs_wrapper.find('.latepoint-tab-content' + jQuery(this).data('tab-target')).addClass('active');
3890 return false;
3891 });
3892
3893
3894 // Main Button to trigger lightbox opening
3895 if(jQuery('.latepoint-book-form-wrapper').length){
3896 jQuery('.latepoint-book-form-wrapper').each(function(){
3897 latepoint_init_booking_form_by_trigger(jQuery(this));
3898 });
3899 }
3900
3901 jQuery('body').on('click', '.latepoint-book-button, .os_trigger_booking', function () {
3902 latepoint_init_booking_form_by_trigger(jQuery(this));
3903 return false;
3904 });
3905
3906 });
3907