PageSourceSearch

https://rx2go.ai/js/modal.js?ver=1.0

js rx2go.ai collected 2026-10-05 07:59:16 UTC 8,985 bytes, 278 lines download raw bytes

1(() => {
2    const KEY_ESCAPE = 'Escape';
3    const SUCCESS_MODAL_DELAY = 3000;
4    const STATUS = { ERROR: 0, SUCCESS: 1 };
5    const CAPTCHA_UNAVAILABLE_TEXT = 'Verification could not be loaded. Please reload the page and try again.';
6
7    const body = document.querySelector('body');
8    const openButtons = document.querySelectorAll('.js-gq-modal-form-open');
9    const overlay = document.querySelector('.js-gq-overlay');
10    const formModal = document.querySelector('.js-gq-modal-form');
11    const successModal = document.querySelector('.js-gq-modal-success');
12    const errorModal = document.querySelector('.js-gq-modal-error');
13
14    if (openButtons.length === 0 || !overlay || !formModal || !successModal || !errorModal) {
15        console.error("Don't exist modals or button open modal");
16        return;
17    }
18
19    const formCloseButton = formModal.querySelector('.js-gq-modal-form-close');
20    const errorTitle = errorModal.querySelector('.js-gq-modal-error-title');
21    const successCloseButton = successModal.querySelector('.js-gq-modal-success-close');
22
23    if (!formCloseButton || !errorTitle || !successCloseButton) {
24        console.error("Don't exist button close modal or title modal node");
25        return;
26    }
27
28    const form = formModal.querySelector('.js-gq-form');
29    const submitButton = formModal.querySelector('.js-gq-form-submit');
30    const numberInput = formModal.querySelector('.js-gq-num');
31    const rangeInput = formModal.querySelector('.js-gq-range');
32    const phoneInput = formModal.querySelector('.js-gq-phone');
33    const citiesBlock = formModal.querySelector('.js-gq-cities');
34    const daysBlock = formModal.querySelector('.js-gq-days');
35    const captchaBlock = formModal.querySelector('.js-gq-captcha');
36
37    if (!form || !submitButton || !numberInput || !rangeInput || !phoneInput || !citiesBlock || !daysBlock || !captchaBlock) {
38        console.error('Form of modal is invalid');
39        return;
40    }
41
42    if (typeof IMask === 'undefined') {
43        console.error("IMask lib don't exist");
44        return;
45    }
46
47    const formAction = form.action;
48    if (!formAction || formAction.length === 0) {
49        console.error('Form action is invalid');
50        return;
51    }
52
53    const phoneMask = IMask(phoneInput, {
54        mask: '+{1} (000) 000-0000',
55        prepare: (value) => value.replace(/\D/g, ''),
56        commit: (value, masked) => {
57            masked._value = value.replace(/\D/g, '');
58        },
59    });
60
61    // Called by reCAPTCHA (data-callback) once the checkbox is solved.
62    window.gqRecaptchaCallback = () => captchaBlock.classList.remove('error');
63
64    function isCaptchaReady() {
65        return typeof window.grecaptcha !== 'undefined' && typeof window.grecaptcha.getResponse === 'function';
66    }
67
68    function getCaptchaToken() {
69        if (!isCaptchaReady()) {
70            return '';
71        }
72
73        try {
74            return window.grecaptcha.getResponse() || '';
75        } catch (error) {
76            // Thrown while the widget has not been rendered yet.
77            return '';
78        }
79    }
80
81    function resetCaptcha() {
82        if (!isCaptchaReady()) {
83            return;
84        }
85
86        try {
87            window.grecaptcha.reset();
88        } catch (error) {
89            console.error(error);
90        }
91    }
92
93    function validateDeliveries() {
94        if (+form['daily-deliveries'].value === 0) {
95            daysBlock.classList.add('error');
96            return false;
97        }
98
99        daysBlock.classList.remove('error');
100        return true;
101    }
102
103    function validateRegions() {
104        const anyChecked = form['gq-region-cn'].checked
105            || form['gq-region-la'].checked
106            || form['gq-region-ma'].checked
107            || form['gq-region-nj'].checked
108            || form['gq-region-ny'].checked
109            || form['gq-region-pe'].checked;
110
111        citiesBlock.classList.toggle('error', !anyChecked);
112        return anyChecked;
113    }
114
115    function validateFields() {
116        return [form['gq-name'], form['gq-business-name'], form['gq-phone'], form['gq-email']]
117            .map((field) => {
118                const filled = field.value.length > 0;
119                field.classList.toggle('error', !filled);
120                return filled;
121            })
122            .every((filled) => filled);
123    }
124
125    function validateCaptcha() {
126        const passed = getCaptchaToken().length > 0;
127        captchaBlock.classList.toggle('error', !passed);
128        return passed;
129    }
130
131    async function sendQuote(quote) {
132        try {
133            const response = await fetch(formAction, {
134                method: 'POST',
135                headers: {
136                    Accept: 'application/json',
137                    'Content-Type': 'application/json',
138                },
139                body: JSON.stringify({
140                    quote,
141                    captchaToken: getCaptchaToken(),
142                    'gq-website': form['gq-website'] ? form['gq-website'].value : '',
143                }),
144            });
145
146            const data = await response.json();
147
148            closeModal(formModal);
149
150            if (data && data.status === STATUS.ERROR) {
151                showErrorModal(data.payload && data.payload.text);
152            }
153
154            if (data && data.status === STATUS.SUCCESS) {
155                openModal(successModal);
156                setTimeout(() => closeModal(successModal), SUCCESS_MODAL_DELAY);
157            }
158        } catch (error) {
159            console.error(error);
160            showErrorModal();
161        }
162    }
163
164    function openModal(modal) {
165        closeAllModals();
166        body.classList.add('scroll-lock');
167        overlay.classList.add('show');
168        modal.classList.add('show');
169        document.addEventListener('keydown', handleKeydown);
170    }
171
172    function closeModal(modal) {
173        form.reset();
174        overlay.classList.remove('show');
175        modal.classList.remove('show');
176        body.classList.remove('scroll-lock');
177        document.removeEventListener('keydown', handleKeydown);
178    }
179
180    function closeAllModals() {
181        [formModal, errorModal, successModal].forEach((modal) => closeModal(modal));
182    }
183
184    function showErrorModal(text) {
185        openModal(errorModal);
186        errorTitle.textContent = text || errorTitle.dataset.text;
187    }
188
189    function openFormModal() {
190        openModal(formModal);
191        captchaBlock.classList.remove('error');
192        resetCaptcha();
193    }
194
195    function handleKeydown(event) {
196        if (event.key === KEY_ESCAPE) {
197            closeAllModals();
198        }
199    }
200
201    openButtons.forEach((button) => button.addEventListener('click', openFormModal));
202
203    rangeInput.addEventListener('input', (event) => {
204        numberInput.value = event.target.value;
205    });
206
207    numberInput.addEventListener('input', (event) => {
208        const value = +event.target.value;
209
210        if (isNaN(value)) {
211            event.target.value = rangeInput.value;
212            return;
213        }
214
215        if (value < rangeInput.min) {
216            event.target.value = rangeInput.min;
217        }
218
219        if (value > rangeInput.max) {
220            event.target.value = rangeInput.max;
221        }
222
223        rangeInput.value = event.target.value;
224    });
225
226    form.addEventListener('submit', async (event) => {
227        event.preventDefault();
228
229        const deliveriesValid = validateDeliveries();
230        const regionsValid = validateRegions();
231        const fieldsValid = validateFields();
232
233        if (!deliveriesValid || !regionsValid || !fieldsValid) {
234            return;
235        }
236
237        if (!isCaptchaReady()) {
238            showErrorModal(CAPTCHA_UNAVAILABLE_TEXT);
239            return;
240        }
241
242        if (!validateCaptcha()) {
243            return;
244        }
245
246        submitButton.classList.add('loading');
247        submitButton.setAttribute('disabled', 'disabled');
248
249        await sendQuote({
250            dailyDeliveries: +form['daily-deliveries'].value,
251            regionCn: form['gq-region-cn'].checked,
252            regionNj: form['gq-region-nj'].checked,
253            regionLa: form['gq-region-la'].checked,
254            regionPe: form['gq-region-pe'].checked,
255            regionNy: form['gq-region-ny'].checked,
256            regionMa: form['gq-region-ma'].checked,
257            name: form['gq-name'].value,
258            businessName: form['gq-business-name'].value,
259            email: form['gq-email'].value,
260            phone: phoneMask.unmaskedValue.slice(1, 11),
261        });
262
263        // reCAPTCHA responses are single use, so a new one is needed for the next attempt.
264        resetCaptcha();
265
266        submitButton.classList.remove('loading');
267        submitButton.removeAttribute('disabled');
268    });
269
270    overlay.addEventListener('click', (event) => {
271        if (event.target === overlay) {
272            closeAllModals();
273        }
274    });
275
276    formCloseButton.addEventListener('click', () => closeModal(formModal));
277    successCloseButton.addEventListener('click', () => closeModal(successModal));
278})();

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.