PageSourceSearch

https://dmker.hu/components/com_baforms/assets/js/ba-form.js?2.3.0.2

js dmker.hu collected 2026-10-04 05:13:00 UTC 136,810 bytes, 3,248 lines download raw bytes

1/**
2* @package   BaForms
3* @author    Balbooa http://www.balbooa.com/
4* @copyright Copyright @ Balbooa
5* @license   http://www.gnu.org/licenses/gpl.html GNU/GPL
6*/
7
8var $f = jQuery,
9    formsApp = {
10        loaded: {}
11    };
12
13!function($){
14    class formsModal {
15        constructor(element) {
16            this.element = $(element).on('mousedown.formsModal', $.proxy(this.hide, this));
17            this.position = element.dataset.position ? element.dataset.position : '';
18            if (element.parentNode != document.body) {
19                $('body').append(this.element);
20            }
21        }
22        show() {
23            this.element.css({
24                'opacity': '',
25                'pointer-events': ''
26            });
27            this.setScroll();
28            this.element.addClass('visible-forms-modal');
29            let scroll = this.element[0].offsetWidth - this.element[0].clientWidth;
30            this.element[0].style.setProperty('--forms-modal-scroll-width', scroll + 'px');
31        }
32        setScroll() {
33            document.body.classList.add('forms-modal-position-' + this.position);
34            if (!document.body.classList.contains('forms-modal-opened')) {
35                let scroll = window.innerWidth - document.documentElement.clientWidth;
36                document.body.classList.add('forms-modal-opened');
37                document.body.style.setProperty('--forms-body-scroll-width', scroll + 'px');
38            }
39        }
40        hide(event) {
41            if (!event || event.target.dataset.dismiss == 'formsModal' && event.button == 0) {
42                let $this = this;
43                this.element.removeClass('visible-forms-modal');
44                setTimeout(function () {
45                    $this.hideScroll();
46                }, 300);
47            }
48        }
49        hideScroll() {
50            document.body.classList.remove('forms-modal-position-' + this.position);
51            if (!document.querySelector('.visible-forms-modal')) {
52                document.body.classList.remove('forms-modal-opened');
53                document.body.style.removeProperty('--forms-body-scroll-width');
54            }
55        }
56    }
57    $.fn.formsModal = function(option){
58        return this.each(function() {
59            var data = $(this).data('formsModal');
60            if (!data) {
61                $(this).data('formsModal', (data = new formsModal(this)));
62            }
63            if (typeof option == 'string') {
64                data[option]();
65            } else {
66                data.show();
67            }
68        });
69    }
70    $.fn.formsModal.Constructor = formsModal;
71}(window.$f);
72
73formsApp.renderFormsCalendar = function(input){
74    if (input) {
75        formsApp.calendar.formInput = input;
76    }
77    formsApp.fetch.request(JUri+'index.php?option=com_baforms&task=form.renderFormsCalendar', {
78        start: formsApp.calendar.start,
79        year: formsApp.calendar.year,
80        month: formsApp.calendar.month * 1 + 1
81    }).then(function(text){
82        let data = JSON.parse(text),
83            disable = formsApp.calendar.disable,
84            input = formsApp.calendar.formInput,
85            div = document.createElement('div');
86        div.innerHTML = data.body;
87        for (let i = 0; i < disable.days.length; i++) {
88            div.querySelectorAll('.ba-date-cell[data-day-number="'+disable.days[i]+'"]').forEach((cell) => {
89                cell.classList.add('disabled-date');
90            });
91        }
92        for (let i = 0; i < disable.dates.length; i++) {
93            div.querySelectorAll('.ba-date-cell[data-day-date="'+disable.dates[i]+'"]').forEach((cell) => {
94                cell.classList.add('disabled-date');
95            })
96        }
97        for (let i = 0; i < disable.range.length; i++) {
98            let minMax = disable.range[i].split(' - ');
99            div.querySelectorAll('.ba-date-cell:not(.disabled-date)').forEach((cell) => {
100                if (cell.dataset.dayDate >= minMax[0] && cell.dataset.dayDate <= minMax[1]) {
101                    cell.classList.add('disabled-date');
102                }
103            });
104        }
105        div.querySelectorAll('.ba-curent-date').forEach(($this) => {
106            let today = formsApp.calendar.querySelector('.ba-forms-today-btn'),
107                action = $this.classList.contains('disabled-date') ? 'add' : 'remove';
108            today.dataset.date = $this.dataset.date;
109            today.dataset.dayDate = $this.dataset.dayDate;
110            today.classList[action]('disabled-date');
111        });
112        if (input.rangeBtn && input.dataset.index == 1) {
113            let date = input.rangeBtn.dataset.value;
114            div.querySelectorAll('.ba-date-cell:not(.disabled-date)').forEach((cell) => {
115                if (cell.dataset.dayDate <= date) {
116                    cell.classList.add('disabled-date');
117                }
118            });
119        }
120        formsApp.calendar.querySelector('.ba-forms-calendar-title').textContent = data.title;
121        formsApp.calendar.querySelector('.ba-forms-calendar-header').innerHTML = data.header;
122        formsApp.calendar.querySelector('.ba-forms-calendar-body').innerHTML = div.innerHTML;
123        if (!formsApp.calendar.classList.contains('visible-forms-calendar')) {
124            formsApp.calendar.classList.add('visible-forms-calendar');
125        }
126    });
127}
128
129formsApp.setFormsCalendarEvents = function(){
130    let $calendar = $f(formsApp.calendar);
131    $calendar.find('.ba-forms-today-btn').on('click', function(){
132        if (!this.classList.contains('disabled-date')) {
133            formsApp.calendar.formInput.value = this.dataset.date;
134            formsApp.calendar.formInput.dataset.value = this.dataset.dayDate;
135            $f(formsApp.calendar.formInput).trigger('input');
136            formsApp.hideCalendar();
137        }
138    });
139    $calendar.find('i[data-action]').on('click', function(){
140        let year = formsApp.calendar.year * 1,
141            month = formsApp.calendar.month * 1;
142        if (this.dataset.action == 'next') {
143            year = (month === 11) ? year + 1 : year;
144            month = (month + 1) % 12;
145        } else if (this.dataset.action == 'prev') {
146            year = (month === 0) ? year - 1 : year;
147            month = (month === 0) ? 11 : month - 1;
148        } else if (this.dataset.action == 'next-year') {
149            year = year + 1;
150        } else if (this.dataset.action == 'prev-year') {
151            year = year - 1;
152        }
153        formsApp.calendar.year = year;
154        formsApp.calendar.month = month;
155        formsApp.renderFormsCalendar();
156    });
157    $calendar.on('click', '.ba-date-cell', function(){
158        if (!(formsApp.calendar.classList.contains('disable-previous-date') && this.classList.contains('ba-previous-date'))
159            && !this.classList.contains('disabled-date')) {
160            formsApp.calendar.formInput.value = this.dataset.date;
161            formsApp.calendar.formInput.dataset.value = this.dataset.dayDate;
162            $f(formsApp.calendar.formInput).trigger('input');
163            formsApp.hideCalendar();
164        }
165    });
166    $calendar.find('.ba-close-calendar').on('click', function(){
167        formsApp.hideCalendar();
168    });
169}
170
171formsApp.hideCalendar = function(){
172    formsApp.calendar.classList.add('forms-calendar-out');
173    setTimeout(function(){
174        formsApp.calendar.classList.remove('visible-forms-calendar');
175        formsApp.calendar.classList.remove('forms-calendar-out');
176    }, 600);
177}
178
179formsApp.createCalendar = function($this){
180    formsApp.calendar = document.createElement('div');
181    formsApp.calendar.className = 'forms-calendar-wrapper';
182    formsApp.calendar.innerHTML = '<div class="ba-forms-calendar"><div class="ba-forms-calendar-title-wrapper">'+
183        '<span class="forms-calendar-btn"><i class="ba-form-icons ba-icon-chevron-left" data-action="prev-year"></i>'+
184        '<span class="ba-tooltip ba-bottom ba-hide-element">'+formsApp._('YEAR')+'</span></span>'+
185        '<span class="forms-calendar-btn"><i class="ba-form-icons ba-icon-chevron-left" data-action="prev"></i>'+
186        '<span class="ba-tooltip ba-bottom ba-hide-element">'+formsApp._('MONTH')+'</span></span>'+
187        '<span class="ba-forms-calendar-title"></span>'+
188        '<span class="forms-calendar-btn"><i class="ba-form-icons ba-icon-chevron-right" data-action="next"></i>'+
189        '<span class="ba-tooltip ba-bottom ba-hide-element">'+formsApp._('MONTH')+'</span></span>'+
190        '<span class="forms-calendar-btn"><i class="ba-form-icons ba-icon-chevron-right" data-action="next-year"></i>'+
191        '<span class="ba-tooltip ba-bottom ba-hide-element">'+formsApp._('YEAR')+'</span></span>'+
192        '</div><div class="ba-forms-calendar-header"></div>'+
193        '<div class="ba-forms-calendar-body"></div>'+
194        '<div class="ba-forms-calendar-footer"><span class="ba-forms-today-btn">'+formsApp._('TODAY')+'</span></div>'+
195        '</div><div class="ba-close-calendar"></div>';
196    document.body.appendChild(formsApp.calendar);
197    formsApp.calendar.year = $this.dataset.year;
198    formsApp.calendar.month = $this.dataset.month;
199    formsApp.setFormsCalendarEvents();
200}
201
202formsApp.hideNotice = function(){
203    this.notification.classList.remove('notification-in');
204    this.notification.classList.add('animation-out');
205}
206
207formsApp.showNotice = function(message, className = ''){
208    if (!this.notification) {
209        this.notification = document.createElement('div');
210        this.notification.id = 'ba-forms-notification';
211        this.notification.innerHTML = '<i class="ba-form-icons ba-icon-close"></i><h4>'+this._('ERROR')+'</h4><p></p>';
212        this.notification.querySelector('.ba-icon-close').addEventListener('click', function(){
213            formsApp.hideNotice();
214        });
215        document.body.append(this.notification);
216    }
217    this.notification.showCallback = function(){};
218    if (this.notification.classList.contains('notification-in')) {
219        this.notification.showCallback = function(){
220            formsApp.notification.showCallback = function(){};
221            formsApp.addNoticeText(message, className);
222        };
223    } else {
224        this.addNoticeText(message, className);
225    }
226}
227
228formsApp.addNoticeText = function(message, className){
229    $f(this.notification).find(' > p').html(message);
230    if (className) {
231        this.notification.classList.add(className);
232    } else {
233        this.notification.classList.remove('ba-alert');
234    }
235    this.notification.classList.remove('animation-out')
236    this.notification.classList.add('notification-in');
237    clearTimeout(this.notification.hideDelay);
238    this.notification.hideDelay = setTimeout(function(){
239        formsApp.hideNotice();
240        setTimeout(function(){
241            if (className) {
242                formsApp.notification.classList.remove(className);
243            }
244            formsApp.notification.showCallback();
245        }, 400);
246    }, 6000);
247}
248
249formsApp.getLocalStorage = function(){
250    let storage = localStorage.getItem('forms-progress'),
251        date = +new Date(),
252        expire = false;
253    if (!storage) {
254        storage = '{}';
255    }
256    formsApp.storage = JSON.parse(storage);
257    for (let ind in formsApp.storage) {
258        let page = formsApp.storage[ind];
259        for (let key in page) {
260            let data = page[key];
261            if (Math.floor((date - data.date) / (1000 * 60 * 60 * 24)) > 30) {
262                expire = true;
263                delete formsApp.storage[ind][key];
264            }
265        }
266    }
267    if (expire) {
268        localStorage.setItem('forms-progress', JSON.stringify(formsApp.storage));
269    }
270}
271
272formsApp.fetch = {
273    getErrorText: function(text){
274        let div = document.createElement('div');
275        div.innerHTML = text;
276        if (div.querySelector('title')) {
277            text = div.querySelector('title').textContent;
278        }
279
280        return text;
281    },
282    request: async function(url, data){
283        let request = await fetch(url, {
284                method: 'POST',
285                cache: 'no-cache',
286                body: this.getFormData(data)
287            }),
288            response = null;
289        if (request.ok) {
290            response = await request.text();
291        } else {
292            let utf8Decoder = new TextDecoder("utf-8"),
293                reader = request.body.getReader(),
294                textData = await reader.read(),
295                text = utf8Decoder.decode(textData.value);
296            console.info(this.getErrorText(text));
297        }
298
299        return response;
300    },
301    getFormData: function(data){
302        let formData = new FormData();
303        if (data) {
304            for (let ind in data) {
305                formData.append(ind, data[ind]);
306            }
307        }
308
309        return formData;
310    },
311
312}
313
314formsApp.countries = {
315    append: function(){
316        document.querySelectorAll('.ba-form-phone-field .ba-phone-countries-list').forEach(function(ul){
317            if (!ul.querySelector('li')) {
318                ul.innerHTML = formsApp.countries.html;
319            }
320        })
321    },
322    check: function(){
323        if (this.data) {
324            this.append()
325        } else if (document.querySelector('.ba-form-phone-field') && !this.loading) {
326            this.load();
327        }
328    },
329    readCountries: function(){
330        return new Promise(function(resolve, reject) {
331            fetch(JUri+'index.php?option=com_baforms&task=form.readCountries').then(function(response){
332                if (response.ok) {
333                    response.json().then(function(json){
334                        resolve(json);
335                    });
336                } else {
337                    console.info(response)
338                }
339            });
340        });
341    },
342    load: function(){
343        this.loading = true;
344        fetch(JUri+'components/com_baforms/libraries/countries/countries.json').then(function(response){
345            if (!response.ok) {
346                return formsApp.countries.readCountries()
347            } else {
348                return response.json();
349            }
350        }).then(function(json){
351            formsApp.countries.set(json);
352        });
353    },
354    set: function(data){
355        let html = '';
356        for (let ind in data) {
357            html += '<li class="ba-phone-country-item" data-prefix="+'+data[ind].prefix+'" data-flag="'+ind+
358                '" data-title="'+data[ind].title+'" data-placeholder="'+data[ind].placeholder.replace(/X/g, '_')+
359                '"><span class="ba-phone-flag ba-phone-flag-'+ind+'"></span><span class="ba-phone-country-title">'+
360                data[ind].title+'</span><span class="ba-phone-country-prefix">+'+data[ind].prefix+'</span></li>';
361        }
362        this.html = html;
363        this.data = data;
364        this.append();
365    }
366}
367
368formsApp.createForms = function(){
369    formsApp.signature.check();
370    formsApp.checkGoogleMaps();
371    formsApp.checkLanguage();
372    formsApp.countries.check();
373    if (!formsApp.recaptcha && document.querySelector('.ba-form-submit-recaptcha-wrapper')) {
374        formsApp.getRecaptchaData();
375    }
376    formsApp.getLocalStorage();
377    $f('.com-baforms-wrapper').each(formsApp.createForm).closest('.ba-form-lightbox-layout').each(formsApp.renderLightbox);
378}
379
380formsApp.renderLightbox = function(){
381    if (!this.trigger) {
382        this.trigger = JSON.parse(this.dataset.trigger);
383        this.session = JSON.parse(this.dataset.session);
384        $f(this).removeAttr('data-trigger').removeAttr('data-session')
385    }
386    if (!this.session.enable) {
387        formsApp.initLightbox(this);
388    } else if (this.session.enable && this.trigger.type != '') {
389        var flag = true;
390        if (localStorage['formsModal-'+this.dataset.id]) {
391            var date =  new Date().getTime(),
392                expires = new Date(localStorage[this.dataset.id]);
393            expires.getTime();
394            if (date >= expires) {
395                flag = true;
396                localStorage.removeItem(this.dataset.id);
397            } else {
398                flag = false;
399            }
400        }
401        if (flag) {
402            formsApp.initLightbox(this);
403        }
404    }
405}
406
407formsApp.showLightbox = function($this){
408    if ($this.session.enable) {
409        var expiration = new Date();
410        expiration.setDate(expiration.getDate()+$this.session.duration);
411        localStorage.setItem('formsModal-'+$this.dataset.id, expiration);
412    }
413    $f($this).formsModal().find('.ba-form-submit-field').each(function(){
414        formsApp.initFormsRecaptcha(this);
415    });
416}
417
418formsApp.initLightbox = function($this){
419    if ($this.trigger.type == 'time-delay') {
420        setTimeout(function(){
421            formsApp.showLightbox($this);
422        }, $this.trigger.time);
423    } else if ($this.trigger.type == 'scrolling') {
424        formsApp.lightboxScroll($this, $this.trigger.scroll * 1);
425    } else if ($this.trigger.type == 'exit-intent') {
426        $f(document).one('mouseleave', function(){
427            formsApp.showLightbox($this);
428        });
429    } else if ($this.trigger.type == 'bottom-of-page') {
430        formsApp.lightboxScroll($this, 100);
431    }
432}
433
434formsApp.lightboxScroll = function($this, scroll){
435    var top,
436        docHeight,
437        htmlHeight;
438    $f(window).on('scroll.ba-lightbox'+$this.dataset.id+' load.ba-lightbox'+$this.dataset.id, function(){
439        top = $f(window).scrollTop();
440        docHeight = document.documentElement.clientHeight
441        htmlHeight = Math.max(
442            document.body.scrollHeight, document.documentElement.scrollHeight,
443            document.body.offsetHeight, document.documentElement.offsetHeight,
444            document.body.clientHeight, document.documentElement.clientHeight
445        );
446        var x = (docHeight + top) * 100 / htmlHeight;
447        if (x >= scroll || (scroll > 97 && x >= 97)) {
448            $f(window).off('scroll.ba-lightbox'+$this.dataset.id+' load.ba-lightbox'+$this.dataset.id);
449            formsApp.showLightbox($this)
450        }
451    });
452}
453
454formsApp.executeRecaptcha = (btn) => {
455    let visible = true;
456    $f(btn).closest('.ba-form-submit-wrapper').find('.forms-recaptcha').each(function(){
457        let widgetID = formsApp.recaptcha.data[this.id],
458            response = formsApp.getRecaptchaResponse(this);
459        if (this.dataset.captcha == 'recaptcha_invisible' && !response) {
460            grecaptcha.execute(widgetID);
461            visible = false;
462        } else if (this.dataset.captcha == 'hcaptcha' && formsApp.recaptcha.hcaptcha.invisible && !response) {
463            hcaptcha.execute(widgetID)
464            visible = false;
465        }
466    });
467
468    return visible;
469}
470
471formsApp.getRecaptchaResponse = function(elem){
472    let response = '',
473        widgetId = formsApp.recaptcha.data[elem.id];
474    try {
475        if (elem.dataset.captcha == 'recaptcha_invisible') {
476            response = grecaptcha.getResponse();
477        } else if (elem.dataset.captcha == 'hcaptcha') {
478            response = hcaptcha.getResponse(widgetId);
479        } else if (elem.dataset.captcha == 'turnstile') {
480            response = !turnstile.isExpired(widgetId) ? turnstile.getResponse(widgetId) : '';
481        } else {
482            response = grecaptcha.getResponse(widgetId);
483        }
484    } catch (err) {
485        console.info(err)
486    }
487
488    return response != '';
489}
490
491formsApp.checkAlert = function(form){
492    form.find('.confirm-email-wrapper').find('input').each(function(){
493        let alert = !this.value.trim(),
494            key = 'THIS_FIELD_REQUIRED';
495        if (this.value) {
496            alert = !(/@/g.test(this.value) && this.value.match(/@/g).length == 1);
497            key = 'ENTER_VALID_VALUE';
498            if (!alert) {
499                let email = this.closest('.ba-form-field-item').querySelector('.ba-input-wrapper input').value.trim();
500                alert = !(email == this.value.trim());
501                key = 'EMAIL_ADDRESSES_NOT_MATCH';
502            }
503        }
504        formsApp.toggleAlertTooltip(alert, this, this.closest('.ba-field-container'), key);
505    });
506    form.find('.ba-form-signature-field').each(function(){
507        if (this.dataset.required) {
508            let alert = this.signature.isEmpty(),
509                container = this.querySelector('.ba-field-container');
510            formsApp.toggleAlertTooltip(alert, container, container, 'THIS_FIELD_REQUIRED');
511        }
512    })
513    form.find('.confirm-password-wrapper').find('input').each(function(){
514        let password = this.closest('.ba-form-field-item').querySelector('.ba-input-wrapper input').value.trim(),
515            alert = this.required && !this.value.trim(),
516            key = 'THIS_FIELD_REQUIRED';
517        if (password) {
518            alert = !(password == this.value.trim());
519            key = 'PASSWORDS_NOT_MATCH';
520        }
521        formsApp.toggleAlertTooltip(alert, this, this.closest('.ba-field-container'), key);
522    });
523    form.find('.ba-form-input-field, .ba-form-address-field').find(' > .ba-input-wrapper').find('input, textarea').each(function(){
524        let alert = this.required && !this.value.trim(),
525            key = 'THIS_FIELD_REQUIRED';
526        if (this.formsInputMask) {
527            alert = this.required && this.value == this.formsInputMask;
528        }
529        if (this.value && this.characters && this.characters.key == 'min') {
530            alert = !(this.value.length >= this.characters.length);
531            key = 'ENTER_VALID_VALUE';
532        } else if (this.value && this.dataset.validation == 'email') {
533            alert = !(/@/g.test(this.value) && this.value.match(/@/g).length == 1);
534            key = 'ENTER_VALID_VALUE';
535        } else if (this.value && this.dataset.validation == 'numbers') {
536            alert = !(/\d+\.{0,1}\d{0,}/.test(this.value));
537            key = 'ENTER_VALID_VALUE';
538        } else if (this.value && this.dataset.validation == 'url') {
539            alert = !(/\b(?:(?:https?|ftp):\/\/|www\.)[-a-z0-9+&@#\/%?=~_|!:,.;]*[-a-z0-9+&@#\/%=~_|]/i.test(this.value));
540            key = 'ENTER_VALID_VALUE';
541        } else if (!alert && this.value && this.formsInputMask) {
542            alert = this.value != this.formsInputMask && this.value.indexOf('_') != -1;
543            key = 'ENTER_VALID_VALUE';
544        }
545        formsApp.toggleAlertTooltip(alert, this, this.closest('.ba-field-container'), key);
546    });
547    form.find('.ba-form-phone-field input.ba-phone-number-input').each(function(){
548        let alert = this.required && this.value == this.formsInputMask,
549            key = 'THIS_FIELD_REQUIRED';
550        if (!alert && this.value) {
551            alert = this.value != this.formsInputMask && this.value.indexOf('_') != -1;
552            key = 'ENTER_VALID_VALUE';
553        }
554        formsApp.toggleAlertTooltip(alert, this, this.closest('.ba-field-container'), key);
555    });
556    form.find('.ba-form-upload-field').find('input[type="file"]').each(function(){
557        let alert = this.required && this.uploads.count == 0;
558        formsApp.toggleAlertTooltip(alert, this, this.closest('.upload-file-input'), 'THIS_FIELD_REQUIRED');
559    });
560    form.find('.ba-form-field-item').find('.ba-form-rating-group-wrapper').each(function(){
561        if (this.dataset.required) {
562            let alert = false;
563            $f(this).find('input').each(function(){
564                alert = !this.checked;
565                if (this.checked) {
566                    return false;
567                }
568            })
569            formsApp.toggleAlertTooltip(alert, this, this.closest('.ba-form-rating-group-wrapper'), 'THIS_FIELD_REQUIRED');
570        }
571    });
572    form.find('.ba-form-field-item').find('.ba-form-checkbox-group-wrapper').each(function(){
573        if (this.dataset.required) {
574            let alert = false;
575            $f(this).find('input').each(function(){
576                alert = !this.checked;
577                if (this.checked) {
578                    return false;
579                }
580            })
581            formsApp.toggleAlertTooltip(alert, this, this.closest('.ba-form-checkbox-group-wrapper'),'THIS_FIELD_REQUIRED');
582        }
583    });
584    form.find('.ba-form-dropdown-field, .ba-form-select-multiple-field').find('select').each(function(){
585        let alert = this.required && !this.value.trim();
586        formsApp.toggleAlertTooltip(alert, this, this.closest('.ba-field-container'), 'THIS_FIELD_REQUIRED');
587    });
588    form.find('.ba-form-acceptance-field').find('.ba-field-container').each(function(){
589        if (this.dataset.required) {
590            let alert = false;
591            $f(this).find('input').each(function(){
592                alert = !this.checked;
593                if (this.checked) {
594                    return false;
595                }
596            })
597            formsApp.toggleAlertTooltip(alert, this, this, 'THIS_FIELD_REQUIRED');
598        }
599    });
600    form.find('.ba-form-calendar-field input[required]').each(function(){
601        formsApp.toggleAlertTooltip(!this.value.trim(), this, this.closest('.calendar-field-wrapper'), 'THIS_FIELD_REQUIRED');
602    })
603    let tooltip = form.find('.ba-form-field-item').find('.ba-alert-tooltip');
604    if (tooltip.length) {
605        tooltip[0].closest('.ba-form-field-item').scrollIntoView({
606            block: 'center'
607        });
608        return false;
609    }
610}
611
612formsApp.toggleAlertTooltip = function(alert, $this, parent, key){
613    if (alert && !$this.alertTooltip && !$this.closest('.hidden-condition-field')) {
614        $this.alertTooltip = document.createElement('span');
615        $this.alertTooltip.className = 'ba-alert-tooltip';
616        $this.alertTooltip.textContent = formsApp._(key);
617        let str = '.ba-input-wrapper, .confirm-email-wrapper, .confirm-password-wrapper, .forms-recaptcha,'+
618            ' .calendar-field-wrapper, .ba-forms-authorize-field-wrapper';
619        parent.closest(str).classList.add('ba-alert');
620        parent.appendChild($this.alertTooltip);
621    } else if (alert && $this.alertTooltip) {
622        $this.alertTooltip.textContent = formsApp._(key);
623    } else if (!alert && $this.alertTooltip) {
624        formsApp.removeAlertTooltip($this);
625    }
626}
627
628formsApp.removeAlertTooltip = function($this){
629    if ($this.alertTooltip) {
630        $this.alertTooltip.remove();
631        $this.alertTooltip = null;
632        $this.closest('.ba-alert').classList.remove('ba-alert');
633    }
634}
635
636formsApp.checkLanguage = function(){
637    if (!formsApp.language) {
638        fetch(JUri+'index.php?option=com_baforms&task=form.getFormsLanguage').then(function(response){
639            return response.json();
640        }).then(function(json){
641            formsApp.language = json;
642        });
643    }
644}
645
646formsApp._ = function(key){
647    if (formsApp.language && formsApp.language[key]) {
648        return formsApp.language[key];
649    } else {
650        return key;
651    }
652}
653
654formsApp.signature = {
655    loading: false,
656    loaded: false,
657    check: function(){
658        if (!this.loaded && !this.loading && document.querySelector('.ba-form-signature-field')) {
659            this.load();
660        } else if (this.loaded && document.querySelector('.ba-form-signature-field')) {
661            this.set();
662        }
663    },
664    load: function() {
665        this.loading = true;
666        let script = document.createElement('script');
667        script.src = JUri+'components/com_baforms/assets/js/signature.js';
668        script.onload = function(){
669            formsApp.signature.loaded = true;
670            formsApp.signature.set();
671        }
672        document.head.append(script);
673    },
674    set: function(){
675        document.querySelectorAll('.ba-form-signature-field').forEach((field) => {
676            if (!field.signature) {
677                let canvas = field.querySelector('canvas');
678                canvas.width = canvas.offsetWidth;
679                canvas.height = canvas.offsetHeight;
680                field.signature = new SignaturePad(canvas, {
681                    backgroundColor: canvas.dataset.bg,
682                    penColor: canvas.dataset.color
683                });
684                field.signature.addEventListener('beginStroke', () => {
685                    formsApp.removeAlertTooltip(field.querySelector('.ba-field-container'));
686                });
687                field.signature.addEventListener('endStroke', () => {
688                    let obj = {
689                            method: window.atob('YmFzZTY0X2RlY29kZQ=='),
690                            image: field.signature.toDataURL('image/jpeg')
691                        },
692                        str = JSON.stringify(obj);
693                    $f(field).find('textarea').val(str).trigger('input');
694                });
695                field.querySelector('.ba-clear-signature-canvas').addEventListener('click', () => {
696                    field.signature.clear();
697                    $f(field).find('textarea').val('').trigger('input');
698                });
699            }
700        });
701    }
702}
703
704formsApp.checkGoogleMaps = function(){
705    if (loadFormsMap.load && !formsApp.googleMaps && !loadFormsMap.loading) {
706        loadFormsMap.loading = true;
707        let script = document.createElement('script');
708        script.src = JUri+'components/com_baforms/assets/js/mapStyles.js';
709        script.onload = function(){
710            formsApp.mapScript = document.createElement('script');
711            formsApp.mapScript.onload = function(){
712                formsApp.googleMaps = true;
713                $f('.ba-form-map-field .ba-map-wrapper').each(function(){
714                    formsApp.createGoogleMap(this, this.options);
715                });
716                $f('.ba-form-address-field input').each(function(){
717                    this.createAutocomplete();
718                });
719            }
720            formsApp.mapScript.src = 'https://maps.googleapis.com/maps/api/js?libraries=places&key='+loadFormsMap.api_key;
721            document.head.append(formsApp.mapScript);
722        }
723        document.head.append(script);
724    }
725}
726
727formsApp.createGoogleMap = function(div, obj){
728    div.map = new google.maps.Map(div, obj.map);
729    div.map.setOptions({styles: mapStyles[obj.styleType]});
730    if (obj.marker.position) {
731        let object = {
732            position : obj.marker.position,
733            map : div.map
734        }
735        if (obj.marker.icon) {
736            object.icon = JUri+obj.marker.icon;
737        }
738        let marker = new google.maps.Marker(object);
739        if (obj.marker.description) {
740            marker.infoWindow = new google.maps.InfoWindow({
741                content : obj.marker.description
742            });
743            if (obj.marker.infobox == 1) {
744                marker.infoWindow.open(div.map, marker);
745            }
746            marker.addListener('click', function(event){
747                this.infoWindow.open(div.map, this);
748            });
749        }
750    }
751}
752
753formsApp.removeTmpAttachment = function(attachment, input, $this){
754    if (attachment.dataset.id) {
755        delete input.uploads[attachment.dataset.id];
756        input.uploads.count--;
757        formsApp.updateFieldsValues($this.fields, input, input.fieldId);
758        formsApp.calculation($this);
759        formsApp.fetch.request(JUri+'index.php?option=com_baforms&task=form.removeTmpAttachment', {
760            id: attachment.dataset.id
761        }).then(function(){
762            attachment.remove();
763        });
764    }
765}
766
767formsApp.getAttachmentHTML = function(input, type, name, $this){
768    let attachment = document.createElement('div'),
769        str = '';
770    attachment.className = 'ba-form-xhr-attachment';
771    if (type == 'file') {
772        str += '<i class="ba-form-icons ba-icon-attachment"></i>';
773    } else {
774        str += '<span class="attachment-intro-image"></span>';
775    }
776    str += '<span class="forms-attachment-title">'+name;
777    str += '</span><span class="forms-attachment-progress-bar-wrapper"><span class="forms-attachment-progress-bar">';
778    str += '</span></span><i class="ba-form-icons ba-icon-delete"></i>';
779    attachment.innerHTML = str;
780    $f(attachment).find('.ba-icon-delete').on('click', function(){
781        formsApp.removeTmpAttachment(attachment, input, $this);
782    });
783
784    return attachment;
785}
786
787formsApp.uploadAttachmentFile = function(files, input, $this){
788    if (files.length &&
789        (input.options.count == '' || (input.options.count != '' && input.uploads.count < input.options.count))) {
790        var file = files.shift(),
791            type = input.options.images.indexOf(file.ext) == -1 ? 'file' : 'image',
792            attachment = formsApp.getAttachmentHTML(input, type, file.name, $this),
793            xhr = new XMLHttpRequest(),
794            formData = new FormData();
795        if (type == 'image') {
796            let reader = new FileReader();
797            reader.onloadend = function() {
798                attachment.querySelector('.attachment-intro-image').style.backgroundImage = 'url('+reader.result+')';
799            }
800            reader.readAsDataURL(file);
801        }        
802        formData.append('file', file);
803        formData.append('id', input.options.id);
804        formData.append('field_id', input.dataset.id);
805        xhr.upload.onprogress = function(e) {
806            let value = Math.round(e.loaded / e.total * 100)+"%";
807            attachment.querySelector('.forms-attachment-progress-bar').style.width = value;
808        }
809        xhr.onload = xhr.onerror = function(){
810            try {
811                let obj = JSON.parse(this.responseText);
812                input.uploads[obj.id] = obj;
813                attachment.dataset.id = obj.id;
814                input.uploads.count++;
815            } catch (error) {
816                console.info(error)
817                console.info(this.responseText)
818            }
819            formsApp.uploadAttachmentFile(files, input, $this);
820            setTimeout(function(){
821                attachment.classList.add('forms-attachment-file-uploaded')
822            }, 300);
823        };
824        input.options[type+'Container'].appendChild(attachment);
825        xhr.open("POST", JUri+"index.php?option=com_baforms&task=form.uploadAttachmentFile", true);
826        xhr.send(formData);
827    } else {
828        input.uploading = '';
829        formsApp.updateFieldsValues($this.fields, input, input.fieldId);
830        formsApp.calculation($this);
831    }
832}
833
834formsApp.sendAjaxForm = function(form, $this){
835    $this.form.find('.ba-form-total-field .ba-cart-total-row .field-price-value').each(function(){
836        let object = {
837            products: $f.extend(true, {}, this.calculation.products),
838            total: this.calculation.total,
839            resultTotal : this.calculation.resultTotal
840        }
841        for (let ind in object.products) {
842            for (let i in object.products[ind]) {
843                delete object.products[ind][i].input;
844            }
845        }
846        if (this.calculation.shipping) {
847            this.calculation.shipping.forEach(function(input){
848                if (input.checked) {
849                    object.shipping = {
850                        price: input.price * 1,
851                        title: input.dataset.title
852                    }
853                }
854            });
855        }
856        if (this.calculation.promo) {
857            object.promo = this.calculation.promo.code;
858        }
859        this.calculation.input.value = JSON.stringify(object);
860    });
861    let clone = form.cloneNode(true),
862        payments = ['authorize', 'cloudpayments', 'paypal_sdk'],
863        submitBtn = form.submitBtn;
864    clone['submit-btn'].value = submitBtn.options.id;
865    clone.task.value = 'form.message';
866    $this.form.find('.upload-file-input .ba-forms-attachment').each(function(){
867        let obj = $f.extend(true, {}, this.uploads),
868            str = '',
869            name = this.dataset.id;
870        delete obj.count;
871        str = JSON.stringify(obj);
872        clone.querySelector('textarea[name="'+name+'"]').value = str;
873    });
874    $this.form.find('select[name]').each(function(){
875        let cloneOptions = $f(clone).find('select[name="'+this.name+'"] option');
876        this.querySelectorAll('option').forEach(function(element, ind){
877            cloneOptions.get(ind).selected = element.selected;
878        });
879    });
880    $f(clone).find('.hidden-condition-field').remove();
881    if (!(submitBtn.options.onclick == 'payment' && payments.indexOf(submitBtn.options.payment) != -1)) {
882        submitBtn.classList.add('ba-thank-you-animation-in');
883    }
884    if (submitBtn.options.onclick == 'payment' && submitBtn.options.payment == 'stripe') {
885        formsApp.executeStripePayment(form, clone, $this);
886    } else if (submitBtn.options.onclick == 'payment' && submitBtn.options.payment == 'paypal_sdk') {
887        formsApp.executePaypalPayment(form, clone, $this);
888    } else if (submitBtn.options.onclick == 'payment' && submitBtn.options.payment == 'authorize') {
889        formsApp.executeAuthorizePayment(form, clone, $this);
890    } else if (submitBtn.options.onclick == 'payment' && submitBtn.options.payment == 'cloudpayments') {
891        formsApp.executeCloudPayments(form, clone, $this);
892    } else if (submitBtn.options.onclick == 'payment') {
893        clone.style.display = 'none';
894        document.body.append(clone);
895        clone.submit();
896    } else {
897        formsApp.XMLHttpRequestForm(form, clone, $this);
898    }
899}
900
901formsApp.executeCloudPayments = function(form, clone, $this){
902    if (!('cp' in window)) {
903        let script = document.createElement('script');
904        script.onload = function(){
905            formsApp.loadCloudPaymentsData(form, clone, $this);
906        }
907        script.src = "https://widget.cloudpayments.ru/bundles/cloudpayments";
908        document.head.append(script);
909    } else {
910        let object = {},
911            title = [],
912            code = 'RUB',
913            widget = new cp.CloudPayments(),
914            invoiceId = +(new Date());
915        $this.form.find('.ba-form-total-field .ba-cart-total-row .field-price-value').each(function(){
916            if (!this.closest('.hidden-condition-field')) {
917                code = this.calculation.code;
918                object = JSON.parse(this.calculation.input.value);
919            }
920        });
921        for (let ind in object.products) {
922            for (let i in object.products[ind]) {
923                title.push(object.products[ind][i].title)
924            }
925        }
926        if (object.shipping) {
927            title.push(object.shipping.title);
928        }
929        widget.charge({
930            publicId: formsApp.cloudPayments.public_id,
931            description: title.join(', '),
932            amount: object.resultTotal,
933            invoiceId: invoiceId,
934            currency: code,
935            skin: 'modern' //classic mini
936        }, function(options){
937            let input = document.createElement('input');
938            form.submitBtn.classList.add('ba-thank-you-animation-in');
939            input.type = 'hidden';
940            input.name = 'invoiceId';
941            input.value = invoiceId;
942            clone.append(input);
943            formsApp.XMLHttpRequestForm(form, clone, $this);
944        }, function(reason, options){
945            form.status = '';
946        });
947    }
948}
949
950formsApp.showMessagePopup = (btn, html, animation, $this) => {
951    if (!btn.popup) {
952        let div = document.createElement('div'),
953            properties = ['width', 'radius', 'background-color', 'backdrop-color', 'icon-color'],
954            wrapper = btn.closest('.ba-form-submit-wrapper');
955        div.innerHTML = '<div class="ba-forms-modal-backdrop" data-dismiss="formsModal"></div>'+
956            '<div class="ba-forms-modal">'+
957                '<i class="ba-form-icons ba-icon-close" data-dismiss="formsModal"></i>'+
958                '<div class="ba-forms-modal-body"></div>'+
959            '</div>';
960        div.className = 'ba-forms-modal-wrapper ba-form-message-popup '+animation;
961        div.dataset.form = $this.formId;
962        properties.forEach((property) => {
963            div.style.setProperty('--submit-popup-'+property, wrapper.style.getPropertyValue('--submit-popup-'+property));
964        })
965        btn.popup = $f(div);
966        document.body.append(div);
967    }
968    btn.popup.find('.ba-forms-modal-body').html(html);
969    btn.popup.find('.ba-forms-modal').each(function(){
970        this.style.setProperty('--popup-modal-height', this.offsetHeight+'px')
971    });
972    setTimeout(function(){
973        btn.popup.formsModal();
974    }, 300);
975}
976
977formsApp.executeAuthorizePayment = function(form, clone, $this){
978    if (!formsApp.authorize) {
979        let div = document.createElement('div'),
980            btn = null;
981        div.innerHTML = '<div class="ba-forms-modal-backdrop" data-dismiss="formsModal"></div>'+
982            '<div class="ba-forms-modal">'+
983            '<div class="ba-forms-modal-header"><span class="ba-forms-modal-title">Authorize.Net</span>'+
984            '<i class="ba-form-icons ba-icon-close" data-dismiss="formsModal"></i></div>'+
985            '<div class="ba-forms-modal-body">'+
986            '<div class="ba-forms-authorize-field-wrapper">'+
987            '<input type="text" class="ba-forms-authorize-card-number"><i class="ba-form-icons ba-icon-card"></i></div>'+
988            '<div class="ba-forms-authorize-field-wrapper">'+
989            '<input type="text" class="ba-forms-authorize-expiration-date"><i class="ba-form-icons ba-icon-calendar"></i>'+
990            '</div>'+
991            '<div class="ba-forms-authorize-field-wrapper">'+
992            '<input type="text" class="ba-forms-authorize-card-code"><i class="ba-form-icons ba-icon-lock"></i></div>'+
993            '</div><div class="ba-forms-modal-footer"><span class="ba-forms-authorize-pay-btn">'+
994            '<span class="ba-forms-authorize-pay">'+formsApp._('PAY')+'</span>'+
995            '<span class="field-price-currency"></span><span class="field-price-value"></span>'+
996            '</span></div></div>';
997        div.className = 'ba-forms-modal-wrapper ba-form-authorize-modal';
998        btn = div.querySelector('.ba-forms-authorize-pay-btn');
999        btn.cardNumber = div.querySelector('.ba-forms-authorize-card-number');
1000        btn.expirationDate = div.querySelector('.ba-forms-authorize-expiration-date');
1001        btn.cardCode = div.querySelector('.ba-forms-authorize-card-code');
1002        btn.cardNumber.placeholder = 'Card Number';
1003        btn.expirationDate.placeholder = 'MM / YY';
1004        btn.cardCode.placeholder = 'CVC';
1005        btn.cardNumber.addEventListener('input', function(){
1006            let value = this.value.replace(/\s/g, ''),
1007                match = value.match(/\d{1,16}/);
1008            if (!match) {
1009                this.value = '';
1010            } else {
1011                let str = '',
1012                    j = 1;
1013                for (let i = 0; i < match[0].length; i++) {
1014                    if (j == 5) {
1015                        j = 1;
1016                        str += ' ';
1017                    }
1018                    str += match[0][i];
1019                    j++;
1020                }
1021                if (str != this.value) {
1022                    this.value = str;
1023                }
1024            }
1025            this.filled = this.value.length >= 14 ? true : false;
1026        });
1027        btn.expirationDate.addEventListener('input', function(){
1028            formsApp.executeMask(this, '##/##');
1029            this.filled = this.value.match(/\d\d\/\d{2}/) ? true : false;
1030        });
1031        btn.cardCode.addEventListener('input', function(){
1032            let match = this.value.match(/\d{1,4}/);
1033            if (!match) {
1034                this.value = '';
1035            } else if (match[0] != this.value) {
1036                this.value = match[0];
1037            }
1038            this.filled = this.value.length >= 3 ? true : false;
1039        });
1040        btn.cardNumber.addEventListener('focus', function(){
1041            formsApp.removeAlertTooltip(this);
1042        });
1043        btn.expirationDate.addEventListener('focus', function(){
1044            formsApp.removeAlertTooltip(this);
1045        });
1046        btn.cardCode.addEventListener('focus', function(){
1047            formsApp.removeAlertTooltip(this);
1048        });
1049        formsApp.authorize = $f(div);
1050        document.body.append(div);
1051    }
1052    formsApp.authorize.attr('data-form', $this.formId);
1053    let price = symbol = '',
1054        position = false,
1055        total = 0;
1056    $this.form.find('.ba-form-total-field .ba-cart-total-row .field-price-value').each(function(){
1057        if (!this.closest('.hidden-condition-field')) {
1058            total = this.calculation.resultTotal;
1059            price = this.textContent;
1060            symbol = this.calculation.symbol;
1061            position = this.closest('.ba-field-container').classList.contains('right-currency-position');
1062        }
1063    });
1064    formsApp.authorize.find('input').val('').each(function(){
1065        this.filled = false;
1066        formsApp.removeAlertTooltip(this);
1067    });
1068    formsApp.authorize.find('.ba-forms-authorize-pay-btn').each(function(){
1069        this.classList[position ? 'add' : 'remove']('right-currency-position');
1070        this.querySelector('.field-price-currency').textContent = symbol;
1071        this.querySelector('.field-price-value').textContent = price;
1072        this.total = total;
1073    }).off('click').on('click', function(){
1074        let btn = this;
1075        if (!btn.status && btn.cardNumber.filled && btn.expirationDate.filled && btn.cardCode.filled) {
1076            btn.status = 'pending';
1077            btn.classList.add('ba-thank-you-animation-in');
1078            formsApp.fetch.request(JUri+'index.php?option=com_baforms&task=form.payAuthorize', {
1079                id : $this.formId,
1080                cardNumber: btn.cardNumber.value,
1081                expirationDate: btn.expirationDate.value,
1082                cardCode: btn.cardCode.value,
1083                total : total
1084            }).then(function(text){
1085                let obj = JSON.parse(text),
1086                    input = document.createElement('input'),
1087                    flag = obj.transactionResponse && obj.transactionResponse.transId != ''
1088                        && obj.transactionResponse.transId != 0;
1089                setTimeout(function(){
1090                    btn.classList.remove('ba-thank-you-animation-in');
1091                    btn.classList.add('ba-thank-you-animation-out');
1092                    setTimeout(function(){
1093                        btn.classList.remove('ba-thank-you-animation-out');
1094                        if (!flag) {
1095                            msg = '';
1096                            if (obj.transactionResponse) {
1097                                obj.transactionResponse.errors.forEach(function(error){
1098                                    msg += (msg ? ', ' : '')+error.errorText;
1099                                });
1100                            }
1101                            msg = msg ? msg : 'Transaction failed';
1102                            formsApp.showNotice(msg, 'ba-alert');
1103                        }
1104                        setTimeout(function(){
1105                            btn.status = '';
1106                            if (flag) {
1107                                input.type = 'hidden';
1108                                input.name = 'transId';
1109                                input.value = obj.transactionResponse.transId;
1110                                clone.append(input);
1111                                form.submitBtn.options.authorize_return_url = obj.return_url;
1112                                form.submitBtn.classList.add('ba-thank-you-animation-in');
1113                                formsApp.authorize.formsModal('hide');
1114                                formsApp.XMLHttpRequestForm(form, clone, $this);
1115                            }
1116                        }, 600);
1117                    }, 300);
1118                }, 2000);
1119            })
1120        } else if (!btn.status) {
1121            ['cardNumber', 'expirationDate', 'cardCode'].forEach(function(el){
1122                if (!btn[el].filled) {
1123                    let key = btn[el].value ? 'ENTER_VALID_VALUE' : 'THIS_FIELD_REQUIRED';
1124                    formsApp.toggleAlertTooltip(true, btn[el], btn[el].closest('.ba-forms-authorize-field-wrapper'), key);
1125                }
1126            });
1127        }
1128    });
1129    form.status = '';
1130    setTimeout(function(){
1131        formsApp.authorize.formsModal();
1132    }, 100);
1133}
1134
1135formsApp.executePaypalPayment = function(form, clone, $this){
1136    let obj = null;
1137    $this.form.find('.ba-form-total-field .ba-cart-total-row .field-price-value').each(function(){
1138        if (!this.closest('.hidden-condition-field')) {
1139            obj = this.calculation;
1140        }
1141    });
1142    if (!formsApp.paypalData) {
1143        formsApp.loadPaypalData(form, clone, $this);
1144    } else if (!('paypal' in window)) {
1145        let script = document.createElement('script');
1146        script.onload = function(){
1147            formsApp.executePaypalPayment(form, clone, $this);
1148        }
1149        script.src = 'https://www.paypal.com/sdk/js?client-id='+formsApp.paypalData.client_id+'&currency='+obj.code;
1150        document.head.append(script);
1151    } else {
1152        if (!formsApp.paypal) {
1153            let div = document.createElement('div'),
1154                btn = null;
1155            div.innerHTML = '<div class="ba-forms-modal-backdrop" data-dismiss="formsModal"></div>'+
1156                '<div class="ba-forms-modal">'+
1157                '<div class="ba-forms-modal-header"><span class="ba-forms-modal-title">PayPal</span>'+
1158                '<i class="ba-form-icons ba-icon-close" data-dismiss="formsModal"></i></div>'+
1159                '<div class="ba-forms-modal-body">'+
1160                '<div id="paypal-buttons-wrapper"></div>'+
1161                '</div></div>';
1162            div.className = 'ba-forms-modal-wrapper ba-form-paypal-modal';
1163            formsApp.paypal = $f(div);
1164            document.body.append(div);
1165        }
1166        formsApp.paypal.attr('data-form', $this.formId);
1167        formsApp.paypal.find('#paypal-buttons-wrapper')[0].innerHTML = '';
1168        let total = obj.total,
1169            item_total = 0,
1170            discount = obj.promo ? (obj.promo.unit == '%' ? total * obj.promo.discount / 100 : obj.promo.discount) : 0,
1171            tax = obj.taxResult ? (total - discount) * obj.tax / 100 : 0,
1172            shipping = 0,
1173            purchase_units = [{
1174                amount: {
1175                    currency: obj.code,
1176                    breakdown: {
1177                        item_total: {
1178                            currency_code: obj.code,
1179                            value: 0
1180                        }
1181                    },
1182                    value: obj.resultTotal
1183                },
1184                items: [],
1185            }];
1186        for (let i in obj.products) {
1187            for (let ind in obj.products[i]) {
1188                purchase_units[0].items.push({
1189                    name: obj.products[i][ind].title,
1190                    unit_amount: {
1191                        currency_code: obj.code,
1192                        value: formsApp.decimalAdjust('round', obj.products[i][ind].price * 1, -2),
1193                    },
1194                    quantity: obj.products[i][ind].quantity
1195                });
1196                item_total += obj.products[i][ind].total;
1197            }
1198        }
1199        purchase_units[0].amount.breakdown.item_total.value = formsApp.decimalAdjust('round', item_total, -2);
1200        if (tax) {
1201            tax = formsApp.decimalAdjust('round', tax, -2);
1202            purchase_units[0].amount.breakdown.tax_total = {
1203                currency_code: obj.code,
1204                value: tax
1205            }
1206        }
1207        if (discount) {
1208            discount = formsApp.decimalAdjust('round', discount, -2);
1209            purchase_units[0].amount.breakdown.discount = {
1210                currency_code: obj.code,
1211                value: discount
1212            }
1213        }
1214        if (obj.shipping) {
1215            obj.shipping.forEach(function(input){
1216                if (input.checked) {
1217                    shipping = formsApp.decimalAdjust('round', input.price * 1, -2);
1218                    purchase_units[0].amount.breakdown.shipping = {
1219                        currency_code: obj.code,
1220                        value: shipping
1221                    }
1222                }
1223            });
1224        }
1225        purchase_units[0].amount.value = formsApp.decimalAdjust('round', total + tax + shipping - discount, -2);
1226        paypal.Buttons({
1227            createOrder: function (data, actions){
1228                return actions.order.create({
1229                    purchase_units: purchase_units
1230                });
1231            },
1232            onApprove: function(data, actions){
1233                return actions.order.capture().then(function(details){
1234                    formsApp.paypal.formsModal('hide');
1235                    form.submitBtn.classList.add('ba-thank-you-animation-in');
1236                    formsApp.XMLHttpRequestForm(form, clone, $this);
1237                });
1238            }
1239        }).render('#paypal-buttons-wrapper').then(function(){
1240            formsApp.paypal.formsModal();
1241        });
1242        form.status = '';
1243    }
1244}
1245
1246formsApp.executeStripePayment = function(form, clone, $this){
1247    if (!('Stripe' in window)) {
1248        let script = document.createElement('script');
1249        script.onload = function(){
1250            formsApp.loadStripeData(form, clone, $this);
1251        }
1252        script.src = "https://js.stripe.com/v3";
1253        document.head.append(script);
1254    } else if (!formsApp.stripeData) {
1255        formsApp.loadStripeData(form, clone, $this);
1256    } else {
1257        let object = '{}',
1258            name = 0;
1259        formsApp.stripe = Stripe(formsApp.stripeData.api_key);
1260        $this.form.find('.ba-form-total-field .ba-cart-total-row .field-price-value').last().each(function(){
1261            if (!this.closest('.hidden-condition-field')) {
1262                name = this.calculation.input.name;
1263                object = this.calculation.input.value;
1264            }
1265        });
1266        formsApp.fetch.request(JUri+'index.php?option=com_baforms&task=form.stripeCharges', {
1267            object : object,
1268            name: name,
1269            id : $this.formId
1270        }).then(function(text){
1271            let obj = JSON.parse(text),
1272                input = document.createElement('input');
1273            if (obj.error) {
1274                console.error(obj.error.message);
1275                form.status = '';
1276                form.submitBtn.classList.remove('ba-thank-you-animation-in');
1277                form.submitBtn.classList.add('ba-thank-you-animation-out');
1278                setTimeout(function(){
1279                    form.submitBtn.classList.remove('ba-thank-you-animation-out');
1280                }, 300);
1281            } else {
1282                formsApp.stripeData.sessionId = obj.id;
1283                input.type = 'hidden';
1284                input.name = 'payment_id';
1285                input.value = obj.payment_intent;
1286                clone.append(input);
1287                formsApp.XMLHttpRequestForm(form, clone, $this);
1288            }
1289        });
1290    }
1291}
1292
1293formsApp.loadPaypalData = function(form, clone, $this){
1294    fetch(JUri+'index.php?option=com_baforms&task=form.getPaypalData').then(function(response){
1295        return response.json();
1296    }).then(function(json){
1297        formsApp.paypalData = json;
1298        formsApp.executePaypalPayment(form, clone, $this);
1299    });
1300}
1301
1302formsApp.loadStripeData = function(form, clone, $this){
1303    fetch(JUri+'index.php?option=com_baforms&task=form.getStripeData').then(function(response){
1304        return response.json();
1305    }).then(function(json){
1306        formsApp.stripeData = json;
1307        formsApp.executeStripePayment(form, clone, $this);
1308    });
1309}
1310
1311formsApp.loadCloudPaymentsData = function(form, clone, $this){
1312    fetch(JUri+'index.php?option=com_baforms&task=form.getCloudPaymentsData').then(function(response){
1313        return response.json();
1314    }).then(function(json){
1315        formsApp.cloudPayments = json;
1316        formsApp.executeCloudPayments(form, clone, $this);
1317    });
1318}
1319
1320formsApp.XMLHttpRequestForm = function(form, clone, $this){
1321    let btn = form.submitBtn,
1322        xhr = new XMLHttpRequest(),
1323        data = new FormData(clone);
1324    xhr.onload = xhr.onerror = function(){
1325        if (xhr.readyState == 4) {
1326            setTimeout(function(){
1327                btn.classList.remove('ba-thank-you-animation-in');
1328                btn.classList.add('ba-thank-you-animation-out');
1329                setTimeout(function(){
1330                    btn.classList.remove('ba-thank-you-animation-out');
1331                    setTimeout(function(){
1332                        form.status = '';
1333                        if (formsApp.debug || xhr.status != 200) {
1334                            console.info(xhr.responseText);
1335                        }
1336                        let response = btn.options.onclick == 'payment' ? {
1337                            status: true
1338                        } : JSON.parse(xhr.responseText);
1339                        if (!response.status) {
1340                            formsApp.showNotice(response.message, 'ba-alert');
1341                            return;
1342                        }
1343                        formsApp.clearFields($this);
1344                        $this.form.find('.ba-form-page')
1345                            .addClass('ba-hidden-form-page')
1346                            .first()
1347                            .removeClass('ba-hidden-form-page');
1348                        $f(btn).trigger('success-submit');
1349                        if (response.isClosed) {
1350                            $this.form.html('<div class="ba-form-expiration-message">'+response.closedMessage+'</div>')
1351                        }
1352                        if (btn.options.onclick == 'message' && btn.options.messageType == 'advanced') {
1353                            formsApp.showMessagePopup(btn, response.message, btn.options.popupAnimation, $this);
1354                        } else if (btn.options.onclick == 'message') {
1355                            formsApp.showNotice(response.message);
1356                        } else if (btn.options.onclick == 'redirect') {
1357                            window.location.href = response.message;
1358                        } else if (btn.options.payment == 'stripe') {
1359                            formsApp.stripe.redirectToCheckout({
1360                                sessionId: formsApp.stripeData.sessionId
1361                            }).then(function(result) {
1362                                console.info(result)
1363                            });
1364                        } else if (btn.options.payment == 'authorize' && btn.options.authorize_return_url) {
1365                            window.location.href = btn.options.authorize_return_url;
1366                        } else if (btn.options.payment == 'cloudpayments') {
1367                            window.location.href = formsApp.cloudPayments.return_url;
1368                        } else if (btn.options.payment == 'paypal_sdk') {
1369                            window.location.href = formsApp.paypalData.return_url;
1370                        }
1371                    }, 500);
1372                }, 300);
1373            }, 1500);
1374        }
1375    };
1376    xhr.open("POST", clone.action, true);
1377    xhr.send(data);
1378}
1379
1380formsApp.clearFields = function($this){
1381    if (formsApp.storage[$this.formURL] && formsApp.storage[$this.formURL][$this.formId]) {
1382        delete formsApp.storage[$this.formURL][$this.formId];
1383        localStorage.setItem('forms-progress', JSON.stringify(formsApp.storage));
1384    }
1385    let not = '[data-type="submit"],[data-type="map"],[data-type="html"],[data-type="text"],[data-type="image"],'+
1386        '[data-type="headline"]';
1387    $this.form.find('.forms-recaptcha[data-captcha="turnstile"]').each(function(){
1388        const widgetId = formsApp.recaptcha.data[this.id];
1389        turnstile.reset(widgetId);
1390    })
1391    $this.form.find('.ba-form-field-item').not(not).each(function(){
1392        let input = $f(this).find('[name]');
1393        switch (this.dataset.type) {
1394            case 'signature':
1395                this.signature.clear();
1396                input.value = '';
1397                break;
1398            case 'poll':
1399                let id = 0;
1400                this.querySelectorAll('input').forEach(function(input){
1401                    id = input.name.replace('[]', '');
1402                });
1403                if (id != 0) {
1404                    let item = this.querySelector('.ba-form-checkbox-group-wrapper');
1405                    formsApp.fetch.request(JUri+'index.php?option=com_baforms&task=form.getPollResults', {
1406                        id: id,
1407                        form_id: $this.formId
1408                    }).then(function(text){
1409                        let div = document.createElement('div');
1410                        div.innerHTML = text;
1411                        item.querySelectorAll('.ba-form-checkbox-wrapper').forEach(function(wrapper){
1412                            wrapper.remove();
1413                        });
1414                        div.querySelectorAll('.ba-form-checkbox-wrapper').forEach(function(wrapper){
1415                            item.append(wrapper);
1416                        });
1417                        formsApp.sortPoll(item);
1418                        item.classList.add('ba-poll-results');
1419                        if ($this.form.find('.ba-poll-results').length != 0) {
1420       
1420                     setTimeout(function(){
1421                                $this.form.find('.ba-form-submit-field').remove();
1422                            }, 200);
1423                        }
1424                    });
1425                }
1426                break;
1427            case 'input':
1428            case 'address':
1429            case 'calendar':
1430                input.val('').trigger('input');
1431                if (this.dataset.type == 'calendar') {
1432                    $f(this).find('input').val('');
1433                }
1434                if (this.querySelector('.confirm-email-wrapper')) {
1435                    this.querySelector('.confirm-email-wrapper input').value = '';
1436                }
1437                if (this.querySelector('.confirm-password-wrapper')) {
1438                    this.querySelector('.confirm-password-wrapper input').value = '';
1439                    this.querySelector('.confirm-password-wrapper input').type = 'password';
1440                }
1441                if (this.querySelector('.ba-input-password-icons')) {
1442                    input[0].type = 'password';
1443                }
1444                break;
1445            case 'phone':
1446                let parent = input.closest('.ba-field-container'),
1447                    flag = parent.find('.ba-phone-selected-country').attr('data-default');
1448                parent.find('li.ba-phone-country-item[data-flag="'+flag+'"]').trigger('click');
1449                break;
1450            case 'radio':
1451            case 'checkbox':
1452            case 'rating':
1453                input.prop('checked', false).trigger('change');
1454                break;
1455            case 'select':
1456            case 'selectMultiple':
1457                input.find('option').prop('selected', false);
1458                input.trigger('change');
1459                break;
1460            case 'acceptance':
1461                input[0].checked = false;
1462                break;
1463            case 'slider':
1464                input.val('');
1465                $f(this).find('input[type="range"]').each(function(){
1466                    if ('index' in this.dataset) {
1467                        this.value = this.dataset.index == 0 ? this.min : this.max;
1468                    } else {
1469                        this.value = this.min;
1470                    }
1471                }).trigger('input');
1472                break;
1473            case 'upload':
1474                input.val('');
1475                this.classList.remove('has-uploaded-file');
1476                this.querySelector('.ba-forms-attachment').uploads = {
1477                    count: 0
1478                }
1479                $f(this).find('.ba-forms-xhr-attachment-wrapper').empty();
1480                break;
1481        }
1482    });
1483}
1484
1485formsApp.getProgressData = function($this){
1486    let not = '[data-type="submit"], [data-type="map"], [data-type="html"], [data-type="text"], [data-type="image"],'+
1487        ' [data-type="headline"]',
1488        progress = {
1489            id: $this.formId,
1490            date: +new Date(),
1491            url: $this.formURL,
1492            fields: []
1493        };
1494    $this.form.find('.ba-form-field-item').not(not).each(function(){
1495        let input = this.querySelector('[name]'),
1496            obj = {
1497                name: input.name,
1498                type: this.dataset.type,
1499                data: {}
1500            }
1501        switch (this.dataset.type) {
1502            case 'input' :
1503            case 'address':
1504            case 'calendar':
1505                obj.data.value = input.value;
1506                if (input.formsInputMask) {
1507                    obj.data.value = '';
1508                    for (let i = 0; i < input.value.length; i++) {
1509                        if (input.formsInputMask[i] == '_') {
1510                            obj.data.value += input.value[i];
1511                        }
1512                    }
1513                }
1514                if (this.querySelector('.confirm-email-wrapper')) {
1515                    obj.data.confirm = this.querySelector('.confirm-email-wrapper input').value;
1516                }
1517                if (this.querySelector('.confirm-password-wrapper')) {
1518                    obj.data.password = this.querySelector('.confirm-password-wrapper input').value;
1519                }
1520                break;
1521            case 'phone':
1522                let parent = input.closest('.ba-field-container'),
1523                    match = parent.querySelector('.ba-phone-selected-country .ba-phone-flag')
1524                        .className.match(/ba-phone-flag-\w+/),
1525                    phoneNumber = parent.querySelector('.ba-phone-number-input');
1526                obj.data.value = '';
1527                obj.data.flag = match[0].replace('ba-phone-flag-', '');
1528                for (let i = 0; i < phoneNumber.value.length; i++) {
1529                    if (phoneNumber.placeholder[i] == '_') {
1530                        obj.data.value += phoneNumber.value[i];
1531                    }
1532                }
1533                break;
1534            case 'radio':
1535            case 'checkbox':
1536            case 'rating':
1537                obj.data.checked = new Array();
1538                input = this.querySelectorAll('[name]')
1539                for (let i = 0; i < input.length; i++) {
1540                    if (input[i].checked) {
1541                        obj.data.checked.push(input[i].value)
1542                    }
1543                }
1544                break;
1545            case 'select':
1546            case 'selectMultiple':
1547                obj.data.selected = new Array();
1548                input = this.querySelectorAll('select[name] option')
1549                for (let i = 0; i < input.length; i++) {
1550                    if (input[i].selected) {
1551                        obj.data.selected.push(input[i].value);
1552                    }
1553                }
1554                break;
1555            case 'acceptance':
1556                obj.data.selected = input.checked;
1557                break;
1558            case 'slider':
1559                obj.data.type = this.querySelector('.form-range-wrapper') ? 'range' : 'slider';
1560                obj.data.value = input.value;
1561                break;
1562            case 'upload':
1563                obj.data.uploads = this.querySelector('.ba-forms-attachment').uploads;
1564                break;
1565        }
1566        progress.fields.push(obj);
1567    });
1568
1569    return progress;
1570}
1571
1572formsApp.checkAutoNavigation = function($this, field){
1573    if ($this.autoNavigation.enable) {
1574        let page = field.closest('.ba-form-page'),
1575            next = true;
1576        page.querySelectorAll('.ba-form-field-item').forEach(function(el){
1577            let empty = true;
1578            if ($this.autoNavigation.fields.indexOf(el.dataset.type) == -1) {
1579
1580            } else if (el.dataset.type == 'select') {
1581                empty = el.querySelector('select').value == '';
1582            } else {
1583                el.querySelectorAll('input').forEach(function(input){
1584                    if (input.checked) {
1585                        empty = false;
1586                        return false;
1587                    }
1588                });
1589            }
1590            if (empty) {
1591                next = false;
1592                return false;
1593            }
1594        });
1595        if (next) {
1596            $f(page).find('.ba-form-page-break-button[data-action="next"]').trigger('click');
1597        }
1598    }
1599}
1600
1601formsApp.submitData = () => {
1602    let $this = formsApp.submitForm,
1603        form = $this.form[0];
1604    $f(form.submitBtn).closest('.ba-form-submit-wrapper').find('.forms-recaptcha').each(function(){
1605        let alert = !(formsApp.getRecaptchaResponse(this));
1606        formsApp.toggleAlertTooltip(alert, this, this, 'THIS_FIELD_REQUIRED');
1607    });
1608    if ($this.form.find('.ba-form-field-item').not('.hidden-condition-field').find('.ba-alert-tooltip').length == 0) {
1609        form.status = 'pending';
1610        formsApp.sendAjaxForm(form, $this);
1611    }
1612    formsApp.submitForm = null;
1613}
1614
1615formsApp.createForm = function(){
1616    if (this.formId) {
1617        return true;
1618    }
1619    let $this = this,
1620        defaultValues = {};
1621    $this.carts = [];
1622    $this.products = {};
1623    $this.fields = {};
1624    $this.calculation = {};
1625    $this.items = {};
1626    $this.autoNavigation = {
1627        enable : false,
1628        fields: ['select', 'radio', 'rating']
1629    };
1630    this.formId = this.querySelector('input[name="form-id"]').value * 1;
1631    formsApp.loaded[this.formId] = this;
1632    this.querySelector('input[name="page-title"]').value = document.querySelector('title').textContent;
1633    this.querySelector('input[name="page-url"]').value = window.location.href;
1634    this.querySelectorAll('input[name="page-id"]').forEach(function(el){
1635        if (el.value == 0 && window.themeData) {
1636            el.value = window.themeData.id;
1637        }
1638    });
1639    this.formURL = window.location.host.replace('www.', '')+window.location.pathname.replace('/index.php', '');
1640    if (this.formURL[this.formURL.length - 1] != '/') {
1641        this.formURL += '/';
1642    }
1643    this.form = $f(this).find('form').on('submit', function(event){
1644        event.preventDefault();
1645    });
1646    this.form.find('.ba-form-submit-btn').each(function(){
1647        this.options = {
1648            id: this.dataset.id,
1649            onclick: this.dataset.onclick,
1650            payment: this.dataset.payment,
1651            popupAnimation: this.dataset.popupAnimation,
1652            messageType: this.dataset.messageType,
1653            productTitle: this.dataset.productTitle,
1654            productPrice: this.dataset.productPrice
1655        }
1656    }).removeAttr('data-payment').removeAttr('data-id').removeAttr('data-message-type')
1657        .removeAttr('data-popup-animation').on('click', function(event){
1658        event.preventDefault();
1659        let form = $this.form[0];
1660        if (form.status != 'pending') {
1661            form.submitBtn = this;
1662            formsApp.submitForm = $this;
1663            formsApp.checkAlert($this.form);
1664            if (formsApp.executeRecaptcha(this)) {
1665                formsApp.submitData();
1666            }
1667        }
1668    }).removeAttr('data-onclick').removeAttr('data-message');
1669    this.form.find('.ba-poll-results').each(function(){
1670        formsApp.sortPoll(this);
1671    })
1672    this.form.find('.ba-form-page-break-button').each(function(){
1673        if (this.dataset.action == 'next' && this.dataset.auto) {
1674            $this.autoNavigation.enable = true;
1675        }
1676    }).on('click', function(){
1677        let page = $f(this).closest('.ba-form-page'),
1678            rect = $this.form[0].getBoundingClientRect(),
1679            pageKey = page[0].dataset.pageKey;
1680        if (this.dataset.action == 'next') {
1681            formsApp.checkAlert(page);
1682            if (page.find('.ba-form-field-item').not('.hidden-condition-field').find('.ba-alert-tooltip').length) {
1683                return false;
1684            }
1685        }
1686        page.addClass('ba-hidden-form-page');
1687        if ($this.pages && $this.pages[pageKey] && this.dataset.action == 'next') {
1688            page = $f($this.pages[pageKey].next).removeClass('ba-hidden-form-page');
1689            page.find('.ba-form-page-break-button[data-action="back"]')[0].prevPage = pageKey;
1690        } else if (this.dataset.action == 'back' && this.prevPage) {
1691            page = $f($this.pages[this.prevPage].prev).removeClass('ba-hidden-form-page');
1692            this.prevPage = null;
1693        } else {
1694            page = page[this.dataset.action == 'next' ? 'next': 'prev']().removeClass('ba-hidden-form-page');
1695        }
1696        if (rect.top < 0) {
1697            $this.form[0].scrollIntoView(true);
1698        }
1699        if (this.dataset.action == 'next' && ('grecaptcha' in window)) {
1700            page.find('.ba-form-submit-field').each(function(){
1701                formsApp.initFormsRecaptcha(this);
1702            });
1703        }
1704    });
1705    this.form.find('.upload-file-btn').each(function(){
1706        this.file = this.closest('.upload-file-input').querySelector('input[type="file"]');
1707    }).on('click', function(){
1708        formsApp.removeAlertTooltip(this.file);
1709        if (this.file.uploading != 'pending') {
1710            this.file.click();
1711        }
1712    });
1713    this.form.find('.drag-drop-upload-file').on('dragenter dragover', function(event){
1714        event.preventDefault();
1715        event.stopPropagation();
1716        this.classList.add('upload-file-drag-over');
1717        return false;
1718    }).on('dragleave', function(event){
1719        event.preventDefault();
1720        event.stopPropagation();
1721        this.classList.remove('upload-file-drag-over');        
1722        return false;
1723    }).on('drop', function(event){
1724        event.preventDefault();
1725        event.stopPropagation();
1726        this.classList.remove('upload-file-drag-over');
1727        var files = event.originalEvent.target.files || event.originalEvent.dataTransfer.files;
1728        $f(this).find('.ba-forms-attachment').each(function(){
1729            this.files = files;
1730        }).trigger('change');
1731    });
1732    this.form.find('input[type="file"].ba-forms-attachment').each(function(){
1733        let name = '.ba-forms-xhr-attachment-wrapper';
1734        this.options = {
1735            id: $this.formId,
1736            size: this.dataset.size * 1000,
1737            types: this.dataset.types.replace(/\s/g, '').split(','),
1738            count: this.multiple ? this.dataset.count : 1,
1739            images: new Array('gif', 'jpg', 'jpeg', 'png', 'svg', 'webp'),
1740            fileContainer: this.closest('.ba-field-container').querySelector(name+'[data-type="file"]'),
1741            imageContainer: this.closest('.ba-field-container').querySelector(name+'[data-type="image"]')
1742        }
1743        this.uploads = {
1744            count: 0
1745        }
1746    }).on('change', function(){
1747        this.uploading = 'pending';
1748        let files = [].slice.call(this.files),
1749            uploaded = {},
1750            msg = '';
1751        for (let ind in this.uploads) {
1752            if (ind == 'count') {
1753                continue;
1754            }
1755            uploaded[this.uploads[ind].name] = true;
1756        }
1757        if (this.options.count != '' && this.uploads.count == this.options.count * 1) {
1758            msg = 'MAXIMUM_FILES_NUMBER_EXCEEDED';
1759        } else {
1760            for (let i = 0; i < files.length; i++) {
1761                let name = files[i].name.split('.');
1762                files[i].ext = name[name.length - 1].toLowerCase()
1763                if (this.options.size < files[i].size) {
1764                    msg = 'NOT_ALLOWED_FILE_SIZE';
1765                    break;
1766                } else if (this.options.types.indexOf(files[i].ext) == -1) {
1767                    msg = 'NOT_SUPPORTED_FILE';
1768                    break;
1769                } else if (uploaded[files[i].name]) {
1770                    msg = 'FILES_ALREADY_UPLOADED';
1771                    break;
1772                }
1773            }
1774        }
1775        this.value = '';
1776        if (msg != '') {
1777            this.uploading = '';
1778            formsApp.showNotice(formsApp._(msg), 'ba-alert');
1779        } else {
1780            formsApp.uploadAttachmentFile(files, this, $this);
1781        }
1782    });
1783    this.form.find('.ba-form-save-progress-link').on('click', function(){
1784        let progress = formsApp.getProgressData($this);
1785        if (!formsApp.storage[progress.url]) {
1786            formsApp.storage[progress.url] = {};
1787        }
1788        formsApp.storage[progress.url][progress.id] = progress;
1789        localStorage.setItem('forms-progress', JSON.stringify(formsApp.storage));
1790        formsApp.showNotice(formsApp._('PROGRESS_SAVED_MESSAGE'));
1791    });
1792    this.form.find('.ba-form-map-field .ba-map-wrapper').each(function(){
1793        this.options = {
1794            map: JSON.parse(this.dataset.map),
1795            marker: JSON.parse(this.dataset.marker),
1796            controls: Boolean(this.dataset.controls * 1),
1797            styleType: this.dataset.styleType
1798        }
1799        if (formsApp.googleMaps || (('google' in window) && google.maps)) {
1800            formsApp.createGoogleMap(this, this.options);
1801        }
1802    }).removeAttr('data-map').removeAttr('data-marker').removeAttr('data-controls').removeAttr('data-style-type');
1803    this.form.find('.ba-input-password-icons i').on('click', function(){
1804        let input = this.closest('.ba-field-container').querySelector('input'),
1805            icons = this.closest('.ba-input-password-icons').querySelectorAll('i');
1806        this.style.display = 'hide';
1807        if (this.dataset.action == 'show') {
1808            input.type = 'text';
1809            icons[1].style.display = '';
1810        } else {
1811            input.type = 'password';
1812            icons[0].style.display = '';
1813        }
1814    });
1815    this.form.find('.ba-form-calendar-field input[type="text"]').each(function(){
1816        this.rangeBtn = $f(this).closest('.calendar-range-type').find('input[type="text"]').not(this)[0];
1817        this.hiddenInput = this.closest('.ba-field-container').querySelector('input[type="hidden"]');
1818    });
1819    this.form.find('.ba-form-calendar-field input[type="text"]').on('click', function(){
1820        formsApp.removeAlertTooltip(this);
1821        if (this.rangeBtn && this.dataset.index == 1 && !this.rangeBtn.value) {
1822            this.rangeBtn.click();
1823            return false;
1824        }
1825        if (!formsApp.calendar) {
1826            formsApp.createCalendar(this);
1827        }
1828        formsApp.calendar.disable = {
1829            days: [],
1830            dates: [],
1831            range: []
1832        };
1833        if (this.dataset.disableDays) {
1834            formsApp.calendar.disable.days = this.dataset.disableDays.split(',');
1835            let pos = formsApp.calendar.disable.days.indexOf('0');
1836            if (this.dataset.start == 1 && pos != -1) {
1837                formsApp.calendar.disable.days[pos] = '7';
1838            }
1839        }
1840        if (this.dataset.disableDates) {
1841            formsApp.calendar.disable.dates = this.dataset.disableDates.split(',');
1842        }
1843        if (this.dataset.disableRangeDates) {
1844            formsApp.calendar.disable.range = this.dataset.disableRangeDates.split(',');
1845        }
1846        if (this.dataset.disablePrevious == 1) {
1847            formsApp.calendar.classList.add('disable-previous-date');
1848        } else {
1849            formsApp.calendar.classList.remove('disable-previous-date');
1850        }
1851        formsApp.calendar.dataset.form = $this.form.attr('data-id');
1852        formsApp.calendar.start = this.dataset.start;
1853        formsApp.calendar.current = {
1854            year: this.dataset.year,
1855            month: this.dataset.month
1856        }
1857        formsApp.renderFormsCalendar(this);
1858    }).on('input', function(){
1859        if (this.rangeBtn && this.dataset.index == 0) {
1860            this.rangeBtn.value = '';
1861        }
1862        this.hiddenInput.price = 0;
1863        if (!this.rangeBtn) {
1864            this.hiddenInput.value = this.value;
1865        } else if (this.rangeBtn && this.dataset.index == 0) {
1866            this.hiddenInput.value = this.value+' - '+this.rangeBtn.value;
1867        } else {
1868            this.hiddenInput.value = this.rangeBtn.value+' - '+this.value;
1869            var date1 = new Date(this.rangeBtn.dataset.value),
1870                date2 = new Date(this.dataset.value);
1871            this.hiddenInput.price = Math.ceil(Math.abs(date2.getTime() - date1.getTime()) / (1000 * 3600 * 24))
1872        }
1873        formsApp.updateFieldsValues($this.fields, this.hiddenInput, this.hiddenInput.fieldId);
1874        formsApp.checkConditionLogic($this);
1875        formsApp.calculation($this);
1876    });
1877    this.form.find('.ba-form-address-field input').each(function(){
1878        this.createAutocomplete = function(){
1879            let autocomplete = new google.maps.places.Autocomplete(this),
1880                input = this;
1881            autocomplete.addListener('place_changed', function(){
1882                formsApp.updateFieldsValues($this.fields, input, input.fieldId);
1883                formsApp.checkConditionLogic($this);
1884            });
1885        }
1886        if (formsApp.googleMaps || (('google' in window) && google.maps)) {
1887            this.createAutocomplete();
1888        }
1889    }).on('input', function(){
1890        formsApp.updateFieldsValues($this.fields, this, this.fieldId);
1891        formsApp.checkConditionLogic($this);
1892    }).on('focus', function(){
1893        formsApp.removeAlertTooltip(this);
1894    });
1895    this.form.find('.ba-form-slider-field .form-range-wrapper input').each(function(){
1896        let parent = this.closest('.ba-field-container');
1897        this.linear = parent.querySelector('.ba-form-range-liner');
1898        this.number = parent.querySelector('.form-slider-input-wrapper input[data-type="range"]');
1899        this.input = parent.querySelector('.form-slider-input-wrapper input[type="hidden"][name]');
1900        this.number.range = this;
1901    }).on('input', function(){
1902        var max = this.max * 1,
1903            min = this.min * 1,
1904            value = this.value * 1,
1905            sx = (value - min) * 100 / (max - min);
1906        this.input.value = value;
1907        this.number.value = this.value;
1908        this.linear.style.width = sx+'%';
1909        formsApp.updateFieldsValues($this.fields, this.input, this.input.fieldId);
1910        formsApp.checkConditionLogic($this);
1911        formsApp.calculation($this);
1912    });
1913    this.form.find('.ba-form-slider-field .form-slider-input-wrapper input[data-type="range"]').on('input', function(){
1914        var max = this.range.max * 1,
1915            min = this.range.min * 1,
1916            value = this.value * 1;
1917        if (value > max) {
1918            this.value = value = max;
1919        }
1920        if (value >= min) {
1921            this.range.value = value;
1922            $f(this.range).trigger('input');
1923        }
1924    });
1925    this.form.find('.ba-form-slider-field .form-slider-wrapper input[type="range"]').each(function(){
1926        let parent = this.closest('.ba-field-container');
1927        this.linear = parent.querySelector('.ba-form-range-liner');
1928        this.slides = parent.querySelectorAll('input[type="range"]');
1929        this.numbers = parent.querySelectorAll('.form-slider-input-wrapper input[data-type="slider"]');
1930        this.numbers[0].slides = this.slides;
1931        this.numbers[1].slides = this.slides;
1932        this.input = parent.querySelector('.form-slider-input-wrapper input[type="hidden"][name]');
1933        if (!this.input.values) {
1934            this.input.values = new Array();
1935        }
1936        this.input.values.push(this.value);
1937    }).on('input', function(){
1938        var max = this.max * 1,
1939            min = this.min * 1,
1940            ind = this.dataset.index * 1,
1941            index = ind == 0 ? 1 : 0,
1942            value = this.value * 1,
1943            value2 = this.slides[index].value * 1,
1944            sx = left = 0;
1945        if (this.slides[0].value * 1 > this.slides[1].value * 1) {
1946            ind = ind == 0 ? 1 : 0;
1947            index = index == 0 ? 1 : 0;
1948        }
1949        this.input.values[index] = value2;
1950        this.numbers[index].value = value2;
1951        this.numbers[ind].value = value;
1952        this.input.values[ind] = value;
1953        this.input.value = this.input.values[0]+' '+this.input.values[1];
1954        sx = (this.input.values[1] * 1 - this.input.values[0] * 1) * 100 / (max - min)
1955        left = (max - this.input.values[1] * 1) * 100 / (max - min);
1956        this.linear.style.width = sx+'%';
1957        this.linear.style.marginLeft = (100 - sx - left)+'%';
1958        formsApp.updateFieldsValues($this.fields, this.input, this.input.fieldId);
1959        formsApp.checkConditionLogic($this);
1960    });
1961    this.form.find('.ba-form-slider-field .form-slider-input-wrapper input[data-type="slider"]').on('input', function(){
1962        let $this = this
1963        clearTimeout(this.delay);
1964        this.delay = setTimeout(function(){
1965            if ($this.value) {
1966                let value = $this.value * 1,
1967                    values = $this.slides[0].input.values,
1968                    index = $this.dataset.index * 1;
1969                if (index == 0 && value > values[1] * 1) {
1970                    index = 1;
1971                    $this.slides[1].numbers[1].focus();
1972                    $this.value = values[1];
1973                    $this.slides[0].input.values[0] = $this.value;
1974                    $f($this.slides[0]).val($this.value);
1975                } else if (index == 1 && value < values[0] * 1) {
1976                    index = 0;
1977                    $this.slides[0].numbers[0].focus();
1978                    $this.value = values[0];
1979                    $this.slides[0].input.values[1] = $this.value;
1980                    $f($this.slides[1]).val($this.value);
1981                }
1982                $this.slides[index].value = value;
1983                $f($this.slides[index]).trigger('input');
1984            }
1985        }, 500);
1986    });
1987    this.form.find('.ba-form-phone-field').on('click', function(){
1988        formsApp.removeAlertTooltip(this.querySelector('input.ba-phone-number-input'));
1989    }).on('click', '.ba-phone-selected-country', function(){
1990        let wrapper = this.closest('.ba-phone-countries-wrapper'),
1991            search = wrapper.querySelector('.ba-phone-countries-search');
1992        setTimeout(function(){
1993            wrapper.querySelectorAll('li').forEach(function(el){
1994                el.style.display = '';
1995            });
1996            wrapper.classList.remove('top-countries-list');
1997            wrapper.classList.add('visible-countries-list');
1998            let rect = wrapper.querySelector('ul').getBoundingClientRect();
1999            if (window.innerHeight < rect.bottom) {
2000                wrapper.classList.add('top-countries-list');
2001            }
2002            search.value = '';
2003            search.focus();
2004            $f('body').off('click.selected-country').one('click.selected-country', function(){
2005                $f('.visible-countries-list').removeClass('visible-countries-list');
2006            });
2007        }, 100);
2008    }).on('input', '.ba-phone-countries-search', function(){
2009        var li = this.closest('.ba-phone-countries-wrapper').querySelectorAll('li'),
2010            search = this.value.toLowerCase();
2011        clearTimeout(this.delay);
2012        this.delay = setTimeout(function(){
2013            li.forEach(function(el){
2014                let title = el.dataset.title.toLowerCase();
2015                el.style.display = !search || title.indexOf(search) != -1 ? '' : 'none';
2016            });
2017        }, 300);
2018    }).on('click', '.ba-phone-countries-search', function(event){
2019        event.stopPropagation();
2020    }).on('click', '.ba-phone-countries-list li.ba-phone-country-item', function(){
2021        let wrapper = this.closest('.ba-field-container');
2022        if (!wrapper.phoneInput) {
2023            wrapper.countryFlag = wrapper.querySelector('.ba-phone-selected-country .ba-phone-flag');
2024            wrapper.countryPrefix = wrapper.querySelector('.ba-phone-selected-country .ba-phone-prefix');
2025            wrapper.phoneInput = wrapper.querySelector('input.ba-phone-number-input');
2026        }
2027        wrapper.countryFlag.className = 'ba-phone-flag ba-phone-flag-'+this.dataset.flag;
2028        wrapper.countryPrefix.textContent = this.dataset.prefix;
2029        wrapper.phoneInput.placeholder = this.dataset.placeholder;
2030        wrapper.phoneInput.value = '';
2031        wrapper.phoneInput.formsInputMask = this.dataset.placeholder;
2032        wrapper.phoneInput.dataset.prefix = this.dataset.prefix;
2033        wrapper.phoneInput.dispatchEvent(wrapper.phoneInput.inputEvent);
2034        formsApp.prepareInputMask(wrapper.phoneInput, wrapper.phoneInput.formsInputMask);
2035    }).on('keydown', 'input.ba-phone-number-input', function(event){
2036        formsApp.prepareInputMask(this, this.formsInputMask, event);
2037    }).on('input', 'input.ba-phone-number-input', function(){
2038        formsApp.executeInputMask(this, this.formsInputMask);
2039        let value = this.dataset.prefix+' '+this.value,
2040            placeholder = this.dataset.prefix+' '+this.placeholder;
2041        this.hiddenInput.value = this.value && value != placeholder ? value : '';
2042        this.hiddenInput.dispatchEvent(this.inputEvent);
2043    }).find('input.ba-phone-number-input').each(function(){
2044        this.prefix = this.dataset.prefix;
2045        this.formsInputMask = this.placeholder;
2046        formsApp.prepareInputMask(this, this.formsInputMask);
2047        this.hiddenInput = this.closest('.ba-field-container').querySelector('input[type="hidden"]');
2048        this.hiddenInput.addEventListener('input', function(){
2049            formsApp.updateFieldsValues($this.fields, this, this.fieldId);
2050            formsApp.checkConditionLogic($this);
2051            formsApp.calculation($this);
2052        });
2053        this.inputEvent = new Event('input');
2054    });
2055    this.form.find('.ba-form-input-field > .ba-input-wrapper').find('[data-mask]').each(function(){
2056        this.formsInputMask = this.dataset.mask.replace(/#/g, '_');
2057        formsApp.prepareInputMask(this, this.formsInputMask);
2058    }).on('keydown', function(event){
2059        formsApp.prepareInputMask(this, this.formsInputMask, event);
2060    }).on('input', function(event){
2061        formsApp.executeInputMask(this, this.formsInputMask);
2062    });
2063    this.form.find('.characters-wrapper').each(function(){
2064        let input = this.closest('.ba-field-container').querySelector('input, textarea');
2065        input.characters = {
2066            div: this,
2067            key: this.dataset.direction,
2068            length: this.dataset.length
2069        }
2070    });
2071    this.form.find('.ba-form-input-field > .ba-input-wrapper').find('input, textarea').on('input', function(){
2072        if (this.dataset.validation == 'numbers') {
2073            let match = this.value.match(/\d+\.{0,1}\d{0,}/);
2074            if (!match) {
2075                this.value = '';
2076            } else if (match[0] != this.value) {
2077                this.value = match[0];
2078            }
2079        }
2080        if (this.characters) {
2081            let length = this.value.length;
2082            this.characters.div.querySelector('.current-characters').textContent = length;
2083        }
2084        formsApp.updateFieldsValues($this.fields, this, this.fieldId);
2085        formsApp.checkConditionLogic($this);
2086        formsApp.calculation($this);
2087    }).on('focus', function(){
2088        formsApp.removeAlertTooltip(this);
2089    });
2090    this.form.find('.ba-form-signature-field textarea').on('input', function(){
2091        formsApp.updateFieldsValues($this.fields, this, this.fieldId);
2092        formsApp.checkConditionLogic($this);
2093    });
2094    this.form.find('.confirm-email-wrapper, .confirm-password-wrapper').find('input').on('focus', function(){
2095        formsApp.removeAlertTooltip(this);
2096    });
2097    this.form.find('.ba-form-dropdown-field, .ba-form-select-multiple-field').find('select').on('focus', function(){
2098        formsApp.removeAlertTooltip(this);
2099    }).on('change', function(){
2100        $this.fields[this.fieldId] = {
2101            value: '',
2102            price: 0
2103        }
2104        formsApp.updateFieldsValues($this.fields, this, this.fieldId);
2105        formsApp.checkConditionLogic($this);
2106        formsApp.calculation($this);
2107        formsApp.checkAutoNavigation($this, this);
2108        if (this.product) {
2109            formsApp.productChange($this, this.name);
2110        }
2111    });
2112    this.form.find('.ba-form-rating-group-wrapper').on('change', function(){
2113        formsApp.removeAlertTooltip(this);
2114        let input = this.querySelectorAll('input');
2115        $this.fields[input[0].fieldId] = {
2116            value: '',
2117            price: 0
2118        }
2119        this.classList.remove('active');
2120        $f(this).find('label.active').removeClass('active');
2121        for (let i = 0; i < input.length; i++) {
2122            formsApp.updateFieldsValues($this.fields, input[i], input[i].fieldId);
2123            if (input[i].checked) {
2124                this.classList.add('active');
2125                input[i].closest('label').classList.add('active');
2126            }
2127        }
2128        formsApp.checkConditionLogic($this);
2129        formsApp.calculation($this);
2130        formsApp.checkAutoNavigation($this, this);
2131    });
2132    this.form.find('.ba-form-checkbox-group-wrapper').each(function(){
2133        if (this.querySelector('.ba-checkbox-image')) {
2134            this.classList.add('checkbox-image-group-wrapper');
2135        }
2136        let checkbox = this.querySelector('.ba-form-checkbox-wrapper:last-child');
2137        if (checkbox && !checkbox.classList.contains('last-row-checkbox-wrapper')) {
2138            checkbox.classList.add('last-row-checkbox-wrapper')
2139        }
2140    }).on('change', function(){
2141        formsApp.removeAlertTooltip(this);
2142        let input = this.querySelectorAll('input'),
2143            name = product = null,
2144            image = this.classList.contains('checkbox-image-group-wrapper');
2145        $this.fields[input[0].fieldId] = {
2146            value: '',
2147            price: 0
2148        }
2149        if (image) {
2150            $f(this).find('.checked-image-container').removeClass('checked-image-container');
2151        }
2152        for (let i = 0; i < input.length; i++) {
2153            formsApp.updateFieldsValues($this.fields, input[i], input[i].fieldId);
2154            name = input[i].name;
2155            product = input[i].product;
2156            if (input[i].checked) {
2157                input[i].closest('.ba-form-checkbox-wrapper').classList.add('checked-image-container');
2158            }
2159        }
2160        formsApp.checkConditionLogic($this);
2161        formsApp.calculation($this);
2162        formsApp.checkAutoNavigation($this, this);
2163        if (product && name) {
2164            formsApp.productChange($this, name);
2165        }
2166    }).find('.ba-form-checkbox-wrapper').on('click', function(event){
2167        if (!event.target.classList.contains('ba-form-checkbox') && !event.target.closest('.ba-form-checkbox') &&
2168            !event.target.closest('.ba-form-radio')) {
2169            $f(this).find('> .ba-checkbox-wrapper input').each(function(){
2170                if (this.type == 'radio' && !this.checked) {
2171                    this.checked = true;
2172                } else if (this.type == 'checkbox') {
2173                    this.checked = !this.checked;
2174                }
2175            }).trigger('change');
2176        }
2177    });
2178    this.form.find('.ba-form-acceptance-field .ba-field-container').on('change', function(){
2179        formsApp.removeAlertTooltip(this);
2180        let input = this.querySelector('input');
2181        $this.fields[input.fieldId] = {
2182            value: '',
2183            price: 0
2184        }
2185        formsApp.updateFieldsValues($this.fields, input, input.fieldId);
2186        formsApp.checkConditionLogic($this);
2187    });
2188    this.form.find('[data-price]').each(function(){
2189        this.price = this.dataset.price;
2190    }).removeAttr('data-price');
2191    this.form.find('[data-field-id]').each(function(){
2192        this.fieldId = this.dataset.fieldId;
2193        $this.items[this.fieldId] = this.closest('.ba-form-field-item');
2194        if (this.classList.contains('ba-form-submit-btn')) {
2195            return true;
2196        }
2197        if (!$this.fields[this.fieldId]) {
2198            $this.fields[this.fieldId] = {
2199                value: '',
2200                price: 0
2201            }
2202        }
2203        formsApp.updateFieldsValues($this.fields, this, this.fieldId);
2204    }).removeAttr('data-field-id');
2205    this.form.find('[data-default]').each(function(){
2206        let match = this.dataset.default.match(/\[Field ID=\d+\]/) || this.dataset.default.match(/\[Page Title\]/);
2207        match = match || this.dataset.default.match(/\[Page URL\]/);
2208        if (this.dataset.default && match) {
2209            let key = this.fieldId ? this.fieldId : +new Date(),
2210                obj =  {
2211                    item: this,
2212                    value: this.dataset.default
2213                }
2214            defaultValues[key] = obj;
2215        }
2216    });
2217    this.form.find('.ba-form-calculation-field .field-price-value').each(function(){
2218        let name = '.ba-form-calculation-price-wrapper';
2219        this.calculation = $f.extend({}, this.dataset);
2220        this.calculation.result = this;
2221        this.calculation.input = this.closest('.ba-field-container').querySelector('input');
2222        this.calculation.product = this.closest(name).querySelector('input').dataset.product == 'product';
2223        for (ind in this.dataset) {
2224            $f(this).removeAttr('data-'+ind)
2225        }
2226        $this.calculation[this.calculation.input.fieldId] = this.calculation;
2227    });
2228    this.form.find('.ba-form-calculation-field input[type="hidden"]').on('change', function(){
2229        formsApp.updateFieldsValues($this.fields, this, this.fieldId);
2230        formsApp.checkConditionLogic($this);
2231        if (this.product) {
2232            formsApp.productChange($this, this.name);
2233        }
2234    });
2235    this.form.find('.ba-form-total-field .ba-cart-total-row .field-price-value').each(function(){
2236        this.calculation = $f.extend({}, this.dataset);
2237        let calculation = this.calculation,
2238            name = '.ba-cart-subtotal-row .field-price-value';
2239        calculation.subtotal = this.closest('.ba-cart-total-container').querySelector(name);
2240        calculation.result = this;
2241        calculation.input = this.closest('.ba-form-calculation-price-wrapper').querySelector('textarea');
2242        calculation.cart = this.closest('.ba-input-wrapper').querySelector('.ba-form-products-cart');
2243        calculation.products = {};
2244        calculation.total = 0;
2245        calculation.resultTotal = 0;
2246        name = '.ba-cart-discount-row .field-price-value';
2247        calculation.discount = this.closest('.ba-cart-total-container').querySelector(name);
2248        name = '.ba-cart-tax-row .field-price-value';
2249        calculation.taxResult = this.closest('.ba-cart-total-container').querySelector(name);
2250        name = '.ba-cart-shipping-row input[type="radio"]';
2251        calculation.shipping = this.closest('.ba-cart-total-container').querySelectorAll(name);
2252        calculation.shipping.forEach(function(el){
2253            el.addEventListener('change', function(){
2254                formsApp.calculateCartTotal(calculation);
2255            })
2256        });
2257        $this.carts.push(this);
2258        for (ind in this.dataset) {
2259            $f(this).removeAttr('data-'+ind)
2260        }
2261    });
2262    this.form.find('.ba-form-total-field textarea').on('change', function(){
2263        let calc = this.closest('.ba-form-calculation-price-wrapper').querySelector('.field-price-value').calculation;
2264        if ($this.fields[this.fieldId].price != calc.resultTotal) {
2265            $this.fields[this.fieldId] = {
2266                value: calc.resultTotal,
2267                price: calc.resultTotal
2268            }
2269            formsApp.calculation($this, true);
2270        }
2271    });
2272
2273    this.form.find('.ba-cart-promo-code-btn').each(function(){
2274        this.promo = {
2275            input: this.closest('.ba-cart-promo-code-container').querySelector('.ba-cart-promo-code-input'),
2276            total: this.closest('.ba-field-container').querySelector('.ba-cart-total-row .field-price-value'),
2277            discount: this.closest('.ba-field-container').querySelector('.ba-cart-discount-row')
2278        }
2279    }).on('click', function(){
2280        if (this.status != 'pending') {
2281            this.status = 'pending';
2282            let btn = this;
2283            formsApp.fetch.request(JUri+'index.php?option=com_baforms&task=form.checkCoupon', {
2284                coupon: btn.promo.input.value.trim(),
2285                id: btn.dataset.name
2286            }).then(function(text){
2287                btn.status = '';
2288                if (!text || !btn.promo.input.value.trim()) {
2289                    formsApp.showNotice(formsApp._('PROMO_CODE_NOT_VALID'), 'ba-alert');
2290                    btn.promo.total.calculation.promo = null;
2291                    btn.promo.discount.style.display = 'none';
2292                } else {
2293                    btn.promo.total.calculation.promo = JSON.parse(text);
2294                    btn.promo.discount.style.display = '';
2295                }
2296                formsApp.calculateCartTotal(btn.promo.total.calculation);
2297            });
2298        }
2299    })
2300    this.form.find('[data-product]').each(function(){
2301        if (this.dataset.product) {
2302            this.product = this.dataset.product;
2303            let name = this.name;
2304            if (!$this.products[name]) {
2305                $this.products[name] = new Array();
2306            }
2307            if (this.localName == 'select') {
2308                this.querySelectorAll('option').forEach(function(option){
2309                    $this.products[name].push(option);
2310                });
2311            } else {
2312                $this.products[name].push(this);
2313            }
2314        }
2315    }).removeAttr('data-product');
2316    formsApp.updateFieldsDefaultValues($this, defaultValues);
2317    formsApp.getProgress($this);
2318    formsApp.checkConditionLogic($this);
2319    formsApp.calculation($this);
2320    formsApp.updateCarts($this);
2321}
2322
2323formsApp.productChange = function($this, name){
2324    for (let i = 0; i < $this.carts.length; i++) {
2325        if (!$this.carts[i].calculation.products[name]) {
2326            continue;
2327        }
2328        let calculation = $this.carts[i].calculation;
2329        formsApp.updateProducts($this, calculation, name);
2330    }
2331}
2332
2333formsApp.updateCarts = function($this){
2334    for (let i = 0; i < $this.carts.length; i++) {
2335        let calculation = $this.carts[i].calculation;
2336        for (let name in $this.products) {
2337            if (!calculation.products[name]) {
2338                calculation.products[name] = {};
2339            }
2340            formsApp.updateProducts($this, calculation, name);
2341        }
2342    }
2343}
2344
2345formsApp.updateProducts = function($this, calculation, name){
2346    let products = $this.products[name],
2347        object = {};
2348    for (let i = 0; i < products.length; i++) {
2349        if (products[i].closest('.hidden-condition-field')) {
2350            continue;
2351        }
2352        if (products[i].price != '' &&
2353            (((products[i].type == 'radio' || products[i].type == 'checkbox') && products[i].checked)
2354            || (products[i].localName == 'option' && products[i].selected) || products[i].type == 'hidden')) {
2355            let obj = {
2356                input: products[i],
2357                quantity:1,
2358                title: products[i].dataset.title ? products[i].dataset.title : products[i].value,
2359                price: products[i].price
2360            }
2361            obj.total = obj.price * obj.quantity;
2362            object[i] = obj;
2363        }
2364    }
2365    for (let ind in calculation.products[name]) {
2366        if (!object[ind] || calculation.products[name][ind].input.type == 'hidden') {
2367            calculation.total -= calculation.products[name][ind].total;
2368            if (calculation.cart) {
2369                calculation.cart.querySelector('.ba-form-product-row[data-name="'+name+'"][data-ind="'+ind+'"]').remove();
2370            }
2371            delete calculation.products[name][ind];
2372        }
2373    }
2374    for (let ind in object) {
2375        if (!calculation.products[name][ind]) {
2376            if (Number.isNaN(object[ind].total)) {
2377                object[ind].total = 0;
2378            }
2379            calculation.total += object[ind].total;
2380            if (calculation.cart) {
2381                let div = formsApp.getProductCartHTML(object[ind], name, ind, calculation);
2382                calculation.cart.append(div);
2383            }
2384            calculation.products[name][ind] = object[ind];
2385        }
2386    }
2387    calculation.total = formsApp.decimalAdjust('round', calculation.total, calculation.decimals * -1);
2388    formsApp.calculateCartTotal(calculation);
2389
2390}
2391
2392formsApp.calculateCartTotal = function(calculation){
2393    let decimals = calculation.decimals,
2394        price = formsApp.renderPrice(String(calculation.total), calculation.thousand, calculation.separator, decimals),
2395        total = calculation.total,
2396        tax = 0,
2397        discount = calculation.promo ? calculation.promo.discount * 1 : 0;
2398    if (calculation.subtotal) {
2399        calculation.subtotal.textContent = price;
2400    }
2401    if (calculation.promo && calculation.promo.unit == '%') {
2402        discount = total * discount / 100;
2403    }
2404    total -= discount;
2405    if (calculation.taxResult) {
2406        tax = total * calculation.tax / 100
2407        price = formsApp.renderPrice(String(tax), calculation.thousand, calculation.separator, decimals)
2408        calculation.taxResult.textContent = price;
2409    }
2410    total += tax;
2411    if (calculation.discount) {
2412        price = formsApp.renderPrice(String(discount), calculation.thousand, calculation.separator, decimals);
2413        calculation.discount.textContent = '-'+price;
2414    }
2415    if (calculation.shipping) {
2416        calculation.shipping.forEach(function(input){
2417            if (input.checked) {
2418                total += input.price * 1;
2419            }
2420        });
2421    }
2422    calculation.resultTotal = total;
2423    price = formsApp.renderPrice(String(total), calculation.thousand, calculation.separator, decimals)
2424    calculation.result.textContent = price;
2425    $f(calculation.input).trigger('change');
2426}
2427
2428formsApp.getProductCartHTML = function(product, name, ind, calculation){
2429    let div = document.createElement('div'),
2430        decimals = calculation.decimals,
2431        price = formsApp.renderPrice(String(product.total), calculation.thousand, calculation.separator, decimals),
2432        image = product.input.closest('.checkbox-image-wrapper');
2433    div.className = 'ba-form-product-row';
2434    div.dataset.name = name;
2435    div.dataset.ind = ind;
2436    div.innerHTML = '<div class="ba-form-product-title-cell">'+product.title+'</div>'+
2437        '<div class="ba-form-product-quantity-cell"><i class="ba-form-icons ba-icon-minus-circle '+
2438        (product.quantity == 1 ? 'ba-disabled' : '')+'" data-action="-"></i>'+
2439        '<input type="number" value="'+product.quantity+'" min="1" step="1">'+
2440        '<i class="ba-form-icons ba-icon-plus-circle" data-action="+"></i></div>'+
2441        '<div class="ba-form-product-total-cell"><div class="ba-form-calculation-price-wrapper">'+
2442        '<span class="field-price-currency">'+calculation.symbol+'</span><span class="field-price-value">'+price+
2443        '</span></div>'+
2444        '</div><div class="ba-form-product-remove-cell"><i class="ba-form-icons ba-icon-delete"></i></div>';
2445    if (image) {
2446        let img = document.createElement('div');
2447        img.className = 'ba-form-product-image-cell';
2448        img.append(image.querySelector('img').cloneNode());
2449        div.insertBefore(img, div.querySelector('.ba-form-product-title-cell'));
2450    }
2451    let input = div.querySelector('.ba-form-product-quantity-cell input'),
2452        trash = div.querySelector('.ba-form-product-remove-cell i'),
2453        quantityBtns = div.querySelectorAll('.ba-form-product-quantity-cell i[data-action]');
2454    quantityBtns.forEach(function(element){
2455        element.addEventListener('click', function(){
2456            if (this.classList.contains('ba-disabled')) {
2457                return false;
2458            }
2459            if (this.dataset.action == '+') {
2460                input.value = input.value * 1 + 1;
2461            } else {
2462                input.value = input.value * 1 - 1;
2463            }
2464            let event = new Event('input');
2465            input.dispatchEvent(event);
2466        });
2467    });
2468    input.addEventListener('input', function(){
2469        if (this.value != '') {
2470            quantityBtns[0].classList[this.value == 1 ? 'add' : 'remove']('ba-disabled');
2471            let match = this.value.match(/\d+/),
2472                decimals = calculation.decimals;
2473            if (!match || match[0] == 0) {
2474                this.value = 1;
2475            } else if (match[0] != this.value) {
2476                this.value = match[0];
2477            }
2478            if (this.value * 1 > 0) {
2479                calculation.total -= product.total;
2480                product.quantity = this.value * 1;
2481                product.total = product.price * product.quantity;
2482                price = formsApp.renderPrice(String(product.total), calculation.thousand, calculation.separator, decimals);
2483                div.querySelector('.field-price-value').textContent = price;
2484                calculation.total += product.total;
2485                formsApp.calculateCartTotal(calculation);
2486            }
2487        }
2488    });
2489    trash.addEventListener('click', function(){
2490        if (product.input.type == 'hidden') {
2491            calculation.total -= product.total;
2492            delete calculation.products[name][ind];
2493            div.remove();
2494            let decimals = calculation.decimals;
2495            price = formsApp.renderPrice(String(calculation.total), calculation.thousand, calculation.separator, decimals);
2496            calculation.result.textContent = price;
2497        } else {
2498            product.input[product.input.localName == 'option' ? 'selected' : 'checked'] = false;
2499            $f(product.input).trigger('change');
2500        }
2501    });
2502
2503    return div;
2504}
2505
2506formsApp.decimalAdjust = function(type, value, exp){
2507    if (typeof exp === 'undefined' || +exp === 0) {
2508        return Math[type](value);
2509    }
2510    value = +value;
2511    exp = +exp;
2512    if (isNaN(value) || !(typeof exp === 'number' && exp % 1 === 0)) {
2513        return NaN;
2514    }
2515    value = value.toString().split('e');
2516    value = Math[type](+(value[0] + 'e' + (value[1] ? (+value[1] - exp) : -exp)));
2517    value = value.toString().split('e');
2518
2519    return +(value[0] + 'e' + (value[1] ? (+value[1] + exp) : exp));
2520}
2521
2522formsApp.calculation = function($this, fromCart){
2523    formsApp.calculated = {};
2524    for (let i in $this.calculation) {
2525        if (fromCart && $this.calculation[i].product) {
2526            continue;
2527        }
2528        let calc = $this.calculation[i],
2529            total = formsApp.calculate(calc.formula, $this),
2530            price = String(total);
2531        total = formsApp.decimalAdjust('round', total, calc.decimals * -1);
2532        price = String(total);
2533        calc.input.price = total;
2534        formsApp.calculated[i] = total;
2535        if (total != calc.input.value * 1) {
2536            price = formsApp.renderPrice(price, calc.thousand, calc.separator, calc.decimals);
2537            calc.input.value = total;
2538            calc.result.textContent = price;
2539            $f(calc.input).trigger('change');
2540        }
2541    }
2542}
2543
2544formsApp.calculate = function(formula, $this){
2545    if (formula == '') {
2546        return 0;
2547    }
2548    formula = formula.replace(/\s/g, '').replace(/elseif/g, 'else if');
2549    let matches = formula.match(/\[FieldID=\d+\]/g),
2550        total = 0;
2551    if (matches) {
2552        for (let i = 0; i < matches.length; i++) {
2553            let match = matches[i].match(/\d+/g),
2554                price = $this.fields['baform-'+match[0]] ? $this.fields['baform-'+match[0]].price : 0;
2555            if (!$this.items['baform-'+match[0]] || $this.items['baform-'+match[0]].closest('.hidden-condition-field')) {
2556                price = 0;
2557            } else  if ('baform-'+match[0] in formsApp.calculated) {
2558                price = formsApp.calculated['baform-'+match[0]];
2559            } else if ($this.calculation['baform-'+match[0]]) {
2560                price = formsApp.calculate($this.calculation['baform-'+match[0]].formula, $this)
2561                formsApp.calculated['baform-'+match[0]] = price;
2562            }
2563            formula = formula.replace(matches[i], price);
2564        }
2565    }
2566    try {
2567        total = eval(formula);
2568    } catch (error) {
2569        console.error(error)
2570    }
2571    if (total == -Infinity || total == Infinity || Number.isNaN(total)) {
2572        total = 0;
2573    }
2574
2575    return total;
2576}
2577
2578formsApp.getProgress = function($this){
2579    if (formsApp.storage[$this.formURL] && formsApp.storage[$this.formURL][$this.formId]) {
2580        let data = formsApp.storage[$this.formURL][$this.formId];
2581        for (let i = 0; i < data.fields.length; i++) {
2582            let input = $this.form.find('[name="'+data.fields[i].name+'"]'),
2583                obj = data.fields[i];
2584            if (!input.length) {
2585                continue;
2586            }
2587            switch (obj.type) {
2588                case 'input' :
2589                case 'address':
2590                    if (input[0].formsInputMask) {
2591                        input[0].setSelectionRange(0, 0);
2592                        input[0].setRangeText(obj.data.value);
2593                    } else {
2594                        input.val(obj.data.value);
2595                    }
2596                    input.trigger('input');
2597                    if ('confirm' in obj.data) {
2598                        input.closest('.ba-form-field-item').find('.confirm-email-wrapper input').val(obj.data.confirm);
2599                    }
2600                    if ('password' in obj.data) {
2601                        input.closest('.ba-form-field-item').find('.confirm-password-wrapper input').val(obj.data.password);
2602                    }
2603                    break;
2604                case 'phone':
2605                    let parent = input.closest('.ba-field-container');
2606                    parent.find('.ba-phone-country-item[data-flag="'+obj.data.flag+'"]').trigger('click');
2607                    parent.find('.ba-phone-number-input').each(function(){
2608                        this.setSelectionRange(0, 0);
2609                        this.setRangeText(obj.data.value);
2610                    }).trigger('input');
2611                    break;
2612                case 'calendar':
2613                    let value = obj.data.value.split(' - ');
2614                    input.closest('.ba-field-container').find('input[type="text"]').each(function(i){
2615                        this.value = value[i];
2616                    }).trigger('input');
2617                    break;
2618                case 'radio':
2619                case 'checkbox':
2620                case 'rating':
2621                    input.each(function(){
2622                        if (obj.data.checked.indexOf(this.value) != -1) {
2623                            this.checked = true;
2624                            $f(this).trigger('change');
2625                        }
2626                    });
2627                    break;
2628                case 'select':
2629                case 'selectMultiple':
2630                    input.find('option').each(function(){
2631                        if (obj.data.selected.indexOf(this.value) != -1) {
2632                            this.selected = true;
2633                            input.trigger('change');
2634                        }
2635                    });
2636                    break;
2637                case 'acceptance':
2638                    if (obj.data.selected) {
2639                        input.prop('checked', obj.data.selected);
2640                    }
2641                    break;
2642                case 'slider':
2643                    let range = input.closest('.ba-field-container').find('input[type="range"]');
2644                    input.value = obj.data.value;
2645                    if (obj.data.type == 'range') {
2646                        range.val(obj.data.value).trigger('input');
2647                    } else {
2648                        let array = obj.data.value.split(' ');
2649                        range.first().val(array[0]).trigger('input');
2650                        range.last().val(array[1]).trigger('input');
2651                    }
2652                    break;
2653                case 'upload':
2654                    let file = input.closest('.upload-file-input').find('.ba-forms-attachment')[0];
2655                    file.uploads = obj.data.uploads;
2656                    for (let ind in file.uploads) {
2657                        if (ind == 'count') {
2658                            continue;
2659                        }
2660                        let name = file.uploads[ind].name.split('.'),
2661                            ext = name[name.length - 1].toLowerCase(),
2662                            type = file.options.images.indexOf(ext) == -1 ? 'file' : 'image',
2663                            attachment = formsApp.getAttachmentHTML(file, type, file.uploads[ind].name);
2664                        attachment.dataset.id = ind;
2665                        attachment.classList.add('forms-attachment-file-uploaded');
2666                        if (type == 'image') {
2667                            let img = JUri+uploads_storage+'/form-'+$this.formId+'/'+file.uploads[ind].filename;
2668                            attachment.querySelector('.attachment-intro-image').style.backgroundImage = 'url('+img+')';
2669                        }
2670                        file.options[type+'Container'].appendChild(attachment);
2671                    }
2672                    break;
2673            }
2674        }
2675    }
2676}
2677
2678formsApp.updateFieldsDefaultValues = function($this, defaultValues){
2679    let object = {};
2680    for (let ind in defaultValues) {
2681        object[ind] = '';
2682        object[ind] = formsApp.updateFieldsDefaultValue($this, defaultValues, defaultValues[ind].value, object);
2683        defaultValues[ind].item.value = object[ind];
2684    }
2685}
2686
2687formsApp.updateFieldsDefaultValue = function($this, defaultValues, value, object){
2688    let matches = value.match(/\[Field ID=\d+\]/g),
2689        title = $this.querySelector('input[name="page-title"]').value,
2690        url = $this.querySelector('input[name="page-url"]').value;
2691    if (matches) {
2692        for (let i = 0; i < matches.length; i++) {
2693            let match = matches[i].match(/\d+/g),
2694                key = 'baform-'+match[0],
2695                val = $this.fields[key].value;
2696            if (key in object) {
2697                val = object[match[0]];
2698            } else if (key in defaultValues) {
2699                object[key] = '';
2700                val = formsApp.updateFieldsDefaultValue($this, defaultValues, defaultValues[key].value, object);
2701                object[key] = val;
2702            }
2703        }
2704    }
2705    value = value.replace(/\[Page Title\]/g, title);
2706    value = value.replace(/\[Page URL\]/g, url);
2707    
2708
2709    return value;
2710}
2711
2712formsApp.updateFieldsValues = function(fields, $this, id){
2713    if ($this.type == 'radio' && $this.checked) {
2714        fields[id] = {
2715            value: $this.value,
2716            price: $this.price * 1
2717        }
2718    } else if ($this.type == 'checkbox' && $this.checked) {
2719        fields[id].value += fields[id].value != '' ? ' '+$this.value : $this.value;
2720        fields[id].price += $this.price * 1;
2721    } else if ($this.localName == 'select') {
2722        $f($this).find('option').each(function(){
2723            if (this.selected) {
2724                fields[id].value += fields[id].value != '' ? ' '+this.value : this.value;
2725                fields[id].price += this.price * 1;
2726            }
2727        });
2728    } else if ($this.dataset.calendar) {
2729        fields[id].value = $this.value;
2730        fields[id].price = $this.price * 1;
2731    } else if ($this.type == 'hidden' || $this.type == 'text' || $this.type == 'number'
2732        || $this.localName == 'textarea' || $this.type == 'email') {
2733        fields[id].value = $this.value;
2734        fields[id].price = $this.value.replace(/,/g, '.') * 1;
2735    } else if ($this.type == 'file') {
2736        fields[id].value = '';
2737        fields[id].price = $this.uploads.count;
2738        $this.closest('.ba-form-upload-field').classList[$this.uploads.count > 0 ? 'add' : 'remove']('has-uploaded-file');
2739    }
2740    if (String(fields[id].price) == 'NaN') {
2741        fields[id].price = 0;
2742    }
2743}
2744
2745formsApp.checkConditionLogic = function($this){
2746    $this.pages = {};
2747    $this.querySelectorAll('.hidden-condition-field').forEach((div) => {
2748        div.classList.remove('hidden-condition-field');
2749    });
2750    conditionLogic[$this.formId].forEach(function(el){
2751        if (!el.publish) {
2752            return true;
2753        }
2754        let flags = new Array(),
2755            page = null,
2756            value;
2757        el.when.forEach(function(obj){
2758            if (!obj.field || !obj.state) {
2759                return true;
2760            }
2761            if (!$this.fields['baform-'+obj.field]) {
2762                return true;
2763            }
2764            page = $this.items['baform-'+obj.field].closest('.ba-form-page');
2765            if ($this.items['baform-'+obj.field].classList.contains('hidden-condition-field')
2766                || $this.items['baform-'+obj.field].closest('.hidden-condition-field')) {
2767                flags.push(false);
2768                return true;
2769            }
2770            value = $this.fields['baform-'+obj.field].value;
2771            if ($this.items['baform-'+obj.field].dataset.type == 'checkbox'
2772                || $this.items['baform-'+obj.field].dataset.type == 'selectMultiple') {
2773                value = [];
2774                $this.items['baform-'+obj.field].querySelectorAll('input[name], option').forEach(function(input){
2775                    input.checked || input.selected ? value.push(input.value) : '';
2776                });
2777            }
2778            switch (obj.state) {
2779                case 'equal':
2780                    if (typeof value == 'string') {
2781                        flags.push(value == obj.value);
2782                    } else {
2783                        flags.push(value.indexOf(obj.value) != -1);
2784                    }
2785                    break;
2786                case 'not-equal':
2787                    if (typeof value == 'string') {
2788                        flags.push(value != obj.value);
2789                    } else {
2790                        flags.push(value.indexOf(obj.value) == -1);
2791                    }
2792                    break;
2793                case 'not-empty':
2794                    if (typeof value == 'string') {
2795                        flags.push(value != '');
2796                    } else {
2797                        flags.push(value.length != 0);
2798                    }
2799                    break;
2800                case 'empty':
2801                    if (typeof value == 'string') {
2802                        flags.push(value == '');
2803                    } else {
2804                        flags.push(value.length == 0);
2805                    }
2806                    break;
2807                case 'greater':
2808                    flags.push((!isNaN(value) ? value * 1 : value) > (!isNaN(obj.value) ? obj.value * 1 : obj.value));
2809                    break;
2810                case 'less':
2811                    flags.push((!isNaN(value) ? value * 1 : value) < (!isNaN(obj.value) ? obj.value * 1 : obj.value));
2812                    break;
2813                case 'contain':
2814                    flags.push(value.indexOf(obj.value) != -1);
2815                    break;
2816                case 'not-contain':
2817                    flags.push(value.indexOf(obj.value) == -1);
2818                    break;
2819            }
2820        });
2821        if (!flags.length) {
2822            return true;
2823        }
2824        let method = el.operation == 'OR' ? 'some' : 'every',
2825            flag = flags[method](function(state){return state});
2826        el.do.forEach(function(obj){
2827            if (obj.field && obj.action) {
2828                let item = $this.items['baform-'+obj.field]
2829                    || $this.form[0].querySelector('.ba-form-page[data-page-key="'+obj.field+'"]');
2830                if (!item) {
2831                    return true;
2832                }
2833                if (obj.action == 'show' || obj.action == 'hide') {
2834                    let method = (obj.action == 'show' && flag) || (obj.action == 'hide' && !flag) ? 'remove' : 'add';
2835                    item.classList[method]('hidden-condition-field');
2836                    item.querySelectorAll('input[name], select[name], textarea[name]').forEach(function(itm){
2837                        if (itm.product) {
2838                            formsApp.productChange($this, itm.name);
2839                        }
2840                    });
2841                    if (item.classList.contains('ba-form-submit-field') && method == 'remove') {
2842                        formsApp.initFormsRecaptcha(item);
2843                    }
2844                } else if (obj.action == 'move' && flag) {
2845                    $this.pages[page.dataset.pageKey] = {
2846                        prev: page,
2847                        next: item
2848                    }
2849                    let hidden = [];
2850                    $f(page).nextAll('.ba-form-page').each(function(){
2851                        if (this == item) {
2852                            this.classList.remove('hidden-condition-field');
2853                            return false;
2854                        }
2855                        this.classList.add('hidden-condition-field');
2856                        hidden.push(this.dataset.pageKey);
2857                    });
2858                    $this.pages[page.dataset.pageKey].hidden = hidden;
2859                }
2860            }
2861        });
2862        $f(window).trigger('scroll');
2863    });
2864}
2865
2866formsApp.strrev = function(string){
2867    var ret = '', i = 0;
2868    for (i = string.length - 1; i >= 0; i--) {
2869        ret += string[i];
2870    }
2871
2872    return ret;
2873}
2874
2875formsApp.renderPrice = function(value, thousand, separator, decimals){
2876    let delta = value < 0 ? '-' : '',
2877        priceArray = value.replace('-', '').trim().split('.'),
2878        priceThousand = priceArray[0],
2879        priceDecimal = priceArray[1] ? priceArray[1] : '',
2880        price = '';
2881    if (priceThousand.length > 3 && thousand != '') {
2882        for (let i = 0; i < priceThousand.length; i++) {
2883            if (i % 3 == 0 && i != 0) {
2884                price += thousand;
2885            }
2886            price += priceThousand[priceThousand.length - 1 - i];
2887        }
2888        price = formsApp.strrev(price);
2889    } else {
2890        price += priceThousand;
2891    }
2892    if (decimals != 0) {
2893        price += separator;
2894        for (let i = 0; i < decimals; i++) {
2895            price += priceDecimal[i] ? priceDecimal[i] : '0';
2896        }
2897    }
2898
2899    return delta+price;
2900}
2901
2902
2903
2904function formsCaptchaOnload(service, captcha)
2905{
2906    formsApp[service].captcha = captcha;
2907    formsApp[service].loaded = true;
2908    formsApp[service].init();
2909}
2910
2911function formsRecaptchaOnload()
2912{
2913    formsCaptchaOnload('grecaptcha', grecaptcha);
2914}
2915
2916function formshCaptchaOnload()
2917{
2918    formsCaptchaOnload('hcaptcha', hcaptcha);
2919}
2920
2921function formsTurnstileOnload()
2922{
2923    formsCaptchaOnload('turnstile', turnstile);
2924}
2925
2926
2927formsApp.captcha = {
2928    configurate: function(settings) {
2929        this.settings = settings;
2930    },
2931    get: function(key) {
2932        return formsApp.recaptcha[this.type][key];
2933    },
2934    init: function(){
2935        $f('.ba-form-submit-field').each(function(){
2936            formsApp.initFormsRecaptcha(this);
2937        });
2938    },
2939    load: function() {
2940        if (this.loaded) {
2941            this.init();
2942        } else if (!this.loaded && !this.loading) {
2943            let script = document.createElement('script');
2944            this.loading = true;
2945            script.src = this.src;
2946            document.head.appendChild(script);
2947        }
2948    },
2949    set: function(element) {
2950        let div = document.createElement('div'),
2951            parent = $f(element).find('.ba-form-submit-recaptcha-wrapper');
2952        div.id = 'forms-recaptcha-'+(+new Date());
2953        div.className = 'forms-recaptcha';
2954        div.dataset.captcha = this.type;
2955        parent.append(div);
2956        this.render(div);
2957        element.rendered = true;
2958    }
2959}
2960
2961formsApp.turnstile = {
2962    src: 'https://challenges.cloudflare.com/turnstile/v0/api.js?onload=formsTurnstileOnload',
2963    type: 'turnstile',
2964    __proto__: formsApp.captcha,
2965    render: function(div){
2966        formsApp.recaptcha.data[div.id] = this.captcha.render(div, {
2967            sitekey: this.get('site_key'),
2968            theme: this.get('theme'),
2969            size: this.get('size'),
2970            callback: formsVerifyCaptcha
2971        });
2972    }
2973}
2974
2975formsApp.hcaptcha = {
2976    src: 'https://js.hcaptcha.com/1/api.js?onload=formshCaptchaOnload',
2977    type: 'hcaptcha',
2978    __proto__: formsApp.captcha,
2979    render: function(div){
2980        div.dataset.size = this.get('invisible') ? 'invisible' : 'normal';
2981        formsApp.recaptcha.data[div.id] = this.captcha.render(div, {
2982            sitekey: this.get('site_key'),
2983            theme: this.get('theme'),
2984            size: this.get('invisible') ? 'invisible' : 'normal',
2985            callback: formsVerifyCaptcha
2986        });
2987    }
2988}
2989
2990formsApp.grecaptcha = {
2991    init: () => {
2992        $f('.ba-form-submit-field').each(function(){
2993            formsApp.initFormsRecaptcha(this);
2994        });
2995    },
2996    load: () => {
2997        if ('grecaptcha' in window) {
2998            formsRecaptchaOnload()
2999        } else if (!formsApp.grecaptcha.loaded && !formsApp.grecaptcha.loading) {
3000            formsApp.grecaptcha.loading = true;
3001            let script = document.createElement('script');
3002            script.src = 'https://www.google.com/recaptcha/api.js?onload=formsRecaptchaOnload&render=explicit';
3003            script.onload = () => {
3004                formsApp.grecaptcha.loaded = true;
3005            }
3006            document.head.appendChild(script);
3007        }
3008    }
3009}
3010
3011formsApp.loadRecaptcha = function(){
3012    $f('.ba-form-submit-field').each(function(){
3013        let captcha = this.querySelector('.ba-form-submit-btn').dataset.captcha;
3014        if (captcha == 'hcaptcha' && formsApp.recaptcha[captcha]) {
3015            formsApp.hcaptcha.load();
3016        } else if (captcha == 'turnstile' && formsApp.recaptcha[captcha]) {
3017            formsApp.turnstile.load();
3018        } else if (captcha && formsApp.recaptcha[captcha]) {
3019            formsApp.grecaptcha.load();
3020        }
3021    });
3022}
3023
3024formsApp.getRecaptchaData = function(){
3025    fetch(JUri+'index.php?option=com_baforms&task=form.getRecaptchaData').then(function(response){
3026        return response.json();
3027    }).then(function(json){
3028        formsApp.recaptcha = json;
3029        formsApp.loadRecaptcha();
3030    });
3031}
3032
3033formsApp.initFormsRecaptcha = function(element){
3034    let captcha = element.querySelector('.ba-form-submit-btn').dataset.captcha;
3035    if (formsApp.recaptcha && captcha == 'hcaptcha' && formsApp.recaptcha[captcha] && !element.rendered) {
3036        formsApp.hcaptcha.set(element);
3037    } else if (formsApp.recaptcha && captcha == 'turnstile' && formsApp.recaptcha[captcha] && !element.rendered) {
3038        formsApp.turnstile.set(element);
3039    } else if (formsApp.recaptcha && captcha && formsApp.recaptcha[captcha] && ('grecaptcha' in window) && !element.rendered) {
3040        let div = document.createElement('div'),
3041            parent = element.querySelector('.ba-form-submit-recaptcha-wrapper'),
3042            options = {
3043                sitekey : formsApp.recaptcha[captcha].public_key
3044            };
3045        if (captcha == 'recaptcha') {
3046            options.theme = formsApp.recaptcha[captcha].theme;
3047            options.size = formsApp.recaptcha[captcha].size;
3048        } else {
3049            options.badge = formsApp.recaptcha[captcha].badge;
3050            options.size = 'invisible';
3051        }
3052        div.id = 'forms-recaptcha-'+(+new Date());
3053        div.className = 'forms-recaptcha';
3054        div.dataset.captcha = captcha;
3055        div.dataset.size = options.size;
3056        parent.append(div);
3057        options.callback = formsVerifyCaptcha
3058        formsApp.recaptcha.data[div.id] = grecaptcha.render(div, options);
3059        element.rendered = true;
3060    }
3061}
3062
3063function formsVerifyCaptcha(){
3064    $f('.forms-recaptcha').each(function(){
3065        formsApp.removeAlertTooltip(this);
3066    });
3067    if (formsApp.submitForm) {
3068        formsApp.submitData();
3069    }
3070}
3071
3072formsApp.executeInputMask = function(input, mask){
3073    let n = input.beforeData.start + Math.abs(input.beforeData.value.length - input.value.length),
3074        pos = mask.indexOf('_'),
3075        text = [],
3076        delData = [],
3077        start = updateStart = null,
3078        value = mask;
3079    if (input.value.length > input.beforeData.value.length || input.value.length == input.beforeData.value.length) {
3080        let i = 0;
3081        for (i; i < input.beforeData.start; i++) {
3082            if (mask[i] == '_' && /\d/.test(input.beforeData.value[i])) {
3083                text.push(input.beforeData.value[i]);
3084            }
3085        }
3086        i = input.beforeData.start;
3087        n = input.value.length > input.beforeData.value.length ? n : input.beforeData.end;
3088        for (i; i < n; i++) {
3089            if (/\d/.test(input.value[i])) {
3090                text.push(input.value[i]);
3091            }
3092        }
3093        start = pos;
3094        for (i = 0; i < text.length; i++) {
3095            start = mask.indexOf('_', start + 1);
3096            if (start == -1) {
3097                start = mask.length;
3098            }
3099        }
3100        for (i = input.beforeData.end; i < input.beforeData.value.length; i++) {
3101            if (mask[i] == '_' && /\d/.test(input.beforeData.value[i])) {
3102                text.push(input.beforeData.value[i]);
3103            }
3104        }
3105    } else if (input.beforeData.start == 0 && input.beforeData.end == mask.length) {
3106        for (let i = 0; i < input.value.length; i++) {
3107            if (/\d/.test(input.value[i])) {
3108                text.push(input.value[i]);
3109            }
3110        }
3111        updateStart = true;
3112    } else if (input.beforeData.start < input.selectionStart) {
3113        let i = 0;
3114        for (i; i < input.selectionStart; i++) {
3115            if (mask[i] == '_' && /\d/.test(input.value[i])) {
3116                text.push(input.value[i]);
3117            }
3118        }
3119        start = i;
3120        for (let j = input.beforeData.end; j < input.beforeData.value.length; j++) {
3121            if (mask[j] == '_' && /\d/.test(input.beforeData.value[j])) {
3122                text.push(input.beforeData.value[j]);
3123            }
3124        }
3125    } else {
3126        let i = input.selectionStart < input.beforeData.start ? input.selectionStart : input.beforeData.start;
3127        n = input.selectionStart < input.beforeData.start ? input.beforeData.start : n;
3128        for (i; i < n; i++) {
3129            delData.push(i);
3130        }
3131    }
3132    if (delData.length == 1 && mask[delData[0]] != '_' && input.selectionStart < input.beforeData.start) {
3133        for (let i = input.selectionStart; i >= 0; i--) {
3134            if (mask[i] == '_') {
3135                delData[0] = i;
3136                break;
3137            }
3138        }
3139    } else if (delData.length == 1 && mask[delData[0]] != '_') {
3140        for (let i = input.selectionStart; i < mask.length; i++) {
3141            if (mask[i] == '_') {
3142                delData[0] = i;
3143                break;
3144            }
3145        }
3146    }
3147    if (delData.length) {
3148        start = delData[0];
3149        for (let i = 0; i < input.beforeData.value.length; i++) {
3150            if (delData.indexOf(i) == -1 && mask[i] == '_') {
3151                text.push(input.beforeData.value[i]);
3152            }
3153        }
3154    }
3155    input.value = value;
3156    text.forEach(function(el){
3157        if (pos != -1) {
3158            input.setSelectionRange(pos, ++pos);
3159            input.setRangeText(el);
3160            if (updateStart) {
3161                start = pos;
3162            }
3163        }
3164        pos = input.value.indexOf('_');
3165    });
3166    if (start !== null) {
3167        input.setSelectionRange(start, start);
3168    }
3169}
3170
3171formsApp.prepareInputMask = function(input, mask, event){
3172    let value = input.value ? input.value : mask;
3173    if (event) {
3174        input.beforeData = {
3175            start: input.selectionStart,
3176            end: input.selectionEnd,
3177            value: value
3178        };
3179    } else {
3180        input.beforeData = {
3181            start: 0,
3182            end: 0,
3183            value: value
3184        };
3185        input.value = value;
3186    }
3187}
3188
3189formsApp.executeMask = function(input, mask){
3190    let i = 0,
3191        flag = true,
3192        value = '';
3193    for (i = 0; i < input.value.length; i++) {
3194        flag = input.value[i] == mask[i] || mask[i] == '#' && /\d/.test(input.value[i])
3195        if (flag) {
3196            value += input.value[i];
3197        } else {
3198            break;
3199        }
3200    }
3201    if (!flag) {
3202        let subValue = input.value.substr(i),
3203            match = subValue.match(/\d+/),
3204            ind = 0;
3205        for (i; i < mask.length; i++) {
3206            if (mask[i] != '#') {
3207                value += mask[i];
3208            } else if (match && match[0][ind]) {
3209                value += match[0][ind++];
3210            } else {
3211                break;
3212            }
3213        }
3214        input.value = value;
3215    }
3216}
3217
3218formsApp.sortPoll = function(field){
3219    let array = Array.apply(null, field.querySelectorAll('.ba-form-checkbox-wrapper')),
3220        last = null,
3221        count = field.style.getPropertyValue('--checkbox-field-count') * 1,
3222        index = 1;
3223    array.sort(function(a, b){
3224        if (a.style.getPropertyValue('--poll-order') * 1 > b.style.getPropertyValue('--poll-order') * 1) {
3225            return 1;
3226        } else if (a.style.getPropertyValue('--poll-order') * 1 < b.style.getPropertyValue('--poll-order') * 1) {
3227            return -1;
3228        } else {
3229            return 0;
3230        }
3231    });
3232    array.forEach(function(div){
3233        if (index == count) {
3234            div.classList.add('last-row-checkbox-wrapper');
3235            index = 0;
3236        } else {
3237            div.classList.remove('last-row-checkbox-wrapper');
3238        }
3239        index++;
3240    });
3241    last = array.pop();
3242    last ? last.classList.add('last-row-checkbox-wrapper') : '';
3243}
3244
3245document.addEventListener('DOMContentLoaded', formsApp.createForms);
3246if (document.readyState == 'complete') {
3247    formsApp.createForms();
3248}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.