class SVCaptcha { constructor(obj) { this.obj = obj; this.sendingKey = ''; this.hideCaptcha = this.getCookie('SVCaptchaLimit'); this.userKey = this.getCookie('SVCaptchaToken'); this.speed = 1000; this.oldTextButton = ''; this.isBotShield = this.obj.bs !== '1' ? false : true; this.itemWidth = parseFloat(getComputedStyle(document.body).getPropertyValue('--sv-item-width')) || 0; this.gap = parseFloat(getComputedStyle(document.body).getPropertyValue('--sv-gap')) || 0; this.innerPadding = parseFloat(getComputedStyle(document.body).getPropertyValue('--sv-padding')) || 0; this.init(); } detectDevice() { const userAgent = navigator.userAgent || navigator.vendor || window.opera; if ((/iPad|iPhone|iPod/.test(userAgent) && !window.MSStream) || /Macintosh/.test(userAgent)) { return 'apple'; } if (/android/i.test(userAgent)) { return 'android'; } return 'apple'; } uploadVariables() { this.sectionCaptcha = document.querySelector('.sv-captcha'); if (this.sectionCaptcha) { this.sectionCaptchaInner = this.sectionCaptcha.querySelector('.sv-captcha-inner'); this.captchaItems = this.sectionCaptcha.querySelector('.sv-captcha-items'); this.captchaAllItem = this.sectionCaptcha.querySelectorAll('.sv-captcha-item'); this.checkButton = this.sectionCaptcha.querySelector('.check-sv-captcha'); this.messageCaptcha = this.sectionCaptcha.querySelector('.message'); if (this.oldTextButton == '') { this.oldTextButton = this.checkButton.textContent; } } } addCaptcha() { this.ajax('addCaptcha'); } checkCaptcha() { this.checkButton.onclick = (e) => { this.sectionCaptcha.classList.add('loading'); this.checkButton.classList.add('loading'); this.checkButton.innerHTML = ``; this.uploadVariables(); this.sendingKey = ''; this.captchaAllItem.forEach(captchaItem => { this.sendingKey += captchaItem.dataset.index; }); this.ajax('checkCaptcha'); }; } addHtml(response) { if (!response['CAPTCHA_ELEMENTS'] || response['ATTEMPTS'] >= 3) { return; } let deviceClass = this.detectDevice(); let html = ''; html = `
`; html += `
`; html += `

Защита от ботов

`; html += `Перетаскивайте элементы по возрастанию`; html += `
`; response['CAPTCHA_ELEMENTS'].forEach((captchaValue, key) => { html += `
`; }); html += `
`; html += ``; html += `
`; html += `
`; document.body.insertAdjacentHTML('beforeend', html); this.uploadVariables(); new DraggableItems({ containerSelector: '.sv-captcha-items', itemSelector: '.sv-captcha-item', buttonSelector: '.check-sv-captcha' }); this.checkCaptcha(); } addMessage(message) { if (!message) { return; } this.checkButton.textContent = message; this.sectionCaptcha.classList.remove('loading'); setTimeout(() => { this.checkButton.textContent = this.oldTextButton; this.checkButton.classList.remove('loading'); }, this.speed); } getCookie(name) { var matches = document.cookie.match(new RegExp("(?:^|; )" + name.replace(/([\.$?*|{}\(\)\[\]\\\/\+^])/g, '\\$1') + "=([^;]*)")); return matches ? decodeURIComponent(matches[1]) : undefined; } setCookie(name, value, options = {}) { const { days = 0, hours = 0, minutes = 0, seconds = 0, path = '/', secure = false, samesite = 'Lax' } = options; const expires = new Date(); const totalMs = (days * 24 * 60 * 60 * 1000) + (hours * 60 * 60 * 1000) + (minutes * 60 * 1000) + (seconds * 1000); expires.setTime(expires.getTime() + totalMs); let cookieStr = `${name}=${encodeURIComponent(value)};expires=${expires.toUTCString()};path=${path}`; if (secure) cookieStr += ';Secure'; if (samesite) cookieStr += `;SameSite=${samesite}`; document.cookie = cookieStr; } handleFailure(attempts) { if (attempts >= 3) { this.setCookie('SVCaptchaLimit', 'Y', { days: 7 }); } if (attempts < 3 || !this.sectionCaptcha) { return; } setTimeout(() => { this.sectionCaptcha.remove(); }, this.speed); } handleSuccess(success, event) { if (success == 'Y') { this.sendYandexMetrica(event); setTimeout(() => { this.sectionCaptcha.remove(); }, this.speed); } } sendYandexMetrica(event) { const send = () => { if (typeof ym !== 'undefined') { ym(this.obj.ymId, 'reachGoal', event); this.setCookie('SVCaptchaLimit', 'Y', { days: 7 }); } else { setTimeout(send, 100); } }; send(); } ajax(action) { let data = new FormData(); data.set(action, true); data.set('sendingKey', this.sendingKey); data.set('idProject', this.obj.id); data.set('userKey', this.userKey); data.set('sourceUrl', this.obj.sourceUrl); fetch('https://sw1.seoven.ru/SVCaptchaV4/SVCaptchaAjax.php', { method: 'POST', body: data, }) .then(response => response.json()) .then(response => { try { if (response.BREAK === 'Y') return; if (this.isBotShield || response.SILENCE) { this.sendYandexMetrica(response.EVENT); } else { this.addHtml(response); this.addMessage(response.MESSAGE); this.handleFailure(response.ATTEMPTS); this.handleSuccess(response.SUCCESS, response.EVENT); } } catch(e) { console.error(e); } }) .catch(err => { console.error(err); }); } async checkAuthToken() { if (!this.isBotShield) return; try { const response = await fetch('/cgi-bin/botshield/challenge/check-token', { method: 'GET', credentials: 'include', headers: { 'Accept': 'application/json' } }); const data = await response.json(); return response.status === 200 && data?.valid; } catch (err) { // console.error('Ошибка при запросе:', err); return false; } } async init() { try { this.isBotShield = await this.checkAuthToken(); } catch (err) { console.error(err); this.isBotShield = false; } if (this.obj.viewCaptcha == 'fullscreen') { if (this.hideCaptcha != 'Y') { if (!this.userKey) { this.userKey = self.crypto.randomUUID(); this.setCookie('SVCaptchaToken', this.userKey, { minutes: 2 }); } this.addCaptcha(); } } } } class DraggableItems { constructor(config = {}) { this.containerSelector = config.containerSelector || '.container'; this.itemSelector = config.itemSelector || '.item'; this.buttonSelector = config.buttonSelector || '.sync-btn'; this.items = []; this.element = null; this.inputX = 0; this.offsetX = 0; this.raf = null; this.slotSize = 0; this.maxSlots = 0; this.positions = []; this.gap = 0; this.containerOffset = 0; this.init(); } init() { this.items = document.querySelectorAll(`${this.containerSelector} ${this.itemSelector}`); if (!this.items.length) { console.error('No items found with the specified selectors'); return; } this.maxSlots = this.items.length; this.setupContainer(); this.setupPositions(); this.addEventListeners(); this.initSyncButton(); // Добавляем слушатель события resize window.addEventListener('resize', () => this.handleResize()); } setupContainer() { const firstItem = this.items[0]; const computedStyle = getComputedStyle(firstItem); const container = document.querySelector(this.containerSelector); const containerRect = container.getBoundingClientRect(); this.slotSize = parseFloat(computedStyle.width); this.gap = parseFloat(getComputedStyle(document.body).getPropertyValue('--sv-gap')) || 0; container.style.width = `${this.maxSlots * this.slotSize + (this.maxSlots - 1) * this.gap}px`; this.containerOffset = containerRect.left; } setupPositions() { this.positions = Array.from(this.items).map((item, key) => ({ element: item, slot: key })); this.updatePositions(); } updatePositions() { this.positions.forEach(pos => { if (pos.element !== this.element) { pos.element.style.left = `${pos.slot * (this.slotSize + this.gap)}px`; } }); } addEventListeners() { document.addEventListener('mousedown', (e) => this.handlePress(e)); document.addEventListener('touchstart', (e) => this.handlePress(e), { passive: false }); } initSyncButton() { const syncButton = document.querySelector(this.buttonSelector); if (syncButton) { syncButton.addEventListener('click', () => this.syncDomWithPositions()); } else { console.warn('Sync button not found with the specified selector'); } } syncDomWithPositions() { const container = document.querySelector(this.containerSelector); this.positions.forEach(pos => { container.appendChild(pos.element); }); this.updatePositions(); } handlePress(event) { const target = event.target; if (!target.matches(this.itemSelector)) return; event.preventDefault(); this.element = target; this.element.classList.add('grabbing'); const isTouch = event.type === 'touchstart'; const clientX = isTouch ? event.touches[0].clientX : event.clientX; const bbox = this.element.getBoundingClientRect(); this.inputX = clientX; this.offsetX = this.inputX - bbox.left; this.setupMoveListeners(isTouch); this.raf = requestAnimationFrame(() => this.updateDragging()); } setupMoveListeners(isTouch) { if (isTouch) { document.addEventListener('touchmove', (e) => this.handleMove(e), { passive: false }); document.addEventListener('touchend', (e) => this.handleRelease(e), { passive: false }); } else { document.addEventListener('mousemove', (e) => this.handleMove(e)); document.addEventListener('mouseup', (e) => this.handleRelease(e)); } } handleMove(event) { const isTouch = event.type === 'touchmove'; this.inputX = isTouch ? event.touches[0].clientX : event.clientX; } updateDragging() { if (!this.element) return; let newLeft = this.inputX - this.offsetX - this.containerOffset; newLeft = Math.max(0, Math.min(newLeft, (this.maxSlots - 1) * (this.slotSize + this.gap))); this.element.style.left = `${newLeft}px`; const mouseSlot = Math.round(newLeft / (this.slotSize + this.gap)); const boundedSlot = Math.max(0, Math.min(mouseSlot, this.maxSlots - 1)); const draggedIndex = this.positions.findIndex(pos => pos.element === this.element); const draggedPos = this.positions[draggedIndex]; if (boundedSlot !== draggedPos.slot) { this.positions.splice(draggedIndex, 1); this.positions.splice(boundedSlot, 0, draggedPos); this.positions.forEach((pos, index) => pos.slot = index); this.updatePositions(); } this.raf = requestAnimationFrame(() => this.updateDragging()); } handleRelease(event) { if (!this.element) return; this.element.classList.remove('grabbing'); const newLeft = parseFloat(this.element.style.left); const finalSlot = Math.round(newLeft / (this.slotSize + this.gap)); this.element.style.left = `${finalSlot * (this.slotSize + this.gap)}px`; this.cleanup(event); } cleanup(event) { this.element = null; const isTouch = event.type === 'touchend'; if (isTouch) { document.removeEventListener('touchmove', (e) => this.handleMove(e)); document.removeEventListener('touchend', (e) => this.handleRelease(e)); } else { document.removeEventListener('mousemove', (e) => this.handleMove(e)); document.removeEventListener('mouseup', (e) => this.handleRelease(e)); } if (this.raf) { cancelAnimationFrame(this.raf); this.raf = null; } } // Новый метод для обработки ресайза handleResize() { // Пересчитываем параметры контейнера и позиций this.setupContainer(); this.updatePositions(); // Если элемент в данный момент перетаскивается, корректируем его позицию if (this.element) { const newLeft = this.inputX - this.offsetX - this.containerOffset; this.element.style.left = `${Math.max(0, Math.min(newLeft, (this.maxSlots - 1) * (this.slotSize + this.gap)))}px`; } } // Опционально: метод для уничтожения экземпляра класса destroy() { window.removeEventListener('resize', () => this.handleResize()); // Дополнительная очистка других слушателей, если нужно } }var css = document.createElement('link');css.href = 'https://sw1.seoven.ru/SVCaptchaV4/SVCaptchaStyle.css?r=' + Math.random();css.rel = 'stylesheet';document.head.appendChild(css); css.onload = () => { new SVCaptcha({ viewCaptcha: 'fullscreen', id: 7, ymId: 95388618, sourceUrl: '', bs: '', }); };