T.ME/BIBIL_0DAY
CasperSecurity


Server : Apache/2
System : Linux server-15-235-50-60 5.15.0-164-generic #174-Ubuntu SMP Fri Nov 14 20:25:16 UTC 2025 x86_64
User : gositeme ( 1004)
PHP Version : 8.2.29
Disable Function : exec,system,passthru,shell_exec,proc_close,proc_open,dl,popen,show_source,posix_kill,posix_mkfifo,posix_getpwuid,posix_setpgid,posix_setsid,posix_setuid,posix_setgid,posix_seteuid,posix_setegid,posix_uname
Directory :  /home/gositeme/.cursor-server/data/User/History/6b963d87/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Current File : /home/gositeme/.cursor-server/data/User/History/6b963d87/udmV.js
/**
 * Modern Homepage Interactive Map - Complete Rewrite
 * Features: Smooth rendering, proper data loading, modern interactions
 */

(function() {
    'use strict';

    const CONFIG = {
        bounds: {
            minLat: 45.0,
            maxLat: 51.0,
            minLng: -80.0,
            maxLng: -66.0
        },
        zoom: {
            min: 0.5,
            max: 5,
            default: 1.2,
            step: 1.2
        },
        cityRadius: {
            major: 12,
            medium: 8,
            small: 5,
            tiny: 3
        }
    };

    const state = {
        canvas: null,
        ctx: null,
        container: null,
        cities: [],
        villages: [],
        zoom: CONFIG.zoom.default,
        panX: 0,
        panY: 0,
        isDragging: false,
        dragStart: { x: 0, y: 0 },
        hoveredCity: null,
        hoveredVillage: null,
        selectedCity: null,
        selectedVillage: null,
        initialized: false,
        dataLoaded: false
    };

    // Initialize map
    function init() {
        console.log('🗺️ Homepage Map: Initializing...');
        
        state.container = document.getElementById('homepageInteractiveMap');
        state.canvas = document.getElementById('homepageMapCanvas');
        
        if (!state.container || !state.canvas) {
            console.error('🗺️ Homepage Map: Container or canvas not found');
            return;
        }

        state.ctx = state.canvas.getContext('2d');
        if (!state.ctx) {
            console.error('🗺️ Homepage Map: Could not get canvas context');
            return;
        }

        setupCanvas();
        setupControls();
        setupEvents();
        
        // Load data with proper waiting
        waitForDataAndLoad();
        
        // Start render loop
        requestAnimationFrame(render);
        
        state.initialized = true;
        console.log('🗺️ Homepage Map: Initialized successfully');
    }

    function setupCanvas() {
        const rect = state.container.getBoundingClientRect();
        if (rect.width === 0 || rect.height === 0) {
            setTimeout(setupCanvas, 100);
            return;
        }
        
        state.canvas.width = rect.width;
        state.canvas.height = rect.height;
        state.panX = state.canvas.width / 2;
        state.panY = state.canvas.height / 2;
        
        console.log('🗺️ Homepage Map: Canvas sized to', state.canvas.width, 'x', state.canvas.height);
        
        window.addEventListener('resize', () => {
            const rect = state.container.getBoundingClientRect();
            if (rect.width > 0 && rect.height > 0) {
                state.canvas.width = rect.width;
                state.canvas.height = rect.height;
            }
        });
    }

    function setupControls() {
        const zoomIn = document.getElementById('mapZoomIn');
        const zoomOut = document.getElementById('mapZoomOut');
        const reset = document.getElementById('resetMapView');
        const locateBtn = document.getElementById('mapLocateBtn');

        if (zoomIn) {
            zoomIn.onclick = () => {
                state.zoom = Math.min(CONFIG.zoom.max, state.zoom * CONFIG.zoom.step);
            };
        }

        if (zoomOut) {
            zoomOut.onclick = () => {
                state.zoom = Math.max(CONFIG.zoom.min, state.zoom / CONFIG.zoom.step);
            };
        }

        if (reset) {
            reset.onclick = () => {
                state.zoom = CONFIG.zoom.default;
                state.panX = state.canvas.width / 2;
                state.panY = state.canvas.height / 2;
            };
        }
    }

    function setupEvents() {
        state.canvas.addEventListener('mousedown', handleMouseDown);
        state.canvas.addEventListener('mousemove', handleMouseMove);
        state.canvas.addEventListener('mouseup', handleMouseUp);
        state.canvas.addEventListener('wheel', handleWheel, { passive: false });
        state.canvas.addEventListener('click', handleClick);
        
        // Touch events
        state.canvas.addEventListener('touchstart', handleTouchStart, { passive: false });
        state.canvas.addEventListener('touchmove', handleTouchMove, { passive: false });
        state.canvas.addEventListener('touchend', handleTouchEnd);
    }

    function handleMouseDown(e) {
        state.isDragging = true;
        const rect = state.canvas.getBoundingClientRect();
        state.dragStart.x = e.clientX - rect.left;
        state.dragStart.y = e.clientY - rect.top;
        state.canvas.style.cursor = 'grabbing';
    }

    function handleMouseMove(e) {
        const rect = state.canvas.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;

        if (state.isDragging) {
            state.panX += x - state.dragStart.x;
            state.panY += y - state.dragStart.y;
            state.dragStart.x = x;
            state.dragStart.y = y;
        } else {
            // Check hover
            const worldPos = screenToWorld(x, y);
            checkHover(worldPos.x, worldPos.y);
        }
    }

    function handleMouseUp() {
        state.isDragging = false;
        state.canvas.style.cursor = 'grab';
    }

    function handleWheel(e) {
        e.preventDefault();
        const rect = state.canvas.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        
        const oldZoom = state.zoom;
        const zoomFactor = e.deltaY > 0 ? 1 / CONFIG.zoom.step : CONFIG.zoom.step;
        state.zoom = Math.max(CONFIG.zoom.min, Math.min(CONFIG.zoom.max, state.zoom * zoomFactor));
        
        const zoomChange = state.zoom / oldZoom;
        const worldPos = screenToWorld(x, y);
        state.panX = x - (x - state.panX) * zoomChange;
        state.panY = y - (y - state.panY) * zoomChange;
    }

    function handleClick(e) {
        if (state.isDragging) return;
        
        const rect = state.canvas.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        const worldPos = screenToWorld(x, y);
        
        // Check villages first
        const village = getVillageAtWorldPos(worldPos.x, worldPos.y);
        if (village) {
            state.selectedVillage = village;
            state.selectedCity = null;
            showVillageDetails(village);
            return;
        }
        
        // Then cities
        const city = getCityAtWorldPos(worldPos.x, worldPos.y);
        if (city) {
            state.selectedCity = city;
            state.selectedVillage = null;
            showCityDetails(city);
        }
    }

    function handleTouchStart(e) {
        if (e.touches.length === 1) {
            const touch = e.touches[0];
            const rect = state.canvas.getBoundingClientRect();
            state.isDragging = true;
            state.dragStart.x = touch.clientX - rect.left;
            state.dragStart.y = touch.clientY - rect.top;
        }
    }

    function handleTouchMove(e) {
        if (e.touches.length === 1 && state.isDragging) {
            e.preventDefault();
            const touch = e.touches[0];
            const rect = state.canvas.getBoundingClientRect();
            const x = touch.clientX - rect.left;
            const y = touch.clientY - rect.top;
            
            state.panX += x - state.dragStart.x;
            state.panY += y - state.dragStart.y;
            state.dragStart.x = x;
            state.dragStart.y = y;
        }
    }

    function handleTouchEnd() {
        state.isDragging = false;
    }

    function screenToWorld(screenX, screenY) {
        const bounds = CONFIG.bounds;
        const worldX = ((screenX - state.panX) / state.zoom) + (state.canvas.width / 2);
        const worldY = ((screenY - state.panY) / state.zoom) + (state.canvas.height / 2);
        
        const lng = bounds.minLng + (worldX / state.canvas.width) * (bounds.maxLng - bounds.minLng);
        const lat = bounds.maxLat - (worldY / state.canvas.height) * (bounds.maxLat - bounds.minLat);
        
        return { x: worldX, y: worldY, lat, lng };
    }

    function worldToScreen(lat, lng) {
        const bounds = CONFIG.bounds;
        const worldX = ((lng - bounds.minLng) / (bounds.maxLng - bounds.minLng)) * state.canvas.width;
        const worldY = ((bounds.maxLat - lat) / (bounds.maxLat - bounds.minLat)) * state.canvas.height;
        
        const screenX = (worldX - state.canvas.width / 2) * state.zoom + state.panX;
        const screenY = (worldY - state.canvas.height / 2) * state.zoom + state.panY;
        
        return { x: screenX, y: screenY };
    }

    function waitForDataAndLoad() {
        let attempts = 0;
        const maxAttempts = 100; // 10 seconds
        
        function tryLoad() {
            attempts++;
            
            // Check for quebecMunicipalities
            if (window.quebecMunicipalities && Object.keys(window.quebecMunicipalities).length > 0) {
                console.log('🗺️ Homepage Map: Found quebecMunicipalities, loading cities...');
                loadCities();
                loadVillages();
                state.dataLoaded = true;
            } else if (attempts < maxAttempts) {
                setTimeout(tryLoad, 100);
            } else {
                console.warn('🗺️ Homepage Map: quebecMunicipalities not found after 10 seconds, loading anyway...');
                loadCities();
                loadVillages();
            }
        }
        
        tryLoad();
    }

    function loadCities() {
        if (!window.quebecMunicipalities) {
            console.warn('🗺️ Homepage Map: quebecMunicipalities not available');
            state.cities = [];
            return;
        }

        const bounds = CONFIG.bounds;
        state.cities = [];
        
        for (const [name, data] of Object.entries(window.quebecMunicipalities)) {
            if (!data || !data.lat || !data.lng) continue;
            
            const lat = parseFloat(data.lat);
            const lng = parseFloat(data.lng);
            
            if (isNaN(lat) || isNaN(lng)) continue;
            if (lat < bounds.minLat || lat > bounds.maxLat || lng < bounds.minLng || lng > bounds.maxLng) continue;
            
            state.cities.push({
                name: name,
                lat: lat,
                lng: lng,
                region: data.region || '',
                population: parseInt(data.population) || 0,
                member_count: 0,
                post_count: 0,
                event_count: 0
            });
        }
        
        console.log('🗺️ Homepage Map: Loaded', state.cities.length, 'cities');
        if (state.cities.length > 0) {
            console.log('🗺️ Homepage Map: Sample cities:', state.cities.slice(0, 3).map(c => c.name));
        }
    }

    function loadVillages() {
        fetch('/api/map')
            .then(res => res.json())
            .then(data => {
                const villages = data.villages || data.cities || [];
                
                state.villages = villages
                    .map(v => {
                        const lat = parseFloat(v.location_lat || v.lat);
                        const lng = parseFloat(v.location_lng || v.lng);
                        if (isNaN(lat) || isNaN(lng)) return null;
                        
                        return {
                            id: v.id,
                            name: v.name,
                            name_fr: v.name_fr,
                            slug: v.slug,
                            lat: lat,
                            lng: lng,
                            region: v.region || '',
                            status: v.status || 'forming',
                            member_count: v.member_count || 0,
                            post_count: v.post_count || 0,
                            event_count: v.event_count || 0
                        };
                    })
                    .filter(v => v !== null && 
                        v.lat >= CONFIG.bounds.minLat && v.lat <= CONFIG.bounds.maxLat &&
                        v.lng >= CONFIG.bounds.minLng && v.lng <= CONFIG.bounds.maxLng
                    );
                
                console.log('🗺️ Homepage Map: Loaded', state.villages.length, 'villages');
                
                // Merge village data into cities
                villages.forEach(village => {
                    if (!village.location_lat || !village.location_lng) return;
                    const vLat = parseFloat(village.location_lat);
                    const vLng = parseFloat(village.location_lng);
                    if (isNaN(vLat) || isNaN(vLng)) return;

                    let closest = null;
                    let minDist = Infinity;
                    state.cities.forEach(city => {
                        const dist = Math.sqrt(Math.pow(city.lat - vLat, 2) + Math.pow(city.lng - vLng, 2));
                        if (dist < minDist && dist < 0.1) {
                            minDist = dist;
                            closest = city;
                        }
                    });

                    if (closest) {
                        closest.member_count += village.member_count || 0;
                        closest.post_count += village.post_count || 0;
                        closest.event_count += village.event_count || 0;
                    }
                });
            })
            .catch(err => {
                console.error('🗺️ Homepage Map: Failed to load villages', err);
            });
    }

    function getCityRadius(population) {
        if (population > 100000) return CONFIG.cityRadius.major;
        if (population > 50000) return CONFIG.cityRadius.medium;
        if (population > 10000) return CONFIG.cityRadius.small;
        return CONFIG.cityRadius.tiny;
    }

    function checkHover(worldX, worldY) {
        // Check villages first
        const village = getVillageAtWorldPos(worldX, worldY);
        if (village !== state.hoveredVillage) {
            state.hoveredVillage = village;
            state.hoveredCity = null;
            state.canvas.style.cursor = village ? 'pointer' : 'grab';
        }
        
        if (!village) {
            const city = getCityAtWorldPos(worldX, worldY);
            if (city !== state.hoveredCity) {
                state.hoveredCity = city;
                state.canvas.style.cursor = city ? 'pointer' : 'grab';
            }
        }
    }

    function getCityAtWorldPos(worldX, worldY) {
        for (const city of state.cities) {
            const screenPos = worldToScreen(city.lat, city.lng);
            const radius = getCityRadius(city.population);
            const dist = Math.sqrt(Math.pow(screenPos.x - worldX, 2) + Math.pow(screenPos.y - worldY, 2));
            if (dist <= radius * 2) {
                return city;
            }
        }
        return null;
    }

    function getVillageAtWorldPos(worldX, worldY) {
        for (const village of state.villages) {
            const screenPos = worldToScreen(village.lat, village.lng);
            const radius = Math.max(8, Math.min(20, 8 + (village.member_count || 0) * 0.5));
            const dist = Math.sqrt(Math.pow(screenPos.x - worldX, 2) + Math.pow(screenPos.y - worldY, 2));
            if (dist <= radius * 2) {
                return village;
            }
        }
        return null;
    }

    function render() {
        if (!state.ctx || !state.canvas || state.canvas.width === 0 || state.canvas.height === 0) {
            requestAnimationFrame(render);
            return;
        }

        const ctx = state.ctx;
        const w = state.canvas.width;
        const h = state.canvas.height;

        // Clear
        ctx.clearRect(0, 0, w, h);

        // Background
        const bgColor = getComputedStyle(document.documentElement).getPropertyValue('--color-bg-light').trim() || '#1a1a1a';
        ctx.fillStyle = bgColor;
        ctx.fillRect(0, 0, w, h);

        // Grid
        ctx.strokeStyle = 'rgba(212, 165, 116, 0.1)';
        ctx.lineWidth = 1;
        for (let x = 0; x < w; x += 50) {
            ctx.beginPath();
            ctx.moveTo(x, 0);
            ctx.lineTo(x, h);
            ctx.stroke();
        }
        for (let y = 0; y < h; y += 50) {
            ctx.beginPath();
            ctx.moveTo(0, y);
            ctx.lineTo(w, y);
            ctx.stroke();
        }

        // Draw cities
        if (state.cities.length === 0) {
            // Show loading message
            ctx.fillStyle = 'rgba(255, 255, 255, 0.5)';
            ctx.font = '16px sans-serif';
            ctx.textAlign = 'center';
            ctx.fillText('Loading cities...', w / 2, h / 2);
        } else {
            state.cities.forEach(city => {
                const screenPos = worldToScreen(city.lat, city.lng);
                
                // Skip if off screen
                if (screenPos.x < -100 || screenPos.x > w + 100 || screenPos.y < -100 || screenPos.y > h + 100) return;

                const isHovered = state.hoveredCity === city;
                const isSelected = state.selectedCity === city;
                const isMajor = city.population > 100000;
                const isMedium = city.population > 50000;
                const hasMembers = (city.member_count || 0) > 0;
                
                const radius = getCityRadius(city.population);
                const color = hasMembers ? '212, 165, 116' : (isMajor ? '212, 165, 116' : isMedium ? '139, 195, 74' : '100, 150, 200');
                const opacity = isSelected ? 1 : isHovered ? 0.9 : (hasMembers ? 0.9 : 0.7);
                const drawRadius = isSelected ? radius * 1.3 : isHovered ? radius * 1.1 : radius;

                // Glow
                if (hasMembers || isHovered || isSelected) {
                    const gradient = ctx.createRadialGradient(
                        screenPos.x, screenPos.y, 0,
                        screenPos.x, screenPos.y, drawRadius * 3
                    );
                    gradient.addColorStop(0, `rgba(212, 165, 116, ${isSelected ? 0.6 : hasMembers ? 0.5 : 0.4})`);
                    gradient.addColorStop(1, 'rgba(212, 165, 116, 0)');
                    ctx.fillStyle = gradient;
                    ctx.beginPath();
                    ctx.arc(screenPos.x, screenPos.y, drawRadius * 3, 0, Math.PI * 2);
                    ctx.fill();
                }

                // City marker
                ctx.fillStyle = `rgba(${color}, ${opacity})`;
                ctx.beginPath();
                ctx.arc(screenPos.x, screenPos.y, drawRadius, 0, Math.PI * 2);
                ctx.fill();

                ctx.strokeStyle = `rgba(${color}, ${opacity + 0.2})`;
                ctx.lineWidth = (hasMembers || isSelected) ? 3 : 2;
                ctx.stroke();

                // Member indicator
                if (hasMembers) {
                    ctx.fillStyle = '#10b981';
                    ctx.beginPath();
                    ctx.arc(screenPos.x + drawRadius * 0.6, screenPos.y - drawRadius * 0.6, drawRadius * 0.3, 0, Math.PI * 2);
                    ctx.fill();
                }

                // Label
                const shouldShowLabel = (isMajor || hasMembers || state.zoom > 1.8) && (isHovered || isSelected || hasMembers || state.zoom > 1.5);
                if (shouldShowLabel) {
                    const labelText = city.name + (hasMembers ? ` (${city.member_count})` : '');
                    ctx.fillStyle = 'rgba(0, 0, 0, 0.8)';
                    ctx.font = `${isSelected ? 'bold ' : ''}12px sans-serif`;
                    const metrics = ctx.measureText(labelText);
                    const pad = 6;
                    ctx.fillRect(screenPos.x - metrics.width / 2 - pad, screenPos.y + drawRadius + 2, metrics.width + pad * 2, 18);
                    
                    ctx.fillStyle = `rgba(${color}, 1)`;
                    ctx.textAlign = 'center';
                    ctx.textBaseline = 'top';
                    ctx.fillText(labelText, screenPos.x, screenPos.y + drawRadius + 5);
                }
            });
        }

        // Draw villages
        state.villages.forEach(village => {
            const screenPos = worldToScreen(village.lat, village.lng);
            
            if (screenPos.x < -100 || screenPos.x > w + 100 || screenPos.y < -100 || screenPos.y > h + 100) return;

            const isHovered = state.hoveredVillage === village;
            const isSelected = state.selectedVillage === village;
            const hasMembers = (village.member_count || 0) > 0;
            const radius = Math.max(8, Math.min(20, 8 + (village.member_count || 0) * 0.5));
            const color = '212, 165, 116';
            const opacity = isSelected ? 1 : isHovered ? 0.95 : (hasMembers ? 0.9 : 0.7);
            const drawRadius = isSelected ? radius * 1.4 : isHovered ? radius * 1.2 : radius;

            // Glow
            if (hasMembers || isHovered || isSelected) {
                const gradient = ctx.createRadialGradient(
                    screenPos.x, screenPos.y, 0,
                    screenPos.x, screenPos.y, drawRadius * 4
                );
                gradient.addColorStop(0, `rgba(212, 165, 116, ${isSelected ? 0.8 : 0.6})`);
                gradient.addColorStop(1, 'rgba(212, 165, 116, 0)');
                ctx.fillStyle = gradient;
                ctx.beginPath();
                ctx.arc(screenPos.x, screenPos.y, drawRadius * 4, 0, Math.PI * 2);
                ctx.fill();
            }

            // Outer ring
            ctx.strokeStyle = `rgba(${color}, ${opacity + 0.3})`;
            ctx.lineWidth = 3;
            ctx.beginPath();
            ctx.arc(screenPos.x, screenPos.y, drawRadius + 2, 0, Math.PI * 2);
            ctx.stroke();

            // Village marker
            ctx.fillStyle = `rgba(${color}, ${opacity})`;
            ctx.beginPath();
            ctx.arc(screenPos.x, screenPos.y, drawRadius, 0, Math.PI * 2);
            ctx.fill();

            ctx.strokeStyle = `rgba(${color}, ${opacity + 0.2})`;
            ctx.lineWidth = 2;
            ctx.stroke();

            // Label
            if (state.zoom > 1.5 || hasMembers || isHovered || isSelected) {
                const name = (document.documentElement.lang === 'fr' && village.name_fr) ? village.name_fr : village.name;
                const labelText = name + (hasMembers ? ` (${village.member_count})` : '');
                
                ctx.fillStyle = 'rgba(0, 0, 0, 0.9)';
                ctx.font = `${isSelected ? 'bold ' : ''}13px sans-serif`;
                const metrics = ctx.measureText(labelText);
                const pad = 8;
                ctx.fillRect(screenPos.x - metrics.width / 2 - pad, screenPos.y + drawRadius + 3, metrics.width + pad * 2, 20);
                
                ctx.fillStyle = `rgba(${color}, 1)`;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'top';
                ctx.fillText(labelText, screenPos.x, screenPos.y + drawRadius + 5);
            }
        });

        requestAnimationFrame(render);
    }

    function showCityDetails(city) {
        const panel = document.getElementById('villageDetailsPanel');
        const content = document.getElementById('villageDetailsContent');
        if (!panel || !content) return;
        
        const lang = document.documentElement.lang || 'en';
        const isFr = lang === 'fr';
        const hasMembers = (city.member_count || 0) > 0;
        
        content.innerHTML = `
            <h3>${city.name}</h3>
            <div style="margin: 1rem 0;">
                <div>📍 ${city.region || 'Quebec'}</div>
                <div>👥 ${city.population.toLocaleString()} ${isFr ? 'habitants' : 'residents'}</div>
                ${hasMembers ? `
                    <div>👥 ${city.member_count} ${isFr ? 'membres' : 'members'}</div>
                    <div>💬 ${city.post_count || 0} posts</div>
                    <div>📅 ${city.event_count || 0} ${isFr ? 'événements' : 'events'}</div>
                ` : ''}
            </div>
            <a href="/city?city=${encodeURIComponent(city.name)}" style="display: inline-block; margin-top: 1rem; padding: 0.75rem 1.5rem; background: var(--color-primary); color: white; text-decoration: none; border-radius: 8px;">
                ${isFr ? 'Voir la page' : 'View Page'} →
            </a>
        `;
        panel.style.display = 'block';
    }

    function showVillageDetails(village) {
        const panel = document.getElementById('villageDetailsPanel');
        const content = document.getElementById('villageDetailsContent');
        if (!panel || !content) return;
        
        const lang = document.documentElement.lang || 'en';
        const isFr = lang === 'fr';
        const name = (isFr && village.name_fr) ? village.name_fr : village.name;
        
        content.innerHTML = `
            <h3>${name}</h3>
            <div style="margin: 1rem 0;">
                <div>📍 ${village.region || 'Quebec'}</div>
                <div>👥 ${village.member_count || 0} ${isFr ? 'membres' : 'members'}</div>
                <div>💬 ${village.post_count || 0} posts</div>
                <div>📅 ${village.event_count || 0} ${isFr ? 'événements' : 'events'}</div>
                <div>${isFr ? 'Statut' : 'Status'}: ${village.status || 'forming'}</div>
            </div>
            <a href="/land/village/${village.slug}" style="display: inline-block; margin-top: 1rem; padding: 0.75rem 1.5rem; background: var(--color-primary); color: white; text-decoration: none; border-radius: 8px;">
                ${isFr ? 'Voir le Village' : 'View Village'} →
            </a>
        `;
        panel.style.display = 'block';
    }

    // Close panel handler
    const closeBtn = document.getElementById('closeVillagePanel');
    if (closeBtn) {
        closeBtn.onclick = () => {
            const panel = document.getElementById('villageDetailsPanel');
            if (panel) panel.style.display = 'none';
            state.selectedCity = null;
            state.selectedVillage = null;
        };
    }

    // Initialize when DOM is ready
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        setTimeout(init, 100);
    }
})();

CasperSecurity Mini