PageSourceSearch

https://alp-itsm.ru/local/templates/itsm/js/custom.js?_v=1782304257

js alp-itsm.ru collected 2026-10-02 19:19:50 UTC 18,650 bytes, 378 lines download raw bytes

1$(document).ready(function() {
2    // --- Хелперы интеграции с Yandex SmartCaptcha (модуль skyweb24) ---
3    // Модуль skyweb24 рендерит виджет капчи один раз и не сбрасывает свой
4    // одноразовый токен. Оборачиваем smartCaptcha.render, чтобы запомнить id
5    // виджета для каждого контейнера и обновлять токен после каждой попытки отправки.
6    (function () {
7        function wrapRender() {
8            if (window.smartCaptcha && !window.smartCaptcha.__skyWrapped) {
9                var origRender = window.smartCaptcha.render;
10                window.smartCaptcha.render = function (container) {
11                    var id = origRender.apply(this, arguments);
12                    try {
13                        var el = (typeof container === 'string') ? document.getElementById(container) : container;
14                        if (el) el.setAttribute('data-sky-widget-id', id);
15                    } catch (e) {}
16                    return id;
17                };
18                window.smartCaptcha.__skyWrapped = true;
19                return true;
20            }
21            return false;
22        }
23        // smartCaptcha подгружается асинхронно через captcha.js - ждём его в цикле.
24        if (!wrapRender()) {
25            var timer = setInterval(function () { if (wrapRender()) clearInterval(timer); }, 50);
26            setTimeout(function () { clearInterval(timer); }, 15000);
27        }
28    })();
29
30    // Обновляет виджет SmartCaptcha внутри формы, чтобы следующая отправка получила
31    // свежий (одноразовый) токен. На формах без капчи ничего не делает.
32    function resetFormCaptcha(form) {
33        if (!window.smartCaptcha) return;
34        var container = form.querySelector('.captchaYandexClass');
35        if (!container) return;
36        var widgetId = container.getAttribute('data-sky-widget-id');
37        try {
38            if (widgetId !== null) {
39                window.smartCaptcha.reset(widgetId);
40            } else {
41                window.smartCaptcha.reset();
42            }
43        } catch (e) {}
44    }
45
46    $('.form').on('submit', function(e) {
47        e.preventDefault();
48        var form = $(this)[0];
49        // Защёлка от повторной отправки: пока запрос в полёте, новые сабмиты игнорируем
50        // (иначе второй уйдёт с уже использованным токеном капчи и сервер зависнет).
51        if (form.dataset.submitting === '1') { return false; }
52
53        if($(form).hasClass('form-kp-small-business')) {
54            $(form).find('[data-computers]').val($('#cslider-1').val());
55            $(form).find('[data-servers]').val($('#cslider-2').val());
56            $(form).find('[data-rate_economical]').val($('#cslider-1-price-1').html()+" ₽/мес");
57            $(form).find('[data-rate_partner]').val($('#cslider-1-price-2').html()+" ₽/мес");
58            $(form).find('[data-rate_unlimited]').val($('#cslider-1-price-3').html()+" ₽/мес");
59        }
60        if($(form).hasClass('form-kp-retail-business')) {
61            let shops = $('#cslider-3').val();
62            $(form).find('[data-shops]').val(shops);
63
64            t2 = Math.round(.75 * shops);
65            t1 = Math.round(1600 * t2);
66
67            $(form).find('[data-rate_monthly]').val(t1);
68            $(form).find('[data-departure_monthly]').val(t2.toString().replace(/\B(?=(\d{3})+(?!\d))/g, " ") + " ₽");
69        }
70
71        $(form).find('[data-url]').val(window.location.href);
72        // Валидируем на клиенте ДО отправки. Иначе невалидная первая попытка
73        // (например, не отмечены чекбоксы согласий) всё равно уходит на сервер и
74        // сжигает одноразовый токен SmartCaptcha, после чего форму нельзя отправить
75        // до перезагрузки страницы. getErrors() заодно показывает ошибки по полям.
76        var hasErrors = formValidate.getErrors(form) > 0;
77        // Капча (модуль skyweb24) должна быть пройдена. Если отправить с пустым
78        // smart-token, сервер виснет и кнопка застревает на "Идет отправка...".
79        var captchaBox = form.querySelector('.captchaYandexClass');
80        if (captchaBox) {
81            var holder = captchaBox.parentElement;
82            var smartToken = form.querySelector('input[name="smart-token"]');
83            if (!smartToken || !smartToken.value) {
84                if (holder && !holder.querySelector('.form__captcha-error')) {
85                    holder.insertAdjacentHTML('beforeend', '<div class="form__error form__c
85aptcha-error">Подтвердите, что вы не робот</div>');
86                }
87                hasErrors = true;
88            } else if (holder) {
89                var prevErr = holder.querySelector('.form__captcha-error');
90                if (prevErr) prevErr.remove();
91            }
92        }
93        if (hasErrors) {
94            return false;
95        }
96        let formData = new FormData(form);
97        if($(form).data("customresult") && $(form).data("customresult") !== "0") {
98            formData.append('customresult', $(form).data("customresult"));
99        }
100   
101        let ajaxUrl = 'ajax.php';
102        if(location.pathname.indexOf('/uslugi/') === 0) {
103            ajaxUrl = '/uslugi/ajax.php';
104        }
105        if(location.pathname.indexOf('/info/') === 0) {
106            ajaxUrl = '/info/ajax.php';
107        }
108        if(location.pathname.indexOf('/feedback/') === 0) {
109            ajaxUrl = '/feedback/ajax.php';
110        }
111        if(location.pathname.indexOf('/promo/') === 0) {
112            ajaxUrl = '/promo/ajax.php';
113        }
114
115        let buttonText = $(form).find('.form__submit button').text();
116        form.dataset.submitting = '1';
117        $(form).find('.form__submit button').prop('disabled', true);
118        $(form).find('.form__submit button').text('Идет отправка...');
119        $.ajax({
120            type: 'POST',
121            url: ajaxUrl,
122            dataType: 'text',
123            contentType: false,
124            processData: false,
125            data: formData,
126            // Таймаут, чтобы кнопка не зависла навсегда, если сервер не отвечает
127            // (например, токен капчи протух и проверка на сервере виснет).
128            timeout: 30000,
129            success: function (response) {
130                if(response.indexOf('success') >= 0){
131                    form.reset();
132                    $(form)
133                        .find('input[type=file]')
134                        .siblings('label')
135                        .html('Прикрепить файл');
136                    
137                    
138                    switch($(form).attr('id')) {
139                        case "callback":ym(23183956,'reachGoal','obratni_zvonok');break;
140                        case "offer":ym(23183956,'reachGoal','zaprosit_kp');break;
141                        case "consult":ym(23183956,'reachGoal','poluchit_konsultaciu');break;
142                        case "main-request-form":ym(23183956,'reachGoal','forma_na_glavnoi');break;
143                        case "feedback-form-footer":ym(23183956,'reachGoal','obratnaia_svyaz_niz_stranicy');break;
144                        case "65":ym(23183956,'reachGoal','polni_it_audit_niz_stranicy');break;
145                        case "subscribe":
146                            if(location.pathname.indexOf('/info/articles/optimizatsiya-it-infrastruktury-poluchite-chek-list-dlya-povysheniya-effektivnosti/') === 0) {
147                                ym(23183956,'reachGoal','send_chek_list');
148                            }
149                            break;
150                        default:
151                            ym(23183956,'reachGoal','poluchit_kp_niz_stranicy');
152                    }
153
154                    if($(form).hasClass('panel-multiform__form')) {
155                        $(form).removeClass('form-visible');
156                        let responseHtml = JSON.parse(response);
157                        $('.panel-multiform__result').html(responseHtml[1]);
158                        $('.panel-multiform__result').addClass('form-visible');
159                    } else {
160                        let resultDiv = $(form).parents('.section-request').find('.section-request__result');
161                        if(resultDiv) {
162                            $(form).parents('.section-request').find('.request').remove();
163                            let responseHtml = JSON.parse(response);
164                            $(resultDiv).html(responseHtml[1]);
165                        }
166                    }
167                } else {
168                    // Сервер отклонил отправку (например, капча/согласие). Токен
169                    // SmartCaptcha одноразовый и только что израсходован - обновляем
170                    // виджет перед следующей попыткой, иначе каждая повторная отправка
171                    // будет падать на проверке капчи.
172                    resetFormCaptcha(form);
173                    $(form).find('.form__submit button').prop('disabled', false);
174                    $(form).find('.form__submit button').text(buttonText);
175                    return false;
176                }
177            },
178            error: function (err) {
179                // Не оставляем кнопку навсегда в состоянии "Идет отправка...":
180                // разблокируем её и обновляем капчу, чтобы можно было повторить
181                // отправку без перезагрузки страницы.
182                resetFormCaptcha(form);
183                $(form).find('.form__submit button').prop('disabled', false);
184                $(form).find('.form__submit button').text(buttonText);
185            },
186            complete: function () {
187                // Снимаем защёлку в любом исходе (успех / ошибка / таймаут).
188                form.dataset.submitting = '';
189            }
190        });
191    });
192    
193
194    $('.set-form-service-name').click(function(){
195        $('input[data-service_name]').val('('+$(this).data('service_name')+')'); 
196    });
197
198    let formValidate = {
199        getErrors(form) {
200            let error = 0;
201            let formRequiredItems = form.querySelectorAll("*[data-required]");
202            if (formRequiredItems.length) formRequiredItems.forEach((formRequiredItem => {
203                if ((null !== formRequiredItem.offsetParent || "SELECT" === formRequiredItem.tagName) && !formRequiredItem.disabled) error += this.validateInput(formRequiredItem);
204            }));
205            return error;
206        },
207        validateInput(formRequiredItem) {
208            let error = 0;
209            if ("email" === formRequiredItem.dataset.required) {
210                formRequiredItem.value = formRequiredItem.value.replace(" ", "");
211                if (this.emailTest(formRequiredItem)) {
212                    this.addError(formRequiredItem);
213                    error++;
214                } else this.removeError(formRequiredItem);
215            } else if ("checkbox" === formRequiredItem.type && !formRequiredItem.checked) {
216                this.addError(formRequiredItem);
217                error++;
218            } else if (!formRequiredItem.value.trim()) {
219                this.addError(formRequiredItem);
220                error++;
221            } else this.removeError(formRequiredItem);
222            return error;
223        },
224        addError(formRequiredItem) {
225            formRequiredItem.classList.add("_form-error");
226            formRequiredItem.parentElement.classList.add("_form-error");
227            let inputError = formRequiredItem.parentElement.querySelector(".form__error");
228            if (inputError) formRequiredItem.parentElement.removeChild(inputError);
229            if (formRequiredItem.dataset.error) formRequiredItem.parentElement.insertAdjacentHTML("beforeend", `<div class="form__error">${formRequiredItem.dataset.error}</div>`);
230        },
231        removeError(formRequiredItem) {
232            formRequiredItem.classList.remove("_form-error");
233            formRequiredItem.parentElement.classList.remove("_form-error");
234            if (formRequiredItem.parentElement.querySelector(".form__error")) formRequiredItem.parentElement.removeChild(formRequiredItem.parentElement.querySelector(".form__error"));
235        },
236        formClean(form) {
237            form.reset();
238            setTimeout((() => {
239                let inputs = form.querySelectorAll("input,textarea");
240                for (let index = 0; index < inputs.length; index++) {
241                    const el = inputs[index];
242                    el.parentElement.classList.remove("_form-focus");
243                    el.classList.remove("_form-focus");
244                    formValidate.removeError(el);
245                }
246                let checkboxes = form.querySelectorAll(".checkbox__input");
247                if (checkboxes.length > 0) for (let index = 0; index < checkboxes.length; index++) {
248                    const checkbox = checkboxes[index];
249                    checkbox.checked = false;
250                }
251                if (flsModules.select) {
252                    let selects = form.querySelectorAll(".select");
253                    if (selects.length) for (let index = 0; index < selects.length; index++) {
254                        const select = selects[index].querySelector("select");
255                        flsModules.select.selectBuild(select);
256                    }
257                }
258            }), 0);
259        },
260        emailTest(formRequiredItem) {
261            return !/^\w+([\.-]?\w+)*@\w+([\.-]?\w+)*(\.\w{2,8})+$/.test(formRequiredItem.value);
262        }
263    };
264    function showMoreButton() {
265        $('.show-more-button').click( (e) => {
266
267            $.ajax({url: window.location.origin + window.location.pathname + '?AJAXLOAD=Y&PAGEN_1=' + ($(e.target).data('page') + 1)})
268                .done(function (response) {
269                    if (response) {
270                        $(e.target).parents('.show-more').replaceWith(response);
271                        showMoreButton();
272                    }
273                });
274        });
275    }
276    showMoreButton();
277
278    if($('.calc-to-multiform').length) {
279
280        $('.calc-to-multiform .tariffs__button').click( (e) => {
281            let parent = $(e.target).parents('.tariffs__item'),
282            name = $(parent).find('.tariffs__hdr-name').html(),
283            price = $(parent).find('.tariffs__hdr-price').data('num');
284            
285            let text = `Тариф ${name} ${price} ₽/мес
286Количество физических серверов Windows ${$('#cslider-server-1-count').val()}
287Количество виртуальных серверов Windows ${$('#cslider-server-2-count').val()}
288Количество физических серверов Unix/Linux ${$('#cslider-server-3-count').val()}
289Количество виртуальных серверов Unix/Linux ${$('#cslider-server-4-count').val()}`;
290
291            $('#offer textarea').html(text);
292        });
293    }
294
295    if($('.calc-to-multiform-static').length) {
296
297        $('.calc-to-multiform-static .tariffs__button').click( (e) => {
298            let parent = $(e.target).parents('.tariffs__item'),
299            name = $(parent).find('.tariffs__hdr-name').html()
300            .replace(/<br\s*\/?>/gi, "\n")
301            .replace(/<\/?[^>]+(>|$)/g, ""),
302            price = $(parent).find('.tariffs__hdr-price').html();
303            
304            let text = '';
305            if(name)
306                text+= `Получить информацию для "${name}"`;
307            if(price)
308                text+= ` ${price} ₽/мес`;
309            
310
311            $('#offer textarea').html(text);
312        });
313    }
314
315    if($('.section-calc').length || $('.section-cost-examples').length) {
316        $('.scroll-to-form').click( (e) => {
317            $([document.documentElement, document.body]).animate({
318                scrollTop: $('.'+$(e.target).data('form')).parents('.section-request').offset().top
319            }, 1000);
320        });
321    }
322
323    $('body').append('<div id="scroll-up-arrow"></div>');
324    $(window).scroll(function () {
325        if ($(this).scrollTop() > 150) {
326            $('#scroll-up-arrow').fadeIn();
327        } else {
328            $('#scroll-up-arrow').fadeOut();
329        }
330    });
331    if ($(window).scrollTop() > 150) {
332        $('#scroll-up-arrow').fadeIn();
333    } else {
334        $('#scroll-up-arrow').fadeOut();
335    }
336    $('#scroll-up-arrow').click(function () {
337        $('body,html').animate({
338            scrollTop: 0
339        }, 400);
340        return false;
341    });
342    $(document).on("click", ".call-multiform[data-formid='subscribe']", function () {
343        let formTitle = $(this).data("formtitle");
344        let customResult = $(this).data("customresult");
345        if(formTitle) {
346            $('#subscribe h2').text(formTitle);
347        } else {
348            $('#subscribe h2').text('Подписка на новости');
349        }
350        let form = $('#subscribe')[0];
351        if(customResult) {
352            $(form).attr("data-customresult", customResult);
353        } else {
354            $(form).attr("data-customresult", 0);
355        }
356        
357    });    
358});
359
360
361// Сохраняем UTM метки в cookie
362function setUTMCookies() {
363    const queryString = window.location.search;
364    const urlParams = new URLSearchParams(queryString);
365    const utmParams = {};
366
367    for (const [paramName, paramValue] of urlParams.entries()) {
368        if (paramName.startsWith('utm_')) {
369            // Устанавливаем на сутки
370            document.cookie = `${paramName}=${paramValue}; expires=${new Date(Date.now() + 1 * 24 * 3600 * 1000).toUTCString()}; path=/`;
371            utmParams[paramName] = paramValue;
372        }
373    }
374
375    return utmParams;
376}
377
378const utmParams = setUTMCookies();

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.