PageSourceSearch

https://podshipnikru.com/local/templates/podshipnik/js/consult-widget.js?178913450313457

js podshipnikru.com collected 2026-10-02 17:50:16 UTC 13,457 bytes, 432 lines download raw bytes

1(function () {
2    if (window.consultWidgetInited) {
3        return;
4    }
5    window.consultWidgetInited = true;
6
7    function start($) {
8    var YM_ID = 40834834;
9    var MAX_FILES = 5;
10    var MAX_SIZE = 10 * 1024 * 1024;
11    var ALLOWED_EXT = ['doc', 'docx', 'pdf', 'xls', 'xlsx', 'png', 'jpg', 'jpeg'];
12    var PLACEHOLDERS = {
13        mail: '[email protected]',
14        phone: '+7 (___) ___-__-__',
15        max: 'Номер в MAX +7 (___) ___-__-__'
16    };
17
18    var FILE_NAME_MAX = 32;
19    var selectedFiles = [];
20    var contactMask = null;
21    var $widget;
22    var $overlay;
23    var $popup;
24    var $form;
25    var $contact;
26    var $channelInput;
27    var $fileInput;
28    var $fileStatus;
29    var $fileList;
30    var $submit;
31
32    function reachGoal(name) {
33        if (typeof ym === 'function') {
34            try {
35                ym(YM_ID, 'reachGoal', name);
36            } catch (e) {}
37        }
38    }
39
40    function phoneMask() {
41        if (window.arTires2Options && arTires2Options.THEME && arTires2Options.THEME.PHONE_MASK) {
42            return arTires2Options.THEME.PHONE_MASK;
43        }
44        return '+7 (999) 999-99-99';
45    }
46
47    function hasInputmask() {
48        return !!(window.jQuery && $.fn && $.fn.inputmask);
49    }
50
51    function destroyMask() {
52        if (contactMask && typeof contactMask.destroy === 'function') {
53            contactMask.destroy();
54            contactMask = null;
55        }
56        if ($contact && $contact.length && hasInputmask()) {
57            try {
58                $contact.inputmask('remove');
59            } catch (e) {}
60        }
61        if ($contact && $contact.length) {
62            $contact.off('input.consultPhoneMask');
63        }
64    }
65
66    function formatPhoneFallback(value) {
67        var digits = String(value || '').replace(/\D+/g, '');
68        if (digits.charAt(0) === '8') {
69            digits = '7' + digits.slice(1);
70        }
71        if (digits.charAt(0) !== '7') {
72            digits = '7' + digits;
73        }
74        digits = digits.slice(0, 11);
75        var rest = digits.slice(1);
76        var out = '+7';
77        if (rest.length) {
78            out += ' (' + rest.slice(0, 3);
79        }
80        if (rest.length >= 3) {
81            out += ')';
82        }
83        if (rest.length > 3) {
84            out += ' ' + rest.slice(3, 6);
85        }
86        if (rest.length > 6) {
87            out += '-' + rest.slice(6, 8);
88        }
89        if (rest.length > 8) {
90            out += '-' + rest.slice(8, 10);
91        }
92        return out;
93    }
94
95    function applyMask(channel) {
96        destroyMask();
97        if (!$contact || !$contact.length) {
98            return;
99        }
100        $contact.toggleClass('phone', channel === 'phone' || channel === 'max');
101        if (channel !== 'phone' && channel !== 'max') {
102            return;
103        }
104
105        var el = $contact.get(0);
106        if (window.IMask && el) {
107            contactMask = window.IMask(el, {
108                mask: '+{7} (000) 000-00-00',
109                lazy: false,
110                placeholderChar: '_'
111            });
112            return;
113        }
114
115        if (hasInputmask()) {
116            $contact.inputmask('mask', {
117                mask: phoneMask(),
118                showMaskOnHover: false,
119                showMaskOnFocus: true,
120                clearIncomplete: false
121            });
122            return;
123        }
124
125        $contact.on('input.consultPhoneMask', function () {
126            var formatted = formatPhoneFallback(this.value);
127            if (this.value !== formatted) {
128                this.value = formatted;
129            }
130        });
131    }
132
133    function setChannel(channel) {
134        var prev = $channelInput.val();
135        destroyMask();
136        $channelInput.val(channel);
137        $form.find('.consult-widget-form__tab').each(function () {
138            var active = $(this).data('channel') === channel;
139            $(this).toggleClass('is-active', active).attr('aria-selected', active ? 'true' : 'false');
140        });
141        $contact.attr({
142            type: channel === 'mail' ? 'email' : 'tel',
143            autocomplete: channel === 'mail' ? 'email' : 'tel',
144            inputmode: channel === 'mail' ? 'email' : 'tel',
145            placeholder: PLACEHOLDERS[channel] || PLACEHOLDERS.mail
146        });
147        if (prev === 'mail' || channel === 'mail') {
148            $contact.val('');
149        }
150        applyMask(channel);
151        clearError('contact');
152    }
153
154    function openPopup() {
155        $overlay.addClass('is-open').prop('hidden', false);
156        $popup.addClass('is-open').prop('hidden', false);
157        $widget.addClass('is-hidden');
158        $('body').addClass('consult-widget-lock');
159        setTimeout(function () {
160            $contact.trigger('focus');
161        }, 50);
162    }
163
164    function closePopup() {
165        $overlay.removeClass('is-open').prop('hidden', true);
166        $popup.removeClass('is-open is-success').prop('hidden', true);
167        $widget.removeClass('is-hidden');
168        $('body').removeClass('consult-widget-lock');
169        $submit.prop('disabled', false);
170    }
171
172    function clearError(name) {
173        $form.find('[data-field="' + name + '"]').removeClass('is-error');
174        $form.find('[data-error="' + name + '"]').text('');
175    }
176
177    function showError(name, message) {
178        var $field = $form.find('[data-field="' + name + '"]');
179        $field.addClass('is-error');
180        $field.find('[data-error="' + name + '"]').text(message);
181    }
182
183    function digitsPhone(value) {
184        var digits = String(value || '').replace(/\D+/g, '');
185        if (digits.charAt(0) === '8') {
186            digits = '7' + digits.slice(1);
187        }
188        if (digits.charAt(0) !== '7') {
189            digits = '7' + digits;
190        }
191        return digits;
192    }
193
194    function isValidEmail(value) {
195        return /^[^\s@]+@[^\s@]+\.[a-zA-Z]{2,}$/.test(String(value || '').trim());
196    }
197
198    function isValidPhone(value) {
199        return digitsPhone(value).length === 11;
200    }
201
202    function fileExt(name) {
203        var parts = String(name || '').toLowerCase().split('.');
204        return parts.length > 1 ? parts.pop() : '';
205    }
206
207    function shortFileName(name) {
208        var full = String(name || '');
209        if (full.length <= FILE_NAME_MAX) {
210            return full;
211        }
212        var ext = '';
213        var dot = full.lastIndexOf('.');
214        if (dot > 0 && full.length - dot <= 8) {
215            ext = full.slice(dot);
216            full = full.slice(0, dot);
217        }
218        var keep = Math.max(10, FILE_NAME_MAX - ext.length - 1);
219        return full.slice(0, keep) + '…' + ext;
220    }
221
222    function unwrapFileInput() {
223        if (!$fileInput || !$fileInput.length) {
224            return;
225        }
226        var $uploader = $fileInput.closest('.uploader');
227        if ($uploader.length) {
228            $uploader.after($fileInput);
229            $uploader.remove();
230        }
231        $fileInput.attr({ hidden: true, tabindex: -1, 'aria-hidden': 'true' });
232    }
233
234    function renderFiles() {
235        if (!selectedFiles.length) {
236            $fileList.prop('hidden', true).empty();
237            $fileStatus.prop('hidden', false).text('Файл не выбран');
238            return;
239        }
240        $fileStatus.prop('hidden', true);
241        $fileList.prop('hidden', false).empty();
242        selectedFiles.forEach(function (file, index) {
243            var $item = $('<div class="consult-widget-form__file-item"/>');
244            $item.append(
245                $('<span/>').text(shortFileName(file.name)).attr('title', file.name)
246            );
247            $item.append(
248                $('<button type="button" class="consult-widget-form__file-remove" aria-label="Удалить файл" title="Удалить">&times;</button>')
249                    .on('click', function () {
250                        selectedFiles.splice(index, 1);
251                        renderFiles();
252                    })
253            );
254            $fileList.append($item);
255        });
256    }
257
258    function addFiles(fileList) {
259        var files = Array.prototype.slice.call(fileList || []);
260        files.forEach(function (file) {
261            if (selectedFiles.length >= MAX_FILES) {
262                return;
263            }
264            var ext = fileExt(file.name);
265            if (ALLOWED_EXT.indexOf(ext) === -1) {
266                showError('contact', '');
267                alert('Допустимые форматы: ' + ALLOWED_EXT.join(', '));
268                return;
269            }
270            if (file.size > MAX_SIZE) {
271                alert('Файл «' + file.name + '» больше 10 МБ');
272                return;
273            }
274            selectedFiles.push(file);
275        });
276        renderFiles();
277        $fileInput.val('');
278    }
279
280    function validate() {
281        var ok = true;
282        var channel = $channelInput.val();
283        var value = $.trim($contact.val());
284        clearError('contact');
285        clearError('agree');
286
287        if (!value) {
288            showError('contact', channel === 'mail' ? 'Укажите e-mail' : 'Укажите телефон');
289            ok = false;
290        } else if (channel === 'mail' && !isValidEmail(value)) {
291            showError('contact', 'Укажите корректный e-mail');
292            ok = false;
293        } else if ((channel === 'phone' || channel === 'max') && !isValidPhone(value)) {
294            showError('contact', 'Укажите корректный телефон');
295            ok = false;
296        }
297
298        if (!$form.find('input[name="agree"]').prop('checked')) {
299            showError('agree', 'Подтвердите согласие');
300            ok = false;
301        }
302
303        return ok;
304    }
305
306    function submitForm(e) {
307        e.preventDefault();
308        if (!validate()) {
309            return;
310        }
311
312        var data = new FormData($form.get(0));
313        data.delete('files[]');
314        selectedFiles.forEach(function (file) {
315            data.append('files[]', file, file.name);
316        });
317
318        $submit.prop('disabled', true);
319
320        $.ajax({
321            url: $form.attr('action'),
322            method: 'POST',
323            data: data,
324            processData: false,
325            contentType: false,
326            dataType: 'json'
327        }).done(function (res) {
328            if (res && res.success) {
329                reachGoal('widget_form_send');
330                $popup.addClass('is-success');
331                $form.trigger('reset');
332                selectedFiles = [];
333                renderFiles();
334                setChannel('mail');
335                return;
336            }
337            if (res && res.errors) {
338                if (res.errors.contact) {
339                    showError('contact', res.errors.contact);
340                }
341                if (res.errors.agree) {
342                    showError('agree', res.errors.agree);
343                }
344                if (res.errors.form) {
345                    alert(res.errors.form);
346                }
347            } else {
348                alert('Не удалось отправить заявку. Попробуйте ещё раз.');
349            }
350            $submit.prop('disabled', false);
351        }).fail(function () {
352            alert('Не удалось отправить заявку. Попробуйте ещё раз.');
353            $submit.prop('disabled', false);
354        });
355    }
356
357    $(function () {
358        $widget = $('#consult-widget');
359        if (!$widget.length) {
360            return;
361        }
362
363        $overlay = $('#consult-widget-overlay');
364        $popup = $('#consult-widget-popup');
365        $form = $('#consult-widget-form');
366        $contact = $('#consult-widget-contact');
367        $channelInput = $form.find('input[name="channel"]');
368        $fileInput = $('#consult-widget-file-input');
369        $fileStatus = $('#consult-widget-file-status');
370        $fileList = $('#consult-widget-file-list');
371        $submit = $form.find('.consult-widget-form__submit');
372        unwrapFileInput();
373        setTimeout(unwrapFileInput, 50);
374        setTimeout(unwrapFileInput, 400);
375
376        $widget.on('click', function () {
377            reachGoal('widget_click');
378            openPopup();
379        });
380
381        $('#consult-widget-close').on('click', closePopup);
382        $overlay.on('click', closePopup);
383
384        $(document).on('keydown.consultWidget', function (e) {
385            if (e.key === 'Escape' && $popup.hasClass('is-open')) {
386                closePopup();
387            }
388        });
389
390        $form.on('click', '.consult-widget-form__tab', function () {
391            setChannel($(this).data('channel'));
392        });
393
394        $contact.on('input', function () {
395            clearError('contact');
396        });
397
398        $form.find('input[name="agree"]').on('change', function () {
399            clearError('agree');
400        });
401
402        $('#consult-widget-attach').on('click', function () {
403            $fileInput.trigger('click');
404        });
405
406        $fileInput.on('change', function () {
407            addFiles(this.files);
408        });
409
410        $form.on('submit', submitForm);
411        setChannel('mail');
412    });
413    }
414
415    function boot() {
416        if (window.jQuery) {
417            start(window.jQuery);
418            return;
419        }
420        if (boot.tries > 40) {
421            return;
422        }
423        boot.tries = (boot.tries || 0) + 1;
424        setTimeout(boot, 50);
425    }
426
427    if (document.readyState === 'loading') {
428        document.addEventListener('DOMContentLoaded', boot);
429    } else {
430        boot();
431    }
432})();

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.