UNPKG

paybito-slider-captcha

Version:

A beautiful, interactive slider-based CAPTCHA verification system with puzzle piece matching. Provides secure human verification with an elegant user interface.

1,100 lines (962 loc) â€ĸ 32.8 kB
/** * Paybito Slider Captcha - Interactive Puzzle Verification System * A beautiful, secure slider-based CAPTCHA with puzzle piece matching * * @author Md Athar * @version 1.0.1 * @license MIT */ class VerificationSlider { constructor(options = {}) { if(!options.apiEndpoint) return alert("API endpoint is required for VerificationSlider"); // API Configuration this.API_ENDPOINT = options.apiEndpoint; // Captcha Properties this.puzzleX = 0; this.sliderValue = 0; this.captchaId = ''; this.positionX = ''; this.positionY = ''; this.encryptedData = ''; this.tolerance = options.tolerance || 5; // Puzzle Configuration this.shapes = ["star", "circle", "triangle", "diamond", "puzzle", "heart"]; this.defaultImage = options.defaultImage || "https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcRsULqCy41TcR6rECUinoAuRQhaJsgcIjvtmw&s"; this.imageWidth = options.imageWidth || 300; this.imageHeight = options.imageHeight || 200; this.pieceSize = options.pieceSize || 50; // State Management this.inactivityTimer = null; this.callback = null; this.modal = null; this.overlay = null; this.isInitialized = false; this.isVisible = false; } /** * Initialize the verification slider * Creates the modal and sets up the interface */ init() { if (this.isInitialized) return; this.createModal(); this.isInitialized = true; } /** * Show verification modal and start the verification process * @param {Function} callback - Callback function to handle verification result */ verify(callback) { if (!this.isInitialized) this.init(); this.callback = callback; this.showModal(); this.loadDefaultImage(); } /** * Create the modal HTML structure * @private */ createModal() { this.overlay = document.createElement('div'); this.overlay.className = 'verification-overlay'; this.modal = document.createElement('div'); this.modal.className = 'verification-modal'; const modalHTML = ` <div class="verification-modal-content"> <div class="verification-modal-header"> <h3 class="verification-modal-title"> <span class="verification-shield-icon">đŸ›Ąī¸</span> Security Verification </h3> <button type="button" class="verification-close-btn" id="verification-close-btn"> <span class="verification-close-icon">×</span> </button> </div> <div class="verification-modal-body"> <div class="verification-captcha-container"> <div class="verification-captcha-area"> <img id="verification-base-image" alt="Verification puzzle" class="verification-base-image"> <img id="verification-drag-piece" class="verification-drag-piece"> <div id="verification-loader" class="verification-loader"> <div class="verification-spinner"></div> <p>Generating puzzle...</p> </div> </div> <div class="verification-slider-container"> <label for="verification-slider" class="verification-slider-label"> <span class="verification-arrow-icon">⇄</span> Slide to verify </label> <div class="verification-slider-wrapper"> <input type="range" id="verification-slider" class="verification-slider" min="0" max="100" value="0"> <div class="verification-slider-track"> <div class="verification-slider-fill" id="verification-slider-fill"></div> </div> </div> </div> <div class="verification-status" id="verification-status"> <div class="verification-alert verification-alert-info"> <span class="verification-info-icon">â„šī¸</span> Drag the slider to match the puzzle piece position </div> </div> </div> </div> <div class="verification-modal-footer"> <div class="verification-powered-by"> Powered by <a href="https://paybito.com" target="_blank">PayBitoPro</a> </div> <div class="verification-footer-buttons"> <button type="button" class="verification-btn verification-btn-secondary" id="verification-refresh-btn"> <span class="verification-refresh-icon">🔄</span> Refresh </button> <button type="button" class="verification-btn verification-btn-cancel" id="verification-cancel-btn"> Cancel </button> </div> </div> </div> `; this.modal.innerHTML = modalHTML; this.overlay.appendChild(this.modal); document.body.appendChild(this.overlay); this.addStyles(); this.setupEventListeners(); } /** * Add CSS styles to the document * @private */ addStyles() { const styles = ` <style id="verification-slider-styles"> .verification-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0, 0, 0, 0.5); display: none; justify-content: center; align-items: center; z-index: 10000; backdrop-filter: blur(5px); animation: fadeIn 0.3s ease-in-out; } .verification-overlay.show { display: flex; } .verification-modal { background: white; border-radius: 12px; margin: 0 auto; box-shadow: 0 20px 40px rgba(0, 0, 0, 0.3); max-width: 500px; width: 90%; max-height: 90vh; overflow-y: auto; transform: scale(0.8); opacity: 0; transition: all 0.3s ease-in-out; } .verification-modal.show { transform: scale(1); opacity: 1; } .verification-modal-content { position: relative; } .verification-modal-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 20px; border-radius: 12px 12px 0 0; display: flex; justify-content: space-between; align-items: center; } .verification-modal-title { margin: 0; font-size: 18px; font-weight: 600; display: flex; align-items: center; gap: 10px; } .verification-shield-icon { font-size: 20px; } .verification-close-btn { background: none; border: none; color: white; cursor: pointer; padding: 5px; border-radius: 50%; width: 30px; height: 30px; display: flex; align-items: center; justify-content: center; transition: background 0.2s; } .verification-close-btn:hover { background: rgba(255, 255, 255, 0.2); } .verification-close-icon { font-size: 20px; line-height: 1; } .verification-modal-body { padding: 30px; } .verification-captcha-container { max-width: 400px; margin: 0 auto; padding: 25px; border: 2px solid #e1e8ed; border-radius: 12px; background: linear-gradient(145deg, #ffffff, #f8f9fa); box-shadow: 0 8px 25px rgba(0, 0, 0, 0.1); } .verification-captcha-area { position: relative; width: 100%; aspect-ratio: 3/2; margin: 0 auto 25px; border: 2px solid #ddd; border-radius: 8px; background: #f8f9fa; box-shadow: inset 0 2px 6px rgba(0, 0, 0, 0.1); overflow: hidden; } .verification-base-image { position: absolute; top: 0; left: 0; width: 100%; height: 100%; object-fit: cover; border-radius: 6px; opacity: 0; transition: opacity 0.3s ease; } .verification-base-image.loaded { opacity: 1; } .verification-drag-piece { position: absolute; width: 16.67%; height: 25%; cursor: grab; z-index: 10; transition: left 0.1s ease-in-out; border-radius: 6px; left: 0; top: 0; opacity: 0; transition: opacity 0.3s ease, left 0.1s ease-in-out; } .verification-drag-piece.loaded { opacity: 1; } .verification-drag-piece:active { cursor: grabbing; } .verification-loader { position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%); text-align: center; background: rgba(255, 255, 255, 0.95); padding: 25px; border-radius: 10px; box-shadow: 0 6px 20px rgba(0, 0, 0, 0.15); z-index: 20; } .verification-spinner { width: 40px; height: 40px; border: 4px solid #f3f3f3; border-top: 4px solid #667eea; border-radius: 50%; animation: spin 1s linear infinite; margin: 0 auto 15px; } .verification-loader p { margin: 0; color: #666; font-size: 14px; } .verification-slider-container { margin-bottom: 25px; } .verification-slider-label { display: flex; align-items: center; gap: 8px; margin-bottom: 12px; font-size: 14px; font-weight: 600; color: #333; } .verification-arrow-icon { font-size: 16px; color: #667eea; font-weight: bold; } .verification-slider-wrapper { position: relative; } .verification-slider { width: 100%; height: 8px; background: linear-gradient(to right, #e9ecef, #dee2e6); border-radius: 20px; outline: none; appearance: none; cursor: pointer; position: relative; z-index: 2; } .verification-slider::-webkit-slider-thumb { appearance: none; width: 32px; height: 32px; background: linear-gradient(45deg, #667eea, #764ba2); border-radius: 50%; cursor: pointer; box-shadow: 0 3px 8px rgba(102, 126, 234, 0.5); transition: all 0.2s ease; position: relative; } .verification-slider::-webkit-slider-thumb:hover { transform: scale(1.1); box-shadow: 0 5px 15px rgba(102, 126, 234, 0.7); } .verification-slider::-moz-range-thumb { width: 32px; height: 32px; background: linear-gradient(45deg, #667eea, #764ba2); border-radius: 50%; cursor: pointer; border: none; box-shadow: 0 3px 8px rgba(102, 126, 234, 0.5); position: relative; } .verification-slider-track { position: absolute; top: 0; left: 0; width: 100%; background: #e9ecef; z-index: 1; } .verification-slider-fill { height: 100%; background: linear-gradient(90deg, #667eea, #764ba2); border-radius: 20px; width: 0%; transition: width 0.1s ease; } .verification-status { min-height: 60px; } .verification-alert { padding: 12px 16px; border-radius: 8px; margin: 0; font-size: 14px; display: flex; align-items: center; gap: 10px; line-height: 1.4; } .verification-alert-success { background: #d4edda; border: 1px solid #c3e6cb; color: #155724; } .verification-alert-danger { background: #f8d7da; border: 1px solid #f5c6cb; color: #721c24; } .verification-alert-info { background: #cce7ff; border: 1px solid #b8daff; color: #004085; } .verification-info-icon { font-size: 16px; } .verification-modal-footer { padding: 20px 30px; border-top: 1px solid #e1e8ed; display: flex; justify-content: space-between; align-items: center; gap: 12px; border-radius: 0 0 12px 12px; background: #f8f9fa; } .verification-powered-by { font-size: 12px; color: #666; display: flex; align-items: center; gap: 5px; } .verification-powered-by a { color: #667eea; text-decoration: none; font-weight: 600; } .verification-powered-by a:hover { text-decoration: underline; } .verification-footer-buttons { display: flex; gap: 12px; } .verification-btn { padding: 10px 20px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; transition: all 0.2s ease; display: flex; align-items: center; gap: 8px; min-width: 100px; justify-content: center; } .verification-btn-secondary { background: #6c757d; color: white; } .verification-btn-secondary:hover { background: #5a6268; transform: translateY(-1px); } .verification-btn-cancel { background: #dc3545; color: white; } .verification-btn-cancel:hover { background: #c82333; transform: translateY(-1px); } .verification-refresh-icon { font-size: 14px; } @keyframes fadeIn { from { opacity: 0; } to { opacity: 1; } } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } @media (max-width: 767px) { .verification-modal { width: 95%; margin: 10px; } .verification-modal-body { padding: 20px; } .verification-captcha-container { padding: 20px; } .verification-drag-piece { width: 20%; height: 30%; } .verification-modal-footer { padding: 15px 20px; flex-direction: column; } .verification-footer-buttons { width: 100%; flex-direction: column; } .verification-btn { width: 100%; } .verification-powered-by { align-self: center; margin-top: 10px; } } </style> `; document.head.insertAdjacentHTML('beforeend', styles); } /** * Set up event listeners for the slider and modal interactions * @private */ setupEventListeners() { const slider = document.getElementById('verification-slider'); const sliderFill = document.getElementById('verification-slider-fill'); const dragPiece = document.getElementById('verification-drag-piece'); const closeBtn = document.getElementById('verification-close-btn'); const cancelBtn = document.getElementById('verification-cancel-btn'); const refreshBtn = document.getElementById('verification-refresh-btn'); // Slider events slider.addEventListener('input', (e) => { this.sliderValue = e.target.value; sliderFill.style.width = `${this.sliderValue}%`; const captchaArea = document.querySelector('.verification-captcha-area'); const areaWidth = captchaArea.offsetWidth; const pieceWidth = dragPiece.offsetWidth; const maxOffset = areaWidth - pieceWidth; const offsetX = (this.sliderValue / 100) * maxOffset; dragPiece.style.left = `${offsetX}px`; this.resetInactivityTimer(); }); slider.addEventListener('change', () => { this.validatePosition(); }); // Button events - bind to this instance closeBtn.addEventListener('click', () => { this.hideModal(); }); cancelBtn.addEventListener('click', () => { this.hideModal(); }); refreshBtn.addEventListener('click', () => { this.refreshCaptcha(); }); // Close modal on overlay click this.overlay.addEventListener('click', (e) => { if (e.target === this.overlay) this.hideModal(); }); // Close modal on Escape key document.addEventListener('keydown', (e) => { if (e.key === 'Escape' && this.isVisible) this.hideModal(); }); } /** * Show the verification modal * @private */ showModal() { this.overlay.classList.add('show'); setTimeout(() => { this.modal.classList.add('show'); }, 100); this.isVisible = true; document.body.style.overflow = 'hidden'; } /** * Hide the verification modal */ hideModal() { this.modal.classList.remove('show'); setTimeout(() => { this.overlay.classList.remove('show'); this.isVisible = false; document.body.style.overflow = ''; this.cleanup(); if (this.callback) this.callback({ success: false }); }, 300); } /** * Load the default image and start puzzle generation * @private */ loadDefaultImage() { // Show loader immediately this.showLoader(); // Hide images initially const baseImage = document.getElementById('verification-base-image'); const dragPiece = document.getElementById('verification-drag-piece'); baseImage.classList.remove('loaded'); dragPiece.classList.remove('loaded'); baseImage.style.opacity = '0'; dragPiece.style.opacity = '0'; // Start generating captcha after a short delay setTimeout(() => { this.generateCaptcha(); }, 500); } /** * Generate CAPTCHA from API * @private */ async generateCaptcha() { try { const response = await fetch(this.API_ENDPOINT); const data = await response.json(); this.captchaId = data.sessionId; this.positionX = data.positionX; this.positionY = data.positionY; const msg = `${this.positionX}:Rtz2IQzefccFwmiurXq)8IjzUm?v3o:${this.positionY}`; this.encryptedData = await this.sha256(msg); // Create puzzle and wait for it to complete await this.createPuzzle(data); } catch (error) { console.error('Error generating CAPTCHA:', error); this.showStatus('Error generating verification. Please try again.', 'danger'); this.hideLoader(); } } /** * Create the puzzle piece and base image * @private * @param {Object} data - CAPTCHA data from API */ async createPuzzle(data) { const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.width = this.imageWidth; canvas.height = this.imageHeight; const image = new Image(); image.crossOrigin = "anonymous"; image.src = data.baseImage || this.defaultImage; return new Promise((resolve, reject) => { image.onload = () => { try { ctx.drawImage(image, 0, 0, canvas.width, canvas.height); const captchaArea = document.querySelector('.verification-captcha-area'); const areaWidth = captchaArea.offsetWidth; const areaHeight = captchaArea.offsetHeight; const pieceWidthPx = (this.pieceSize / this.imageWidth) * areaWidth; const pieceHeightPx = (this.pieceSize / this.imageHeight) * areaHeight; this.puzzleX = Math.floor(Math.random() * (canvas.width - this.pieceSize)); const fixedY = parseInt(data.positionY || Math.floor(Math.random() * (canvas.height - this.pieceSize))); const shape = this.shapes[Math.floor(Math.random() * this.shapes.length)]; // Create puzzle piece const pieceCanvas = document.createElement('canvas'); const pieceCtx = pieceCanvas.getContext('2d'); pieceCanvas.width = this.pieceSize; pieceCanvas.height = this.pieceSize; this.drawShape(pieceCtx, shape, this.pieceSize / 2, this.pieceSize / 2); pieceCtx.clip(); pieceCtx.drawImage(canvas, this.puzzleX, fixedY, this.pieceSize, this.pieceSize, 0, 0, this.pieceSize, this.pieceSize); // Remove piece from base image ctx.save(); ctx.beginPath(); this.drawShape(ctx, shape, this.puzzleX + this.pieceSize / 2, fixedY + this.pieceSize / 2); ctx.clip(); ctx.clearRect(this.puzzleX, fixedY, this.pieceSize, this.pieceSize); ctx.restore(); // Update UI const baseImage = document.getElementById('verification-base-image'); const dragPiece = document.getElementById('verification-drag-piece'); const slider = document.getElementById('verification-slider'); const sliderFill = document.getElementById('verification-slider-fill'); // Set the images baseImage.src = canvas.toDataURL(); dragPiece.src = pieceCanvas.toDataURL(); // Reset slider slider.value = 0; sliderFill.style.width = '0%'; dragPiece.style.left = '0px'; const topPositionPercent = (fixedY / this.imageHeight) * 100; dragPiece.style.top = `${topPositionPercent}%`; // Wait for both images to load before hiding loader let baseImageLoaded = false; let pieceImageLoaded = false; const checkBothImagesLoaded = () => { if (baseImageLoaded && pieceImageLoaded) { // Small delay to ensure smooth transition setTimeout(() => { this.hideLoader(); baseImage.classList.add('loaded'); dragPiece.classList.add('loaded'); baseImage.style.opacity = '1'; dragPiece.style.opacity = '1'; }, 200); } }; // Add load listeners to both images const tempBaseImg = new Image(); tempBaseImg.onload = () => { baseImageLoaded = true; checkBothImagesLoaded(); }; tempBaseImg.src = baseImage.src; const tempPieceImg = new Image(); tempPieceImg.onload = () => { pieceImageLoaded = true; checkBothImagesLoaded(); }; tempPieceImg.src = dragPiece.src; this.resetInactivityTimer(); resolve(); } catch (error) { console.error('Error creating puzzle:', error); this.hideLoader(); reject(error); } }; image.onerror = () => { console.error('Error loading image'); this.hideLoader(); this.showStatus('Error loading image. Please try again.', 'danger'); reject(new Error('Image loading failed')); }; }); } /** * Draw different shapes for puzzle pieces * @private * @param {CanvasRenderingContext2D} ctx - Canvas context * @param {string} shape - Shape type * @param {number} x - X coordinate * @param {number} y - Y coordinate */ drawShape(ctx, shape, x, y) { switch (shape) { case 'star': this.drawStar(ctx, x, y, 5, 20, 10); break; case 'circle': this.drawCircle(ctx, x, y, 20); break; case 'triangle': this.drawTriangle(ctx, x, y, 40, 40); break; case 'diamond': this.drawDiamond(ctx, x, y, 40, 40); break; case 'puzzle': this.drawPuzzle(ctx, x, y, 60); break; case 'heart': this.drawHeart(ctx, x, y, 30); break; default: this.drawCircle(ctx, x, y, 20); } } /** * Draw star shape * @private */ drawStar(ctx, cx, cy, spikes, outerRadius, innerRadius) { let rot = (Math.PI / 2) * 3; const step = Math.PI / spikes; ctx.beginPath(); ctx.moveTo(cx, cy - outerRadius); for (let i = 0; i < spikes; i++) { const x = cx + Math.cos(rot) * outerRadius; const y = cy + Math.sin(rot) * outerRadius; ctx.lineTo(x, y); rot += step; const x2 = cx + Math.cos(rot) * innerRadius; const y2 = cy + Math.sin(rot) * innerRadius; ctx.lineTo(x2, y2); rot += step; } ctx.closePath(); } /** * Draw circle shape * @private */ drawCircle(ctx, cx, cy, radius) { ctx.beginPath(); ctx.arc(cx, cy, radius, 0, Math.PI * 2); ctx.closePath(); } /** * Draw triangle shape * @private */ drawTriangle(ctx, cx, cy, width, height) { ctx.beginPath(); ctx.moveTo(cx, cy - height / 2); ctx.lineTo(cx - width / 2, cy + height / 2); ctx.lineTo(cx + width / 2, cy + height / 2); ctx.closePath(); } /** * Draw diamond shape * @private */ drawDiamond(ctx, cx, cy, width, height) { ctx.beginPath(); ctx.moveTo(cx, cy - height / 2); ctx.lineTo(cx - width / 2, cy); ctx.lineTo(cx, cy + height / 2); ctx.lineTo(cx + width / 2, cy); ctx.closePath(); } /** * Draw puzzle piece shape * @private */ drawPuzzle(ctx, centerX, centerY, size) { const puzzleSize = size * 0.6; const tabSize = puzzleSize * 0.25; const left = centerX - puzzleSize / 2; const right = centerX + puzzleSize / 2; const top = centerY - puzzleSize / 2; const bottom = centerY + puzzleSize / 2; const midX = centerX; const midY = centerY; ctx.beginPath(); ctx.moveTo(left, top); ctx.lineTo(midX - tabSize, top); ctx.quadraticCurveTo(midX - tabSize + 9, top - tabSize - 4, midX + tabSize, top); ctx.lineTo(right, top); ctx.lineTo(right, midY - tabSize); ctx.quadraticCurveTo(right + tabSize + 4, midY - tabSize + 9, right, midY + tabSize); ctx.lineTo(right, bottom); ctx.lineTo(left, bottom); ctx.lineTo(left, midY + tabSize); ctx.quadraticCurveTo(left + tabSize, midY + tabSize, left + tabSize, midY); ctx.quadraticCurveTo(left + tabSize, midY - tabSize, left, midY - tabSize); ctx.lineTo(left, top); ctx.closePath(); } /** * Draw heart shape * @private */ drawHeart(ctx, centerX, centerY, size) { const topCurveHeight = size * 0.3; ctx.beginPath(); ctx.moveTo(centerX, centerY + size / 2); ctx.bezierCurveTo( centerX - size, centerY - topCurveHeight, centerX - size * 0.1, centerY - topCurveHeight, centerX, centerY ); ctx.bezierCurveTo( centerX + size * 0.1, centerY - topCurveHeight, centerX + size, centerY - topCurveHeight, centerX, centerY + size / 2 ); ctx.closePath(); } /** * Validate if the slider position matches the puzzle position * @private */ validatePosition() { const captchaArea = document.querySelector('.verification-captcha-area'); const areaWidth = captchaArea.offsetWidth; const dragPiece = document.getElementById('verification-drag-piece'); const pieceWidth = dragPiece.offsetWidth; const maxOffset = areaWidth - pieceWidth; const offsetX = (this.sliderValue / 100) * maxOffset; const actualPositionPercent = (this.puzzleX / this.imageWidth) * 100; const sliderPositionPercent = (offsetX / areaWidth) * 100; if (Math.abs(actualPositionPercent - sliderPositionPercent) <= this.tolerance) { this.showStatus('Verification successful! ✅', 'success'); setTimeout(() => { this.hideModal(); if (this.callback) { this.callback({ success: true, sessionId: this.captchaId, gRecaptchaResponse: this.encryptedData }); } }, 1000); } else { this.showStatus('Position incorrect. Please try again. ❌', 'danger'); setTimeout(() => { this.refreshCaptcha(); }, 1500); } } /** * Show status message * @private * @param {string} message - Status message * @param {string} type - Message type (success, danger, info) */ showStatus(message, type) { const statusDiv = document.getElementById('verification-status'); const iconClass = type === 'success' ? '✅' : type === 'danger' ? '❌' : 'â„šī¸'; statusDiv.innerHTML = ` <div class="verification-alert verification-alert-${type}"> <span class="verification-info-icon">${iconClass}</span> ${message} </div> `; } /** * Show loading indicator * @private */ showLoader() { document.getElementById('verification-loader').style.display = 'block'; } /** * Hide loading indicator * @private */ hideLoader() { document.getElementById('verification-loader').style.display = 'none'; } /** * Refresh the CAPTCHA puzzle */ refreshCaptcha() { const slider = document.getElementById('verification-slider'); const sliderFill = document.getElementById('verification-slider-fill'); slider.value = 0; sliderFill.style.width = '0%'; this.sliderValue = 0; this.showStatus('Drag the slider to match the puzzle piece position', 'info'); this.loadDefaultImage(); } /** * Reset the inactivity timer * @private */ resetInactivityTimer() { clearTimeout(this.inactivityTimer); this.inactivityTimer = setTimeout(() => { if (this.sliderValue === 0) { this.refreshCaptcha(); } }, 60000); // 60 seconds } /** * Generate SHA-256 hash * @private * @param {string} message - Message to hash * @returns {Promise<string>} Hexadecimal hash */ async sha256(message) { const msgBuffer = new TextEncoder().encode(message); const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer); const hashArray = Array.from(new Uint8Array(hashBuffer)); return hashArray.map(b => b.toString(16).padStart(2, '0')).join(''); } /** * Clean up timers and reset state * @private */ cleanup() { clearTimeout(this.inactivityTimer); this.sliderValue = 0; this.showStatus('Drag the slider to match the puzzle piece position', 'info'); } /** * Destroy the verification slider and clean up */ destroy() { if (this.overlay) this.overlay.remove(); const styles = document.getElementById('verification-slider-styles'); if (styles) styles.remove(); document.body.style.overflow = ''; this.isInitialized = false; this.isVisible = false; this.cleanup(); } } export default VerificationSlider;