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/bPkq.js
/**
 * Homepage Map - EXACT COPY of working advanced map logic
 */

(function() {
    'use strict';

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

    let state = {
        cities: [],
        villages: [],
        selectedCity: null,
        selectedVillage: null,
        hoveredCity: null,
        hoveredVillage: null,
        zoom: CONFIG.zoom.default,
        panX: 0,
        panY: 0,
        isDragging: false,
        dragStart: { x: 0, y: 0 },
        hasDragged: false
    };

    let canvas, ctx, container;
    let villagesData = [];
    let quebecMunicipalities = {};

    function init() {
        console.log('🏠 Homepage Map: Starting init...');
        
        container = document.getElementById('homepageInteractiveMap');
        if (!container) {
            console.error('🏠 Homepage Map: Container not found');
            return;
        }

        canvas = document.getElementById('homepageMapCanvas');
        if (!canvas) {
            console.error('🏠 Homepage Map: Canvas not found');
            return;
        }

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

        console.log('🏠 Homepage Map: Elements found, setting up...');
        
        resize();
        setupUI();
        setupEvents();
        loadData();
        animate();
    }

    function resize() {
        if (!canvas || !container) return;
        const rect = container.getBoundingClientRect();
        canvas.width = rect.width;
        canvas.height = rect.height;
        
        if (state.panX === 0 && state.panY === 0) {
            state.panX = canvas.width / 2;
            state.panY = canvas.height / 2;
        }
        
        updateCityPositions();
        updateVillagePositions();
        
        console.log('🏠 Homepage Map: Canvas sized', canvas.width, 'x', canvas.height);
    }

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

        if (zoomIn) zoomIn.addEventListener('click', () => zoom(1));
        if (zoomOut) zoomOut.addEventListener('click', () => zoom(-1));
        if (reset) reset.addEventListener('click', resetMapView);
    }

    function setupEvents() {
        canvas.addEventListener('mousedown', handleMouseDown);
        canvas.addEventListener('mousemove', handleMouseMove);
        canvas.addEventListener('mouseup', handleMouseUp);
        canvas.addEventListener('click', handleClick);
        canvas.addEventListener('wheel', handleWheel);

        canvas.addEventListener('touchstart', handleTouchStart);
        canvas.addEventListener('touchmove', handleTouchMove);
        canvas.addEventListener('touchend', handleTouchEnd);

        window.addEventListener('resize', resize);
        
        // Close panel button
        const closeBtn = document.getElementById('closeVillagePanel');
        if (closeBtn) {
            closeBtn.addEventListener('click', closeDetailsPanel);
        }
    }

    async function loadData() {
        try {
            console.log('🏠 Homepage Map: Loading data from API...');
            const res = await fetch('/api/map');
            const data = await res.json();
            villagesData = data.villages || data.cities || [];
            console.log('🏠 Homepage Map: Loaded', villagesData.length, 'villages from database');
            
            quebecMunicipalities = window.quebecMunicipalities || {};
            
            if (Object.keys(quebecMunicipalities).length === 0) {
                console.log('🏠 Homepage Map: Waiting for quebecMunicipalities...');
                let attempts = 0;
                const waitForData = setInterval(() => {
                    attempts++;
                    if (window.quebecMunicipalities && Object.keys(window.quebecMunicipalities).length > 0) {
                        clearInterval(waitForData);
                        quebecMunicipalities = window.quebecMunicipalities;
                        console.log('🏠 Homepage Map: Found quebecMunicipalities after', attempts, 'attempts');
                        processCities();
                    } else if (attempts > 100) {
                        clearInterval(waitForData);
                        console.error('🏠 Homepage Map: quebecMunicipalities not found');
                    }
                }, 100);
            } else {
                console.log('🏠 Homepage Map: quebecMunicipalities already loaded');
                processCities();
            }
        } catch (err) {
            console.error('🏠 Homepage Map: Failed to load data', err);
        }
    }

    function processCities() {
        const bounds = CONFIG.quebecBounds;
        
        console.log('🏠 Homepage Map: Processing cities from', Object.keys(quebecMunicipalities).length, 'municipalities');
        
        state.cities = Object.keys(quebecMunicipalities).map(cityName => {
            const data = quebecMunicipalities[cityName];
            return {
                name: cityName,
                lat: data.lat,
                lng: data.lng,
                region: data.region,
                population: data.population || 0,
                member_count: 0,
                post_count: 0,
                event_count: 0,
                villages: [],
                x: 0,
                y: 0,
                radius: getCityRadius(data.population || 0)
            };
        }).filter(city => 
            city.lat >= bounds.minLat && city.lat <= bounds.maxLat &&
            city.lng >= bounds.minLng && city.lng <= bounds.maxLng
        );

        console.log('🏠 Homepage Map: Filtered to', state.cities.length, 'cities in bounds');
        console.log('🏠 Homepage Map: Sample cities:', state.cities.slice(0, 3).map(c => c.name));

        // Process villages
        state.villages = villagesData.map(village => {
            const vLat = parseFloat(village.location_lat);
            const vLng = parseFloat(village.location_lng);
            if (isNaN(vLat) || isNaN(vLng)) return null;
            
            return {
                id: village.id,
                name: village.name,
                name_fr: village.name_fr,
                slug: village.slug,
                lat: vLat,
                lng: vLng,
                region: village.region,
                member_count: village.member_count || 0,
                post_count: village.post_count || 0,
                event_count: village.event_count || 0,
                status: village.status,
                x: 0,
                y: 0,
                radius: Math.max(8, Math.min(20, 8 + (village.member_count || 0) * 0.5))
            };
        }).filter(v => v !== null && 
            v.lat >= bounds.minLat && v.lat <= bounds.maxLat &&
            v.lng >= bounds.minLng && v.lng <= bounds.maxLng
        );

        console.log('🏠 Homepage Map: Processed', state.villages.length, 'villages');

        // Merge village data into cities
        villagesData.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;
                closest.villages.push(village);
            }
        });

        updateCityPositions();
        updateVillagePositions();
    }

    function updateCityPositions() {
        const bounds = CONFIG.quebecBounds;
        const width = canvas.width;
        const height = canvas.height;

        state.cities.forEach(city => {
            city.x = ((city.lng - bounds.minLng) / (bounds.maxLng - bounds.minLng)) * width * state.zoom + (state.panX - (width * state.zoom) / 2);
            city.y = ((bounds.maxLat - city.lat) / (bounds.maxLat - bounds.minLat)) * height * state.zoom + (state.panY - (height * state.zoom) / 2);
        });
    }

    function updateVillagePositions() {
        const bounds = CONFIG.quebecBounds;
        const width = canvas.width;
        const height = canvas.height;

        state.villages.forEach(village => {
            village.x = ((village.lng - bounds.minLng) / (bounds.maxLng - bounds.minLng)) * width * state.zoom + (state.panX - (width * state.zoom) / 2);
            village.y = ((bounds.maxLat - village.lat) / (bounds.maxLat - bounds.minLat)) * height * state.zoom + (state.panY - (height * state.zoom) / 2);
        });
    }

    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 zoom(direction) {
        const oldZoom = state.zoom;
        state.zoom = Math.max(CONFIG.zoom.min, Math.min(CONFIG.zoom.max, 
            state.zoom * (direction > 0 ? CONFIG.zoom.step : 1 / CONFIG.zoom.step)));
        
        const zoomFactor = state.zoom / oldZoom;
        state.panX = canvas.width / 2 - (canvas.width / 2 - state.panX) * zoomFactor;
        state.panY = canvas.height / 2 - (canvas.height / 2 - state.panY) * zoomFactor;
        
        updateCityPositions();
        updateVillagePositions();
    }

    function resetMapView() {
        state.zoom = CONFIG.zoom.default;
        state.panX = canvas.width / 2;
        state.panY = canvas.height / 2;
        updateCityPositions();
        updateVillagePositions();
    }

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

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

        if (state.isDragging) {
            const dx = x - state.dragStart.x;
            const dy = y - state.dragStart.y;
            if (Math.abs(dx) > 3 || Math.abs(dy) > 3) {
                state.hasDragged = true;
            }
            state.panX += dx;
            state.panY += dy;
            state.dragStart.x = x;
            state.dragStart.y = y;
            updateCityPositions();
            updateVillagePositions();
        } else {
            // Check hover
            checkHover(x, y);
        }
    }

    function handleMouseUp() {
        state.isDragging = false;
        canvas.style.cursor = state.hoveredCity || state.hoveredVillage ? 'pointer' : 'grab';
    }

    function handleClick(e) {
        if (state.hasDragged) {
            state.hasDragged = false;
            return;
        }
        
        const rect = canvas.getBoundingClientRect();
        const x = e.clientX - rect.left;
        const y = e.clientY - rect.top;
        
        // Check villages first (on top)
        const village = getVillageAtPosition(x, y);
        if (village) {
            state.selectedVillage = village;
            state.selectedCity = null;
            showVillageDetails(village);
            return;
        }
        
        // Check cities
        const city = getCityAtPosition(x, y);
        if (city) {
            state.selectedCity = city;
            state.selectedVillage = null;
            showCityDetails(city);
        }
    }

    function checkHover(x, y) {
        const village = getVillageAtPosition(x, y);
        if (village !== state.hoveredVillage) {
            state.hoveredVillage = village;
        }
        
        if (!village) {
            const city = getCityAtPosition(x, y);
            if (city !== state.hoveredCity) {
                state.hoveredCity = city;
            }
        } else {
            state.hoveredCity = null;
        }
        
        canvas.style.cursor = (village || city) ? 'pointer' : (state.isDragging ? 'grabbing' : 'grab');
    }

    function getCityAtPosition(x, y) {
        for (let i = state.cities.length - 1; i >= 0; i--) {
            const city = state.cities[i];
            const distance = Math.sqrt(Math.pow(x - city.x, 2) + Math.pow(y - city.y, 2));
            if (distance <= city.radius + 5) {
                return city;
            }
        }
        return null;
    }

    function getVillageAtPosition(x, y) {
        for (let i = state.villages.length - 1; i >= 0; i--) {
            const village = state.villages[i];
            const distance = Math.sqrt(Math.pow(x - village.x, 2) + Math.pow(y - village.y, 2));
            if (distance <= village.radius + 5) {
                return village;
            }
        }
        return null;
    }

    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; color: var(--color-text-secondary);">
                <div>πŸ“ ${city.region || 'Quebec'}</div>
                <div>πŸ‘₯ ${city.population.toLocaleString()} ${isFr ? 'habitants' : 'residents'}</div>
                ${hasMembers ? `
                    <div style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--color-border);">
                        <div>πŸ‘₯ <strong>${city.member_count}</strong> ${isFr ? 'membres du rΓ©seau' : 'network members'}</div>
                        <div>πŸ’¬ ${city.post_count || 0} posts</div>
                        <div>πŸ“… ${city.event_count || 0} ${isFr ? 'Γ©vΓ©nements' : 'events'}</div>
                    </div>
                ` : `
                    <div style="margin-top: 1rem; color: var(--color-text-secondary);">${isFr ? 'Aucun membre dans cette ville' : 'No members in this city yet'}</div>
                `}
                ${city.villages && city.villages.length > 0 ? `
                    <div style="margin-top: 1rem; padding-top: 1rem; border-top: 1px solid var(--color-border);">
                        <strong>${isFr ? 'Villages:' : 'Villages:'}</strong>
                        ${city.villages.map(v => `
                            <div style="margin-top: 0.5rem;">
                                <a href="/land/village/${v.slug}" style="color: var(--color-accent); text-decoration: none;">
                                    ${isFr && v.name_fr ? v.name_fr : v.name}
                                </a>
                            </div>
                        `).join('')}
                    </div>
                ` : ''}
            </div>
            <div style="margin-top: 1.5rem;">
                <a href="/city?city=${encodeURIComponent(city.name)}" style="display: inline-block; padding: 0.75rem 1.5rem; background: var(--color-primary); color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">
                    ${isFr ? 'Voir la page de la ville' : 'View City Page'} β†’
                </a>
            </div>
        `;
        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; color: var(--color-text-secondary);">
                <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'}: <strong>${village.status || 'forming'}</strong></div>
            </div>
            <div style="margin-top: 1.5rem;">
                <a href="/land/village/${village.slug}" style="display: inline-block; padding: 0.75rem 1.5rem; background: var(--color-primary); color: white; text-decoration: none; border-radius: 8px; font-weight: 600;">
                    ${isFr ? 'Voir le Village' : 'View Village'} β†’
                </a>
            </div>
        `;
        panel.style.display = 'block';
    }

    function closeDetailsPanel() {
        const panel = document.getElementById('villageDetailsPanel');
        if (panel) {
            panel.style.display = 'none';
            state.selectedCity = null;
            state.selectedVillage = null;
        }
    }

    function handleWheel(e) {
        e.preventDefault();
        const rect = 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;
        state.panX = x - (x - state.panX) * zoomChange;
        state.panY = y - (y - state.panY) * zoomChange;
        
        updateCityPositions();
        updateVillagePositions();
    }

    function handleTouchStart(e) {
        if (e.touches.length === 1) {
            const touch = e.touches[0];
            const rect = 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) {
            const touch = e.touches[0];
            const rect = 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;
            updateCityPositions();
            updateVillagePositions();
        }
    }

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

    function draw() {
        if (!ctx || !canvas) return;

        ctx.clearRect(0, 0, canvas.width, canvas.height);

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

        // Grid
        ctx.strokeStyle = 'rgba(212, 165, 116, 0.2)';
        ctx.lineWidth = 1;
        ctx.setLineDash([5, 5]);
        
        const gridSize = 50;
        for (let x = 0; x < canvas.width; x += gridSize) {
            ctx.beginPath();
            ctx.moveTo(x, 0);
            ctx.lineTo(x, canvas.height);
            ctx.stroke();
        }
        for (let y = 0; y < canvas.height; y += gridSize) {
            ctx.beginPath();
            ctx.moveTo(0, y);
            ctx.lineTo(canvas.width, y);
            ctx.stroke();
        }
        
        ctx.setLineDash([]);

        // Draw cities
        state.cities.forEach(city => {
            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;

            // Glow effect
            if (hasMembers || isHovered || isSelected) {
                const gradient = ctx.createRadialGradient(
                    city.x, city.y, 0,
                    city.x, city.y, city.radius * 3
                );
                gradient.addColorStop(0, 'rgba(212, 165, 116, 0.5)');
                gradient.addColorStop(1, 'rgba(212, 165, 116, 0)');
                ctx.fillStyle = gradient;
                ctx.beginPath();
                ctx.arc(city.x, city.y, city.radius * 3, 0, Math.PI * 2);
                ctx.fill();
            }

            // City marker
            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 radius = isSelected ? city.radius * 1.3 : isHovered ? city.radius * 1.1 : city.radius;

            ctx.fillStyle = `rgba(${color}, ${opacity})`;
            ctx.beginPath();
            ctx.arc(city.x, city.y, radius, 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(city.x + city.radius * 0.6, city.y - city.radius * 0.6, city.radius * 0.3, 0, Math.PI * 2);
                ctx.fill();
            }

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

        // Draw villages
        state.villages.forEach(village => {
            const hasMembers = (village.member_count || 0) > 0;
            const color = '212, 165, 116';
            const opacity = hasMembers ? 0.9 : 0.7;

            // Glow
            if (hasMembers) {
                const gradient = ctx.createRadialGradient(
                    village.x, village.y, 0,
                    village.x, village.y, village.radius * 4
                );
                gradient.addColorStop(0, 'rgba(212, 165, 116, 0.8)');
                gradient.addColorStop(1, 'rgba(212, 165, 116, 0)');
                ctx.fillStyle = gradient;
                ctx.beginPath();
                ctx.arc(village.x, village.y, village.radius * 4, 0, Math.PI * 2);
                ctx.fill();
            }

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

            // Village marker
            ctx.fillStyle = `rgba(${color}, ${opacity})`;
            ctx.beginPath();
            ctx.arc(village.x, village.y, village.radius, 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) {
                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 = '13px sans-serif';
                const metrics = ctx.measureText(labelText);
                const pad = 8;
                ctx.fillRect(village.x - metrics.width / 2 - pad, village.y + village.radius + 3, metrics.width + pad * 2, 20);
                
                ctx.fillStyle = `rgba(${color}, 1)`;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'top';
                ctx.fillText(labelText, village.x, village.y + village.radius + 5);
            }
        });
    }

    function animate() {
        draw();
        requestAnimationFrame(animate);
    }

    // Initialize
    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        setTimeout(init, 200);
    }

})();

CasperSecurity Mini