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