PageSourceSearch

https://fckrasnodar-ticket.com/local/templates/new_arena/js/smart_captcha.js

js fckrasnodar-ticket.com collected 2026-10-08 13:39:47 UTC 8,855 bytes, 239 lines download raw bytes

1/**
2 * Универсальный инициализатор Яндекс SmartCaptcha (невидимая) для форм
3 * Использование: добавьте атрибут data-captcha="true" к любой форме
4 *
5 * @version 3.0 (ленивый рендер: виджет создаётся при первом взаимодействии с формой)
6 *
7 * Почему лениво: smartCaptcha.render() сразу вставляет на страницу два iframe
8 * Яндекса, один из которых тянет React с yastatic.net.
9 */
10(function () {
11    'use strict';
12
13    const arCaptchaInstances = new Map();
14    const arFailedForms = new WeakSet();
15    let arPendingForms = [];
16    let nCheckInterval = null;
17
18    /**
19     * Инициализация капчи для формы
20     * @param {HTMLFormElement} obForm
21     */
22    function initCaptchaForForm(obForm) {
23        // Виджет для этой формы уже создан
24        if (obForm.captchaAPI) {
25            return;
26        }
27
28        if (arFailedForms.has(obForm)) {
29            return;
30        }
31
32        // Если API ещё не загружен, откладываем инициализацию
33        if (typeof window.smartCaptcha === 'undefined') {
34            // Проверяем, не добавлена ли форма уже
35            if (!arPendingForms.includes(obForm)) {
36                arPendingForms.push(obForm);
37            }
38            startApiWatcher();
39            return;
40        }
41
42        const sCaptchaId = obForm.id || 'form-' + Math.random().toString(36).substr(2, 9);
43
44        // Пропускаем, если уже инициализирована
45        if (arCaptchaInstances.has(sCaptchaId)) {
46            return;
47        }
48
49        const obCaptchaContainer = obForm.querySelector('.smart-captcha');
50
51        if (!obCaptchaContainer) {
52            console.warn('[Captcha] Контейнер .smart-captcha не найден в форме', obForm);
53            return;
54        }
55
56        let bCaptchaExecuted = false;
57        let sCurrentToken = '';
58        let nWidgetId = null;
59        let fnPendingSubmit = null;
60
61        // Создаём виджет капчи
62        try { nWidgetId = window.smartCaptcha.render(obCaptchaContainer, {
63            sitekey: obCaptchaContainer.getAttribute('data-sitekey'),
64            invisible: true,
65            shieldPosition: `bottom-left`,
66            hideShield: true,
67            callback: function (sToken) {
68                bCaptchaExecuted = true;
69                sCurrentToken = sToken;
70
71                // Добавляем скрытое поле с токеном
72                let obTokenInput = obForm.querySelector('input[name="smart-token"]');
73                if (!obTokenInput) {
74                    obTokenInput = document.createElement('input');
75                    obTokenInput.type = 'hidden';
76                    obTokenInput.name = 'smart-token';
77                    obForm.appendChild(obTokenInput);
78                }
79                obTokenInput.value = sToken;
80
81                // Генерируем событие
82                obForm.dispatchEvent(new CustomEvent('captchaSuccess', {
83                    detail: { token: sToken, formId: sCaptchaId }
84                }));
85
86                // Выполняем отложенную отправку, если есть
87                if (fnPendingSubmit) {
88                    fnPendingSubmit();
89                    fnPendingSubmit = null;
90                }
91            },
92            'expired-callback': function () {
93                bCaptchaExecuted = false;
94                sCurrentToken = '';
95                const obTokenInput = obForm.querySelector('input[name="smart-token"]');
96                if (obTokenInput) {
97                    obTokenInput.value = '';
98                }
99            }
100        }); } catch (e) {
101            console.error('[Captcha] Ошибка render() для формы:', sCaptchaId, e);
102            arFailedForms.add(obForm);
103            return;
104        }
105
106        // Сохраняем API
107        const obCaptchaAPI = {
108            form: obForm,
109            container: obCaptchaContainer,
110            widgetId: nWidgetId,
111            isExecuted: function () { return bCaptchaExecuted; },
112            getToken: function () { return sCurrentToken; },
113            reset: function () {
114                bCaptchaExecuted = false;
115                sCurrentToken = '';
116                fnPendingSubmit = null;
117                const obTokenInput = obForm.querySelector('input[name="smart-token"]');
118                if (obTokenInput) {
119                    obTokenInput.value = '';
120                }
121                try {
122                    if (nWidgetId !== null) {
123                        window.smartCaptcha.reset(nWidgetId);
124                    }
125                } catch (e) {
126                    console.warn('[Captcha] Ошибка reset():', e);
127                }
128            },
129            execute: function (fnCallback) {
130                if (fnCallback) {
131                    fnPendingSubmit = fnCallback;
132                }
133                try {
134                    window.smartCaptcha.execute(nWidgetId);
135                } catch (e) {
136                    console.error('[Captcha] Ошибка execute() для формы:', sCaptchaId, e);
137                    fnPendingSubmit = null;
138                    if (fnCallback) fnCallback(null);
139                }
140            }
141        };
142
143        arCaptchaInstances.set(sCaptchaId, obCaptchaAPI);
144        obForm.captchaAPI = obCaptchaAPI;
145    }
146
147    /**
148     * Инициализация всех форм с капчей
149     */
150    function initAllCaptchaForms() {
151        const arForms = document.querySelectorAll('form[data-captcha="true"]');
152        arForms.forEach(initCaptchaForForm);
153    }
154
155    /**
156     * Обработка отложенных форм после загрузки API
157     */
158    function processPendingForms() {
159        if (arPendingForms.length > 0) {
160            const formsToInit = [...arPendingForms]; // Копируем массив
161            arPendingForms = []; // Очищаем очередь
162            formsToInit.forEach(initCaptchaForForm);
163        }
164    }
165
166    /**
167     * Ожидание загрузки API Яндекса. Запускается только когда есть отложенные формы,
168     * то есть пользователь уже начал взаимодействие, а captcha.js ещё не догрузился.
169     */
170    function startApiWatcher() {
171        if (nCheckInterval !== null) {
172            return;
173        }
174
175        nCheckInterval = setInterval(function () {
176            if (typeof window.smartCaptcha !== 'undefined') {
177                stopApiWatcher();
178
179                // Обрабатываем ТОЛЬКО отложенные формы, не все заново
180                processPendingForms();
181            }
182        }, 100);
183
184        // Очищаем интервал через 10 секунд
185        setTimeout(function () {
186            if (nCheckInterval !== null) {
187                stopApiWatcher();
188                if (arPendingForms.length > 0) {
189                    console.error('[Captcha] API не загрузился за 10 секунд');
190                }
191            }
192        }, 10000);
193    }
194
195    function stopApiWatcher() {
196        clearInterval(nCheckInterval);
197        nCheckInterval = null;
198    }
199
200    /**
201     * Первое взаимодействие с формой — момент создания виджета
202     * @param {Event} obEvent
203     */
204    function onFormInteraction(obEvent) {
205        const obTarget = obEvent.target;
206
207        if (!obTarget || typeof obTarget.closest !== 'function') {
208            return;
209        }
210
211        const obForm = obTarget.closest('form[data-captcha="true"]');
212
213        if (obForm) {
214            initCaptchaForForm(obForm);
215        }
216    }
217
218    document.addEventListener('focusin', onFormInteraction, true);
219    document.addEventListener('pointerdown', onFormInteraction, true);
220    document.addEventListener('click', onFormInteraction, true);
221    document.addEventListener('submit', onFormInteraction, true);
222
223    // Глобальный API
224    window.YandexCaptchaAPI = {
225        getFormCaptcha: function (formIdOrElement) {
226            const sFormId = typeof formIdOrElement === 'string'
227                ? formIdOrElement
228                : formIdOrElement.id;
229            return arCaptchaInstances.get(sFormId) || null;
230        },
231        initForm: initCaptchaForForm,
232        reinitAll: initAllCaptchaForms,
233        // Для отладки
234        debug: {
235            getInstances: () => arCaptchaInstances,
236            getPending: () => arPendingForms
237        }
238    };
239})();

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.