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/rvm5.js
/**
 * Fresh Interactive Map - Clean Start
 */

(function() {
    'use strict';

    const BOUNDS = {
        minLat: 45.0,
        maxLat: 51.0,
        minLng: -80.0,
        maxLng: -66.0
    };

    let map = {
        canvas: null,
        ctx: null,
        container: null,
        cities: [],
        villages: [],
        zoom: 1.2,
        panX: 0,
        panY: 0,
        dragging: false,
        dragStart: { x: 0, y: 0 }
    };

    function init() {
        map.container = document.getElementById('homepageInteractiveMap');
        map.canvas = document.getElementById('homepageMapCanvas');
        if (!map.container || !map.canvas) {
            console.log('Map: Container or canvas not found');
            return;
        }

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

        setupCanvas();
        setupControls();
        loadCities();
        loadVillages();
        // Don't call draw() here - wait for data to load
    }

    function setupCanvas() {
        // Wait for container to have dimensions
        const rect = map.container.getBoundingClientRect();
        if (rect.width === 0 || rect.height === 0) {
            // Container not ready yet, try again
            requestAnimationFrame(setupCanvas);
            return;
        }
        
        map.canvas.width = rect.width;
        map.canvas.height = rect.height;
        map.panX = map.canvas.width / 2;
        map.panY = map.canvas.height / 2;
        
        console.log('Map: Canvas initialized', map.canvas.width, 'x', map.canvas.height);

        map.canvas.addEventListener('mousedown', (e) => {
            map.dragging = true;
            const rect = map.canvas.getBoundingClientRect();
            map.dragStart.x = e.clientX - rect.left;
            map.dragStart.y = e.clientY - rect.top;
        });

        map.canvas.addEventListener('mousemove', (e) => {
            if (map.dragging) {
                const rect = map.canvas.getBoundingClientRect();
                const x = e.clientX - rect.left;
                const y = e.clientY - rect.top;
                map.panX += x - map.dragStart.x;
                map.panY += y - map.dragStart.y;
                map.dragStart.x = x;
                map.dragStart.y = y;
                draw();
            }
        });

        map.canvas.addEventListener('mouseup', () => {
            map.dragging = false;
        });

        map.canvas.addEventListener('wheel', (e) => {
            e.preventDefault();
            const rect = map.canvas.getBoundingClientRect();
            const x = e.clientX - rect.left;
            const y = e.clientY - rect.top;
            const oldZoom = map.zoom;
            map.zoom *= e.deltaY > 0 ? 0.9 : 1.1;
            map.zoom = Math.max(0.5, Math.min(5, map.zoom));
            const zoomChange = map.zoom / oldZoom;
            map.panX = x - (x - map.panX) * zoomChange;
            map.panY = y - (y - map.panY) * zoomChange;
            draw();
        });

        window.addEventListener('resize', () => {
            const rect = map.container.getBoundingClientRect();
            if (rect.width > 0 && rect.height > 0) {
                map.canvas.width = rect.width;
                map.canvas.height = rect.height;
                draw();
            }
        });
    }

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

        if (zoomIn) {
            zoomIn.onclick = () => {
                map.zoom = Math.min(5, map.zoom * 1.2);
                draw();
            };
        }

        if (zoomOut) {
            zoomOut.onclick = () => {
                map.zoom = Math.max(0.5, map.zoom / 1.2);
                draw();
            };
        }

        if (reset) {
            reset.onclick = () => {
                map.zoom = 1.2;
                map.panX = map.canvas.width / 2;
                map.panY = map.canvas.height / 2;
                draw();
            };
        }
    }

    function loadCities(attempts = 0) {
        if (!window.quebecMunicipalities) {
            if (attempts < 50) {
                setTimeout(() => loadCities(attempts + 1), 100);
            } else {
                console.error('Map: quebecMunicipalities not found after 5 seconds');
            }
            return;
        }

        map.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) {
                map.cities.push({
                    name: name,
                    lat: lat,
                    lng: lng,
                    population: parseInt(data.population) || 0
                });
            }
        }
        
        console.log('Map: Loaded', map.cities.length, 'cities');
        
        // Only draw if canvas is ready
        if (map.canvas && map.canvas.width > 0 && map.canvas.height > 0) {
            requestAnimationFrame(draw);
        }
    }

    function loadVillages() {
        fetch('/api/map')
            .then(res => res.json())
            .then(data => {
                // Use villages from the unified API endpoint
                const villages = data.villages || data.cities || [];
                map.villages = villages
                    .map(v => {
                        // Use location_lat/location_lng if available, otherwise lat/lng
                        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,
                            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 >= BOUNDS.minLat && v.lat <= BOUNDS.maxLat &&
                        v.lng >= BOUNDS.minLng && v.lng <= BOUNDS.maxLng
                    );
                console.log('Map: Loaded', map.villages.length, 'villages from database');
                console.log('Sample villages:', map.villages.slice(0, 5).map(v => v.name));
                
                // Only draw if canvas is ready
                if (map.canvas && map.canvas.width > 0 && map.canvas.height > 0) {
                    requestAnimationFrame(draw);
                }
            })
            .catch((err) => {
                console.error('Map: Could not load villages', err);
            });
    }

    function latLngToXY(lat, lng) {
        const x = ((lng - BOUNDS.minLng) / (BOUNDS.maxLng - BOUNDS.minLng)) * map.canvas.width * map.zoom + (map.panX - (map.canvas.width * map.zoom) / 2);
        const y = ((BOUNDS.maxLat - lat) / (BOUNDS.maxLat - BOUNDS.minLat)) * map.canvas.height * map.zoom + (map.panY - (map.canvas.height * map.zoom) / 2);
        return { x, y };
    }

    function draw() {
        if (!map.ctx || !map.canvas) {
            console.log('Map: Cannot draw - ctx or canvas missing');
            return;
        }

        const w = map.canvas.width;
        const h = map.canvas.height;
        
        // Validate canvas dimensions
        if (w === 0 || h === 0) {
            console.log('Map: Canvas not sized yet', w, 'x', h);
            return;
        }

        const ctx = map.ctx;

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

        // Background
        ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--color-bg-light').trim() || '#1a1a1a';
        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 (map.cities.length === 0) {
            console.log('Map: No cities to draw');
        }
        
        map.cities.forEach(city => {
            const pos = latLngToXY(city.lat, city.lng);
            if (isNaN(pos.x) || isNaN(pos.y)) {
                console.log('Map: Invalid position for city', city.name, pos);
                return;
            }
            if (pos.x < -50 || pos.x > w + 50 || pos.y < -50 || pos.y > h + 50) return;

            const radius = city.population > 100000 ? 8 : city.population > 50000 ? 5 : 3;
            const color = city.population > 100000 ? '#d4a574' : city.population > 50000 ? '#8bc34a' : '#6496c8';

            ctx.fillStyle = color;
            ctx.beginPath();
            ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
            ctx.fill();

            ctx.strokeStyle = color;
            ctx.lineWidth = 2;
            ctx.stroke();

            if (city.population > 50000 && map.zoom > 1.5) {
                ctx.fillStyle = getComputedStyle(document.documentElement).getPropertyValue('--color-text').trim() || '#f5f5f5';
                ctx.font = '12px sans-serif';
                ctx.textAlign = 'center';
                ctx.fillText(city.name, pos.x, pos.y + radius + 5);
            }
        });

        // Draw villages - show ALL villages from database
        map.villages.forEach(village => {
            const pos = latLngToXY(village.lat, village.lng);
            if (pos.x < -50 || pos.x > w + 50 || pos.y < -50 || pos.y > h + 50) return;

            const isActive = village.status === 'active';
            const hasMembers = (village.member_count || 0) > 0;
            // Size based on member count, minimum 8px
            const radius = Math.max(8, Math.min(20, 8 + (village.member_count || 0) * 0.5));
            // Gold color for villages (especially those with members)
            const color = hasMembers ? '#d4a574' : (isActive ? '#ffd700' : '#8bc34a');

            // Glow effect for villages with members
            if (hasMembers || isActive) {
                const gradient = ctx.createRadialGradient(
                    pos.x, pos.y, 0,
                    pos.x, pos.y, radius * 3
                );
                gradient.addColorStop(0, `rgba(212, 165, 116, ${hasMembers ? 0.6 : 0.4})`);
                gradient.addColorStop(1, 'rgba(212, 165, 116, 0)');
                ctx.fillStyle = gradient;
                ctx.beginPath();
                ctx.arc(pos.x, pos.y, radius * 3, 0, Math.PI * 2);
                ctx.fill();
            }

            // Village marker
            ctx.fillStyle = color;
            ctx.beginPath();
            ctx.arc(pos.x, pos.y, radius, 0, Math.PI * 2);
            ctx.fill();

            ctx.strokeStyle = color;
            ctx.lineWidth = hasMembers ? 3 : (isActive ? 3 : 2);
            ctx.stroke();

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

            // Show village name when zoomed in or if it has members
            if (map.zoom > 1.5 || hasMembers || isActive) {
                const name = (document.documentElement.lang === 'fr' && village.name_fr) ? village.name_fr : village.name;
                const labelText = name + (hasMembers ? ` (${village.member_count})` : '');
                
                // Background for text
                ctx.fillStyle = 'rgba(0, 0, 0, 0.8)';
                ctx.font = hasMembers ? 'bold 13px sans-serif' : (isActive ? 'bold 12px sans-serif' : '12px sans-serif');
                const metrics = ctx.measureText(labelText);
                const pad = 6;
                ctx.fillRect(pos.x - metrics.width / 2 - pad, pos.y + radius + 2, metrics.width + pad * 2, 18);
                
                // Text
                ctx.fillStyle = color;
                ctx.textAlign = 'center';
                ctx.textBaseline = 'top';
                ctx.fillText(labelText, pos.x, pos.y + radius + 5);
            }
        });
    }

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

CasperSecurity Mini