OpenSnitch Pro

Enhanced location finder with timezone and regional information

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         OpenSnitch Pro
// @namespace    https://openguessr.com/
// @version      1.0.3
// @description  Enhanced location finder with timezone and regional information
// @author       Westagger
// @license      GNU GPLv3
// @match        https://openguessr.com/*
// @grant        GM_xmlhttpRequest
// @icon         https://raw.githubusercontent.com/Westagger/OpenSnitch-Pro/refs/heads/main/assets/osp-logo.png
// @supportURL   https://github.com/Westagger/OpenSnitch-Pro/issues
// ==/UserScript==

(function () {
    'use strict';

    let popup = null;
    let lastLocation = null;
    let updateTimeout = null;
    let isMinimized = false;
    let lastUpdateTime = 0;
    let processingLocation = false;
    const COOLDOWN_TIME = 100;

        function createPopup() {
        if (popup && popup.parentNode) {
            document.body.removeChild(popup);
        }

        popup = document.createElement('div');
        popup.style.cssText = `
            position: fixed;
            top: 20px;
            right: 20px;
            background: linear-gradient(135deg, #1a1a1a 0%, #2d2d2d 100%);
            color: #ffffff;
            border-radius: 10px;
            z-index: 10000;
            font-family: 'Segoe UI', Arial, sans-serif;
            min-width: 250px;
            max-width: 350px;
            box-shadow: 0 4px 15px rgba(0,0,0,0.3);
            border: 1px solid #3d3d3d;
            transition: all 0.3s ease;
        `;

        const titleBar = document.createElement('div');
        titleBar.style.cssText = `
            display: flex;
            justify-content: space-between;
            align-items: center;
            padding: 10px 15px;
            background: linear-gradient(135deg, #2d2d2d 0%, #1a1a1a 100%);
            border-bottom: 1px solid #3d3d3d;
            border-radius: 10px 10px 0 0;
        `;

        const titleText = document.createElement('span');
        titleText.textContent = '🌍 OpenSnitch Pro';
        titleText.style.cssText = `
            font-weight: bold;
            font-size: 14px;
            color: #fff;
        `;

        const minimizeBtn = document.createElement('button');
        minimizeBtn.innerHTML = isMinimized ? '+' : '−';
        minimizeBtn.style.cssText = `
            background: none;
            border: none;
            color: #fff;
            font-size: 18px;
            cursor: pointer;
            padding: 0 5px;
            line-height: 20px;
            transition: opacity 0.2s;
        `;
        minimizeBtn.onmouseover = () => minimizeBtn.style.opacity = '0.7';
        minimizeBtn.onmouseout = () => minimizeBtn.style.opacity = '1';
        minimizeBtn.onclick = (e) => {
            e.stopPropagation();
            toggleMinimize();
        };

        titleBar.appendChild(titleText);
        titleBar.appendChild(minimizeBtn);
        popup.appendChild(titleBar);

        const contentContainer = document.createElement('div');
        contentContainer.id = 'opensnitch-content';
        contentContainer.style.cssText = `
            padding: 20px;
            transition: all 0.3s ease;
            ${isMinimized ? 'display: none;' : ''}
        `;
        popup.appendChild(contentContainer);

        document.body.appendChild(popup);
        return contentContainer;
    }

    function toggleMinimize() {
        const content = document.getElementById('opensnitch-content');
        const minimizeBtn = popup.querySelector('button');
        isMinimized = !isMinimized;

        if (isMinimized) {
            content.style.display = 'none';
            minimizeBtn.innerHTML = '+';
        } else {
            content.style.display = 'block';
            minimizeBtn.innerHTML = '−';
        }
    }

    function formatCountryName(countryName) {
        countryName = countryName
            .replace(/ \([^)]+\)/g, '')
            .replace(/(the|The)$/, '')
            .replace(/^(the|The) /, '')
            .trim();

        const nameMap = { //Shit, this took time
            'United States of America': 'United States',
            'Russian Federation': 'Russia',
            'The Netherlands': 'Netherlands',
            'Czech Republic': 'Czechia',
            'Republic of Korea': 'South Korea',
            'Democratic People\'s Republic of Korea': 'North Korea',
            'People\'s Republic of China': 'China',
            'Kingdom of Thailand': 'Thailand',
            'Republic of India': 'India',
            'Socialist Republic of Vietnam': 'Vietnam',
            'Kingdom of Cambodia': 'Cambodia',
            'Lao People\'s Democratic Republic': 'Laos',
            'Republic of the Philippines': 'Philippines',
            'Republic of Indonesia': 'Indonesia',
            'Republic of South Africa': 'South Africa',
            'United Kingdom of Great Britain and Northern Ireland': 'United Kingdom',
            'Federal Republic of Germany': 'Germany',
            'French Republic': 'France',
            'Kingdom of Spain': 'Spain',
            'Kingdom of Sweden': 'Sweden',
            'Kingdom of Norway': 'Norway',
            'Republic of Finland': 'Finland',
            'Republic of Poland': 'Poland',
            'Italian Republic': 'Italy',
            'Republic of Turkey': 'Turkey',
            'Hellenic Republic': 'Greece',
            'Portuguese Republic': 'Portugal',
            'Kingdom of Belgium': 'Belgium',
            'Swiss Confederation': 'Switzerland',
            'Republic of Austria': 'Austria',
            'Kingdom of Denmark': 'Denmark',
            'Republic of Hungary': 'Hungary',
            'Slovak Republic': 'Slovakia',
            'Republic of Bulgaria': 'Bulgaria',
            'Republic of Croatia': 'Croatia',
            'Republic of Estonia': 'Estonia',
            'Republic of Latvia': 'Latvia',
            'Republic of Lithuania': 'Lithuania',
            'Republic of Slovenia': 'Slovenia',
            'State of Israel': 'Israel',
            'Republic of Kazakhstan': 'Kazakhstan',
            'Republic of Belarus': 'Belarus',
            'Republic of Moldova': 'Moldova',
            'Republic of Azerbaijan': 'Azerbaijan',
            'Georgia': 'Georgia',
            'Republic of Armenia': 'Armenia',
            'Republic of Uzbekistan': 'Uzbekistan',
            'Kyrgyz Republic': 'Kyrgyzstan',
            'Republic of Tajikistan': 'Tajikistan',
            'Turkmenistan': 'Turkmenistan',
            'Islamic Republic of Pakistan': 'Pakistan',
            'People\'s Republic of Bangladesh': 'Bangladesh',
            'Democratic Socialist Republic of Sri Lanka': 'Sri Lanka',
            'Republic of the Union of Myanmar': 'Myanmar',
            'Kingdom of Saudi Arabia': 'Saudi Arabia',
            'United Arab Emirates': 'UAE',
            'Sultanate of Oman': 'Oman',
            'State of Qatar': 'Qatar',
            'State of Kuwait': 'Kuwait',
            'Kingdom of Bahrain': 'Bahrain',
            'Lebanese Republic': 'Lebanon',
            'Hashemite Kingdom of Jordan': 'Jordan',
            'Arab Republic of Egypt': 'Egypt',
            'Republic of Iraq': 'Iraq',
            'Islamic Republic of Iran': 'Iran',
            'Republic of Yemen': 'Yemen',
            'Syrian Arab Republic': 'Syria',
            'Islamic Republic of Afghanistan': 'Afghanistan',
            'Republic of Cyprus': 'Cyprus',
            'Mongolia': 'Mongolia',
            'Republic of Singapore': 'Singapore',
            'Republic of Albania': 'Albania',
            'Bosnia and Herzegovina': 'Bosnia',
            'Republic of Serbia': 'Serbia',
            'Republic of Kosovo': 'Kosovo',
            'Republic of North Macedonia': 'North Macedonia',
            'Republic of Montenegro': 'Montenegro',
            'Republic of Iceland': 'Iceland',
            'Grand Duchy of Luxembourg': 'Luxembourg',
            'Republic of Malta': 'Malta',
            'Republic of Paraguay': 'Paraguay',
            'Oriental Republic of Uruguay': 'Uruguay',
            'Republic of Chile': 'Chile',
            'Plurinational State of Bolivia': 'Bolivia',
            'Republic of Ecuador': 'Ecuador',
            'Republic of Colombia': 'Colombia',
            'Bolivarian Republic of Venezuela': 'Venezuela',
            'Republic of Panama': 'Panama',
            'Republic of Costa Rica': 'Costa Rica',
            'Republic of Nicaragua': 'Nicaragua',
            'Republic of Honduras': 'Honduras',
            'Republic of El Salvador': 'El Salvador',
            'Republic of Guatemala': 'Guatemala',
            'Dominican Republic': 'Dominican Republic',
            'Republic of Haiti': 'Haiti',
            'Jamaica': 'Jamaica',
            'Republic of Cuba': 'Cuba',
            'Republic of Tunisia': 'Tunisia',
            'People\'s Democratic Republic of Algeria': 'Algeria',
            'Kingdom of Morocco': 'Morocco',
            'Republic of Sudan': 'Sudan',
            'Federal Democratic Republic of Ethiopia': 'Ethiopia',
            'Republic of Kenya': 'Kenya',
            'United Republic of Tanzania': 'Tanzania',
            'Republic of Uganda': 'Uganda',
            'Republic of Rwanda': 'Rwanda',
            'Republic of Ghana': 'Ghana',
            'Federal Republic of Nigeria': 'Nigeria',
            'Republic of Senegal': 'Senegal',
            'Republic of Madagascar': 'Madagascar',
            'Republic of Mozambique': 'Mozambique',
            'Republic of Angola': 'Angola',
            'Republic of Namibia': 'Namibia',
            'Republic of Zimbabwe': 'Zimbabwe',
            'Republic of Botswana': 'Botswana',
            'Kingdom of Lesotho': 'Lesotho',
            'Kingdom of Eswatini': 'Eswatini',
            'Republic of Zambia': 'Zambia',
            'Republic of Malawi': 'Malawi',
            'Independent State of Papua New Guinea': 'Papua New Guinea',
            'Solomon Islands': 'Solomon Islands',
            'Republic of Fiji': 'Fiji',
            'Republic of Vanuatu': 'Vanuatu',
            'Independent State of Samoa': 'Samoa'
        };

        return nameMap[countryName] || countryName;
    }

    async function getTimeZoneInfo(lat, lng) {
        const timezoneApis = [
            // TimeDB API
            async () => {
                const response = await fetch(`https://timedb.io/api/TimeZone?lat=${lat}&lng=${lng}`);
                const data = await response.json();
                if (!data.timezone) throw new Error('No timezone data');
                return {
                    timeZone: data.timezone,
                    localTime: new Date().toLocaleString('en-US', {
                        timeZone: data.timezone,
                        hour12: false
                    })
                };
            },
            // TimeZoneDB API
            async () => {
                const response = await fetch(`https://api.timezonedb.com/v2.1/get-time-zone?key=YOUR_API_KEY&format=json&by=position&lat=${lat}&lng=${lng}`);
                const data = await response.json();
                if (data.status !== 'OK') throw new Error('Invalid timezone response');
                return {
                    timeZone: data.zoneName,
                    localTime: new Date(data.formatted).toLocaleString('en-US', {
                        timeZone: data.zoneName,
                        hour12: false
                    })
                };
            },
            // GeoNames API
            async () => {
                const response = await fetch(`http://api.geonames.org/timezoneJSON?lat=${lat}&lng=${lng}&username=YOUR_USERNAME`);
                const data = await response.json();
                if (!data.timezoneId) throw new Error('No timezone data');
                return {
                    timeZone: data.timezoneId,
                    localTime: new Date().toLocaleString('en-US', {
                        timeZone: data.timezoneId,
                        hour12: false
                    })
                };
            }
        ];

        let lastError = null;
        for (const api of timezoneApis) {
            try {
                const timeoutPromise = new Promise((_, reject) => 
                    setTimeout(() => reject(new Error('Timeout')), 3000)
                );

                const result = await Promise.race([
                    api(),
                    timeoutPromise
                ]);

                if (result && result.timeZone) {
                    return result;
                }
            } catch (error) {
                console.error('[OpenSnitch Pro] API attempt failed:', error);
                lastError = error;
                continue;
            }
        }

        // If APIs fail, use approximate calculation
        try {
            const tzOffset = -(new Date().getTimezoneOffset() / 60);
            const approximateOffset = Math.round(lng / 15);
            const hourOffset = approximateOffset;
            
            const timezone = `Etc/GMT${hourOffset >= 0 ? '-' : '+'}${Math.abs(hourOffset)}`;
            return {
                timeZone: timezone,
                localTime: new Date().toLocaleString('en-US', {
                    timeZone: timezone,
                    hour12: false
                })
            };
        } catch (error) {
            console.error('[OpenSnitch Pro] Timezone approximation failed:', error);
            return {
                timeZone: 'UTC',
                localTime: new Date().toLocaleString('en-US', {
                    timeZone: 'UTC',
                    hour12: false
                })
            };
        }
    }

    async function getLocationDetails(lat, lng) {
        const geocodingApis = [
            async () => {
                const response = await fetch(`https://nominatim.openstreetmap.org/reverse?format=json&lat=${lat}&lon=${lng}&zoom=18&addressdetails=1`);
                const data = await response.json();
                const address = data.address;
                return {
                    country: formatCountryName(address.country || 'Unknown Country'),
                    state: address.state || address.region || '',
                    city: address.city || address.town || address.village || address.suburb || 'Unknown City',
                    location: determineLocation(lat, lng, {country: formatCountryName(address.country)})
                };
            },
            async () => {
                const response = await fetch(`https://api.bigdatacloud.net/data/reverse-geocode-client?latitude=${lat}&longitude=${lng}&localityLanguage=en`);
                const data = await response.json();
                return {
                    country: formatCountryName(data.countryName || 'Unknown Country'),
                    state: data.principalSubdivision || '',
                    city: data.locality || 'Unknown City',
                    location: determineLocation(lat, lng, {country: formatCountryName(data.countryName)})
                };
            },
            async () => {
                const response = await fetch(`https://geocode.xyz/${lat},${lng}?json=1`);
                const data = await response.json();
                return {
                    country: formatCountryName(data.country || 'Unknown Country'),
                    state: data.state || '',
                    city: data.city || 'Unknown City',
                    location: determineLocation(lat, lng, {country: formatCountryName(data.country)})
                };
            }
        ];

        try {
            const result = await Promise.race(geocodingApis.map(api => api()));
            return result;
        } catch (error) {
            console.error('[OpenSnitch Pro] All geocoding APIs failed:', error);
            return {
                country: 'Error fetching location',
                state: '',
                city: 'Try again later',
                location: ''
            };
        }
    }

    function createMapsButton(lat, lng) {
        const button = document.createElement('button');
        button.textContent = '📍 Open in Google Maps';
        button.style.cssText = `
            background: #4285f4;
            color: white;
            border: none;
            padding: 8px 12px;
            border-radius: 5px;
            cursor: pointer;
            font-size: 12px;
            margin-top: 10px;
            transition: background-color 0.2s;
            width: 100%;
        `;
        button.onmouseover = () => button.style.background = '#5290f5';
        button.onmouseout = () => button.style.background = '#4285f4';
        button.onclick = () => window.open(`https://www.google.com/maps?q=${lat},${lng}`, '_blank');
        return button;
    }

        function formatContent(locationDetails, timeZoneInfo, lat, lng) {
        const contentDiv = document.createElement('div');
        contentDiv.style.cssText = 'display: flex; flex-direction: column; gap: 10px;';

        const sections = [
            { label: 'Country', value: locationDetails.country },
            { label: 'Region', value: locationDetails.location },
            { label: 'State', value: locationDetails.state },
            { label: 'City', value: locationDetails.city },
            { label: 'Time Zone (≈)', value: `${timeZoneInfo.timeZone}\n${timeZoneInfo.localTime}` }
        ];

        sections.forEach(section => {
            if (section.value) {
                const sectionDiv = document.createElement('div');
                sectionDiv.style.cssText = 'display: flex; flex-direction: column;';

                const label = document.createElement('span');
                label.style.cssText = 'color: #888; font-size: 12px; margin-bottom: 2px;';
                label.textContent = section.label;

                const value = document.createElement('span');
                value.style.cssText = 'color: #fff; font-size: 14px; white-space: pre-line;';
                value.textContent = section.value;

                sectionDiv.appendChild(label);
                sectionDiv.appendChild(value);
                contentDiv.appendChild(sectionDiv);
            }
        });

        contentDiv.appendChild(createMapsButton(lat, lng));
        return contentDiv;
    }

    function checkForLocationChanges() {
        const iframe = document.querySelector('#PanoramaIframe');
        if (!iframe) return;

        const src = iframe.getAttribute('src');
        if (!src) return;

        try {
            const url = new URL(src);
            const newLocation = url.searchParams.get('location');
            const currentTime = Date.now();

            if (processingLocation || currentTime - lastUpdateTime < COOLDOWN_TIME) {
                return;
            }

            if (newLocation && newLocation !== lastLocation) {
                console.log('[OpenSnitch Pro] Location change detected:', newLocation);
                lastUpdateTime = currentTime;
                showLocationInfo();
            }
        } catch (error) {
            console.error('[OpenSnitch Pro] Error checking location:', error);
        }
    }

    async function showLocationInfo() {
        if (updateTimeout) {
            clearTimeout(updateTimeout);
            updateTimeout = null;
        }

        if (processingLocation) {
            return;
        }

        processingLocation = true;

        let iframe = document.querySelector('#PanoramaIframe');
        if (!iframe) {
            createPopup().textContent = 'Waiting for map to load...';
            processingLocation = false;
            return;
        }

        try {
            let location;
            const currentUrl = new URL(window.location.href);
            location = currentUrl.searchParams.get('locationSearch') ||
                      currentUrl.searchParams.get('location');

            if (!location) {
                const src = iframe.getAttribute('src');
                if (src) {
                    const url = new URL(src);
                    location = url.searchParams.get('location');
                }
            }

            if (!location || location === lastLocation) {
                processingLocation = false;
                return;
            }

            console.log('[OpenSnitch Pro] Processing new location:', location);
            lastLocation = location;
            const [lat, lng] = location.split(',').map(Number);

            // Use Promise.all for parallel API calls
            const [locationDetails, timeZoneInfo] = await Promise.all([
                getLocationDetails(lat, lng),
                getTimeZoneInfo(lat, lng)
            ]);

            const popupContent = createPopup();
            const contentDiv = formatContent(locationDetails, timeZoneInfo, lat, lng);
            popupContent.appendChild(contentDiv);

        } catch (error) {
            console.error('[OpenSnitch Pro] Error:', error);
            createPopup().textContent = 'Error processing location';
        } finally {
            processingLocation = false;
        }
    }

        function determineLocation(lat, lng, address) {
        const countryBounds = getCountryBounds(address.country);
        if (!countryBounds) return '';

        let parts = [];

        const latPosition = (lat - countryBounds.south) / (countryBounds.north - countryBounds.south);
        if (latPosition < 0.33) parts.push('Southern');
        else if (latPosition > 0.66) parts.push('Northern');
        else parts.push('Central');

        const lngPosition = (lng - countryBounds.west) / (countryBounds.east - countryBounds.west);
        if (lngPosition < 0.33) parts.push('Western');
        else if (lngPosition > 0.66) parts.push('Eastern');
        else parts.push('Central');

        return `${parts.join(' ')} region`;
    }

    function getCountryBounds(country) { // I added almost every country
        const bounds = {
            'United States': { north: 49, south: 25, east: -66.93, west: -124.78 },
            'Russia': { north: 81.85, south: 41.18, east: 190.32, west: 19.25 },
            'Canada': { north: 83.11, south: 41.67, east: -52.62, west: -141.00 },
            'Brazil': { north: 5.27, south: -33.75, east: -34.79, west: -73.99 },
            'China': { north: 53.55, south: 18.15, east: 134.77, west: 73.55 },
            'Australia': { north: -10.06, south: -43.64, east: 153.61, west: 113.16 },
            'India': { north: 35.51, south: 6.75, east: 97.40, west: 68.10 },
            'Argentina': { north: -21.78, south: -55.06, east: -53.65, west: -73.56 },
            'Mexico': { north: 32.72, south: 14.53, east: -86.70, west: -118.40 },
            'Indonesia': { north: 5.90, south: -10.95, east: 141.02, west: 95.29 },
            'South Africa': { north: -22.13, south: -34.84, east: 32.89, west: 16.47 },
            'Ukraine': { north: 52.37, south: 44.39, east: 40.22, west: 22.13 },
            'France': { north: 51.09, south: 41.36, east: 9.56, west: -5.14 },
            'Germany': { north: 55.06, south: 47.27, east: 15.04, west: 5.87 },
            'Japan': { north: 45.52, south: 24.25, east: 145.82, west: 122.94 },
            'Spain': { north: 43.79, south: 36.00, east: 4.33, west: -9.30 },
            'Sweden': { north: 69.06, south: 55.34, east: 24.16, west: 11.11 },
            'Norway': { north: 71.18, south: 57.97, east: 31.17, west: 4.65 },
            'Finland': { north: 70.09, south: 59.81, east: 31.59, west: 20.55 },
            'Poland': { north: 54.84, south: 49.00, east: 24.15, west: 14.12 },
            'Italy': { north: 47.09, south: 36.65, east: 18.52, west: 6.63 },
            'United Kingdom': { north: 58.67, south: 49.96, east: 1.76, west: -8.65 },
            'Turkey': { north: 42.14, south: 35.82, east: 44.83, west: 25.66 },
            'Thailand': { north: 20.46, south: 5.61, east: 105.64, west: 97.34 },
            'Vietnam': { north: 23.39, south: 8.56, east: 109.47, west: 102.14 },
            'New Zealand': { north: -34.39, south: -47.29, east: 178.56, west: 166.42 },
            'South Korea': { north: 38.61, south: 33.11, east: 131.87, west: 125.07 },
            'Malaysia': { north: 7.36, south: 0.85, east: 119.27, west: 99.64 },
            'Philippines': { north: 21.12, south: 4.58, east: 126.60, west: 116.93 },
            'Chile': { north: -17.50, south: -55.98, east: -66.42, west: -75.64 },
            'Peru': { north: -0.03, south: -18.35, east: -68.68, west: -81.33 },
            'Colombia': { north: 13.38, south: -4.23, east: -66.87, west: -81.73 },
            'Greece': { north: 41.75, south: 34.80, east: 28.24, west: 19.37 },
            'Romania': { north: 48.27, south: 43.62, east: 29.67, west: 20.26 },
            'Portugal': { north: 42.15, south: 36.96, east: -6.19, west: -9.50 },
            'Netherlands': { north: 53.51, south: 50.75, east: 7.22, west: 3.36 },
            'Belgium': { north: 51.50, south: 49.49, east: 6.40, west: 2.54 },
            'Switzerland': { north: 47.81, south: 45.82, east: 10.49, west: 5.96 },
            'Austria': { north: 49.02, south: 46.37, east: 17.16, west: 9.53 },
            'Czechia': { north: 51.06, south: 48.55, east: 18.86, west: 12.09 },
            'Denmark': { north: 57.75, south: 54.56, east: 15.19, west: 8.07 },
            'Hungary': { north: 48.59, south: 45.74, east: 22.90, west: 16.11 },
            'Ireland': { north: 55.39, south: 51.42, east: -6.00, west: -10.48 },
            'Slovakia': { north: 49.61, south: 47.73, east: 22.57, west: 16.84 },
            'Bulgaria': { north: 44.22, south: 41.23, east: 28.61, west: 22.36 },
            'Croatia': { north: 46.55, south: 42.39, east: 19.45, west: 13.49 },
            'Estonia': { north: 59.68, south: 57.52, east: 28.21, west: 21.76 },
            'Latvia': { north: 58.08, south: 55.67, east: 28.24, west: 20.97 },
            'Lithuania': { north: 56.45, south: 53.89, east: 26.87, west: 20.93 },
            'Slovenia': { north: 46.87, south: 45.42, east: 16.61, west: 13.38 },
            'Israel': { north: 33.34, south: 29.49, east: 35.90, west: 34.27 },
            'Kazakhstan': { north: 55.45, south: 40.56, east: 87.31, west: 46.49 },
            'Belarus': { north: 56.17, south: 51.26, east: 32.77, west: 23.17 },
            'Moldova': { north: 48.49, south: 45.47, east: 30.13, west: 26.62 },
            'Azerbaijan': { north: 41.90, south: 38.40, east: 50.37, west: 44.77 },
            'Georgia': { north: 43.57, south: 41.05, east: 46.64, west: 40.00 },
            'Armenia': { north: 41.30, south: 38.84, east: 46.77, west: 43.45 },
            'Uzbekistan': { north: 45.59, south: 37.18, east: 73.14, west: 56.00 },
            'Kyrgyzstan': { north: 43.26, south: 39.17, east: 80.28, west: 69.27 },
            'Tajikistan': { north: 41.04, south: 36.67, east: 75.14, west: 67.34 },
            'Turkmenistan': { north: 42.79, south: 35.14, east: 66.68, west: 52.44 },
            'Pakistan': { north: 37.09, south: 23.69, east: 77.84, west: 60.87 },
            'Bangladesh': { north: 26.63, south: 20.74, east: 92.67, west: 88.03 },
            'Sri Lanka': { north: 9.83, south: 5.92, east: 81.88, west: 79.65 },
            'Myanmar': { north: 28.54, south: 9.78, east: 101.17, west: 92.19 },
            'Saudi Arabia': { north: 32.15, south: 16.38, east: 55.67, west: 34.49 },
            'UAE': { north: 26.08, south: 22.63, east: 56.38, west: 51.58 },
            'Oman': { north: 26.39, south: 16.65, east: 59.84, west: 51.88 },
            'Qatar': { north: 26.18, south: 24.48, east: 51.64, west: 50.75 },
            'Kuwait': { north: 30.10, south: 28.52, east: 48.43, west: 46.55 },
            'Bahrain': { north: 26.29, south: 25.79, east: 50.82, west: 50.45 },
            'Lebanon': { north: 34.69, south: 33.05, east: 36.63, west: 35.10 },
            'Jordan': { north: 33.37, south: 29.19, east: 39.30, west: 34.95 },
            'Egypt': { north: 31.67, south: 22.00, east: 36.90, west: 24.70 },
            'Iraq': { north: 37.38, south: 29.06, east: 48.57, west: 38.79 },
            'Iran': { north: 39.78, south: 25.06, east: 63.32, west: 44.03 },
            'Yemen': { north: 19.00, south: 12.11, east: 54.54, west: 41.81 },
            'Syria': { north: 37.32, south: 32.31, east: 42.37, west: 35.73 },
            'Afghanistan': { north: 38.49, south: 29.38, east: 74.89, west: 60.52 },
            'Cyprus': { north: 35.70, south: 34.63, east: 34.60, west: 32.27 },
            'Mongolia': { north: 52.15, south: 41.56, east: 119.94, west: 87.73 },
            'Singapore': { north: 1.47, south: 1.26, east: 104.01, west: 103.64 },
            'Cambodia': { north: 14.69, south: 10.41, east: 107.64, west: 102.33 },
            'Laos': { north: 22.50, south: 13.91, east: 107.70, west: 100.09 },
            'Nepal': { north: 30.45, south: 26.36, east: 88.20, west: 80.06 },
            'Albania': { north: 42.66, south: 39.65, east: 21.06, west: 19.27 },
            'Bosnia': { north: 45.27, south: 42.55, east: 19.62, west: 15.73 },
            'Serbia': { north: 46.19, south: 41.86, east: 23.01, west: 18.86 },
            'Kosovo': { north: 43.27, south: 41.86, east: 21.78, west: 20.01 },
            'North Macedonia': { north: 42.37, south: 40.86, east: 23.03, west: 20.46 },
            'Montenegro': { north: 43.57, south: 41.85, east: 20.36, west: 18.46 },
            'Iceland': { north: 66.53, south: 63.30, east: -13.49, west: -24.32 },
            'Luxembourg': { north: 50.18, south: 49.45, east: 6.53, west: 5.73 },
            'Malta': { north: 36.08, south: 35.79, east: 14.58, west: 14.18 },
            'Paraguay': { north: -19.29, south: -27.61, east: -54.25, west: -62.65 },
            'Uruguay': { north: -30.08, south: -34.98, east: -53.07, west: -58.44 },
            'Bolivia': { north: -9.68, south: -22.90, east: -57.45, west: -69.65 },
            'Ecuador': { north: 1.44, south: -5.00, east: -75.19, west: -81.01 },
            'Venezuela': { north: 12.20, south: 0.64, east: -59.80, west: -73.35 },
            'Panama': { north: 9.63, south: 7.20, east: -77.17, west: -83.05 },
            'Costa Rica': { north: 11.22, south: 8.03, east: -82.55, west: -85.95 },
            'Nicaragua': { north: 15.03, south: 10.71, east: -82.73, west: -87.69 },
            'Honduras': { north: 16.51, south: 12.98, east: -83.15, west: -89.35 },
            'El Salvador': { north: 14.45, south: 13.15, east: -87.68, west: -90.13 },
            'Guatemala': { north: 17.82, south: 13.74, east: -88.23, west: -92.23 },
            'Dominican Republic': { north: 19.93, south: 17.54, east: -68.32, west: -72.01 },
            'Haiti': { north: 20.09, south: 18.02, east: -71.62, west: -74.48 },
            'Jamaica': { north: 18.53, south: 17.70, east: -76.19, west: -78.37 },
            'Cuba': { north: 23.23, south: 19.82, east: -74.13, west: -84.95 },
            'Tunisia': { north: 37.35, south: 30.23, east: 11.60, west: 7.52 },
            'Algeria': { north: 37.09, south: 18.96, east: 12.00, west: -8.67 },
            'Morocco': { north: 35.92, south: 27.66, east: -1.12, west: -13.17 },
            'Sudan': { north: 22.23, south: 8.68, east: 38.58, west: 21.82 },
            'Ethiopia': { north: 14.89, south: 3.40, east: 47.98, west: 32.99 },
            'Kenya': { north: 5.02, south: -4.72, east: 41.91, west: 33.91 },
            'Tanzania': { north: -0.99, south: -11.75, east: 40.44, west: 29.33 },
            'Uganda': { north: 4.23, south: -1.48, east: 35.03, west: 29.57 },
            'Rwanda': { north: -1.05, south: -2.84, east: 30.90, west: 28.86 },
            'Ghana': { north: 11.17, south: 4.74, east: 1.19, west: -3.26 },
            'Nigeria': { north: 13.89, south: 4.27, east: 14.68, west: 2.67 },
            'Senegal': { north: 16.69, south: 12.31, east: -11.34, west: -17.54 },
            'Madagascar': { north: -11.95, south: -25.61, east: 50.48, west: 43.22 },
            'Mozambique': { north: -10.47, south: -26.87, east: 40.84, west: 30.21 },
            'Angola': { north: -4.37, south: -18.04, east: 24.08, west: 11.67 },
            'Namibia': { north: -16.96, south: -28.97, east: 25.26, west: 11.73 },
            'Zimbabwe': { north: -15.61, south: -22.42, east: 33.06, west: 25.24 },
            'Botswana': { north: -17.78, south: -26.91, east: 29.38, west: 20.00 },
            'Zambia': { north: -8.22, south: -18.08, east: 33.71, west: 21.99 },
            'Malawi': { north: -9.37, south: -17.13, east: 35.92, west: 32.67 },
            'Papua New Guinea': { north: -1.32, south: -11.65, east: 155.96, west: 140.84 },
            'Solomon Islands': { north: -6.59, south: -11.85, east: 166.98, west: 155.51 },
            'Fiji': { north: -16.02, south: -19.15, east: -178.42, west: 177.27 },
            'Vanuatu': { north: -13.07, south: -20.25, east: 170.23, west: 166.52 },
            'Samoa': { north: -13.43, south: -14.05, east: -171.41, west: -172.80 }
        };
        return bounds[country] || null;
    }

    function initialize() {
        console.log('[OpenSnitch Pro] Initializing with multi-API support...');
        showLocationInfo();

        // Primary location interval
        setInterval(checkForLocationChanges, 500);

        // Mutation observer for DOM changes
        const observer = new MutationObserver((mutations) => {
            for (const mutation of mutations) {
                if (mutation.type === 'attributes' && mutation.attributeName === 'src' ||
                    mutation.target.id === 'PanoramaIframe') {
                    checkForLocationChanges();
                }
            }
        });

        observer.observe(document.body, {
            subtree: true,
            attributes: true,
            attributeFilter: ['src'],
            childList: true
        });

        console.log('[OpenSnitch Pro] Initialization complete');
    }

    initialize();
})();