{"version":3,"file":"outage_map.js","mappings":";;;;;;;;;;;;;;AAAA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAAA;AAGA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;;;;;ACnHA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;;;;;;;;;;;;;;;;AC7BA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;;AAEA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;;AAEA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;;AAEA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;;AAEA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AAAA;AACA;AACA;AAEA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;;AAEA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AAAA;AACA;;AAeA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;;AAkBA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAEA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AAEA;;AAEA;AACA;;AAEA;AACA;AACA;AACA;;AAEA;AACA;;AAkBA;AACA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AAAA;AACA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AAAA;AAAA;AACA;AAAA;AAAA;AACA;AACA;AAEA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAIA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA;AACA;AACA;AACA;AACA;AAWA;AACA;;AAEA;AACA","sources":["webpack://ctatus/./app/javascript/user_reports_chart.js","webpack://ctatus/webpack/bootstrap","webpack://ctatus/./app/javascript/packs/outage_map.js"],"sourcesContent":["// Centralized configuration for user reports charts across the app\n// Used in: show_variant, outage_map, details, etc.\n\nconst UserReportsChart = {\n // Status thresholds\n THRESHOLDS: {\n NORMAL: 5, // < 5 = Working normally\n WARNING: 15 // 5-15 = Possible Incident, > 15 = Confirmed Outage\n },\n\n // Get status label based on report count\n getStatusLabel: function(value) {\n if (value < this.THRESHOLDS.NORMAL) {\n return 'Working normally';\n } else if (value <= this.THRESHOLDS.WARNING) {\n return 'Possible Incident';\n } else {\n return 'Confirmed Outage';\n }\n },\n\n // Get bar color based on report count\n getBarColor: function(value) {\n if (value < this.THRESHOLDS.NORMAL) {\n return 'rgba(7, 169, 0, 0.7)'; // Green\n } else if (value <= this.THRESHOLDS.WARNING) {\n return 'rgba(249, 115, 22, 0.7)'; // Orange\n } else {\n return 'rgba(239, 68, 68, 0.7)'; // Red\n }\n },\n\n // Dataset options for the chart\n getDatasetOptions: function() {\n const self = this;\n return {\n backgroundColor: function(context) {\n var value = context.dataset.data[context.dataIndex];\n return self.getBarColor(value ? value['y'] : 0);\n },\n fill: true,\n tension: 0.3,\n minBarLength: 4,\n pointRadius: 2\n };\n },\n\n // Default chart options\n getChartOptions: function(options = {}) {\n const self = this;\n return {\n animation: false,\n scales: {\n x: {\n grid: {\n display: false\n },\n display: options.showXAxis || false,\n ticks: {\n maxTicksLimit: 10\n }\n },\n y: {\n grid: {\n display: options.showYGrid !== false\n },\n beginAtZero: true,\n display: options.showYAxis !== false,\n suggestedMax: 20,\n ticks: {\n stepSize: 5\n }\n }\n },\n plugins: {\n legend: {\n display: false\n },\n tooltip: {\n callbacks: {\n label: function(context) {\n var value = context.parsed.y;\n return self.getStatusLabel(value);\n }\n }\n }\n }\n };\n },\n\n // Create a chart instance\n create: function(canvasId, data, options = {}) {\n const ctx = document.getElementById(canvasId);\n if (!ctx) return null;\n\n return new Chart(ctx, {\n type: options.type || 'bar',\n data: {\n datasets: [{\n label: 'User Reports',\n data: data,\n ...this.getDatasetOptions()\n }]\n },\n options: this.getChartOptions(options)\n });\n }\n};\n\n// Export for use in packs\nif (typeof module !== 'undefined' && module.exports) {\n module.exports = UserReportsChart;\n}\n\n// Make available globally for inline scripts\nwindow.UserReportsChart = UserReportsChart;\n\n","// The module cache\nvar __webpack_module_cache__ = {};\n\n// The require function\nfunction __webpack_require__(moduleId) {\n\t// Check if module is in cache\n\tvar cachedModule = __webpack_module_cache__[moduleId];\n\tif (cachedModule !== undefined) {\n\t\treturn cachedModule.exports;\n\t}\n\t// Check if module exists (development only)\n\tif (__webpack_modules__[moduleId] === undefined) {\n\t\tvar e = new Error(\"Cannot find module '\" + moduleId + \"'\");\n\t\te.code = 'MODULE_NOT_FOUND';\n\t\tthrow e;\n\t}\n\t// Create a new module (and put it into the cache)\n\tvar module = __webpack_module_cache__[moduleId] = {\n\t\t// no module.id needed\n\t\t// no module.loaded needed\n\t\texports: {}\n\t};\n\n\t// Execute the module function\n\t__webpack_modules__[moduleId](module, module.exports, __webpack_require__);\n\n\t// Return the exports of the module\n\treturn module.exports;\n}\n\n","// Outage Map JavaScript\nrequire('../user_reports_chart');\n\n// Constants for configuration\nconst MAP_CONFIG = {\n center: [10, 0],\n zoom: 1,\n minZoom: 1,\n maxZoom: 5,\n tileSize: 256,\n preferCanvas: true,\n maxBounds: [[-90, -180], [90, 180]],\n maxBoundsViscosity: 1.0,\n worldCopyJump: false,\n zoomControl: false,\n scrollWheelZoom: false,\n doubleClickZoom: false,\n boxZoom: false,\n keyboard: false,\n dragging: false\n};\n\nconst CHOROPLETH_CONFIG = {\n FILL_OPACITY: 0.7,\n WEIGHT: 1,\n DEFAULT_COLOR: '#ffffff',\n DEFAULT_FILL_COLOR: '#f7f7f7',\n HOVER_WEIGHT: 2,\n HOVER_COLOR: '#666'\n};\n\nconst COLOR_THRESHOLDS = {\n HIGH: 0.66,\n MEDIUM: 0.33,\n COLORS: {\n HIGH: '#ef4444', // red\n MEDIUM: '#f97316', // orange\n LOW: '#fbbf24', // amber\n NONE: '#f7f7f7' // light gray\n }\n};\n\n// GeoJSON data URL for world countries with ISO codes\n// Using simplified 110m resolution for smaller file size (~200KB)\n// This GeoJSON has ISO_A2 field for 2-letter country codes\nconst GEOJSON_URL = 'https://raw.githubusercontent.com/nvkelso/natural-earth-vector/master/geojson/ne_110m_admin_0_countries.geojson';\n\n// Country coordinates cache\nlet countryCoordinatesCache = null;\n\n// Get country coordinates with caching\nfunction getCountryCoordinates() {\n if (countryCoordinatesCache) {\n return countryCoordinatesCache;\n }\n\n countryCoordinatesCache = {\n // Africa\n 'DZ': [28.0339, 1.6596], // Algeria\n 'AO': [-11.2027, 17.8739], // Angola\n 'BJ': [9.3077, 2.3158], // Benin\n 'BW': [-22.3285, 24.6849], // Botswana\n 'BF': [12.2383, -1.5616], // Burkina Faso\n 'BI': [-3.3731, 29.9189], // Burundi\n 'CM': [7.3697, 12.3547], // Cameroon\n 'CV': [16.0021, -24.0132], // Cape Verde\n 'CF': [6.6111, 20.9394], // Central African Republic\n 'TD': [15.4542, 18.7322], // Chad\n 'KM': [-11.8750, 43.8722], // Comoros\n 'CG': [-0.2280, 15.8277], // Congo\n 'CD': [-4.0383, 21.7587], // Democratic Republic of the Congo\n 'DJ': [11.8251, 42.5903], // Djibouti\n 'EG': [26.8206, 30.8025], // Egypt\n 'GQ': [1.6508, 10.2679], // Equatorial Guinea\n 'ER': [15.1794, 39.7823], // Eritrea\n 'SZ': [-26.5225, 31.4659], // Eswatini\n 'ET': [9.1450, 40.4897], // Ethiopia\n 'GA': [-0.8037, 11.6094], // Gabon\n 'GM': [13.4432, -15.3101], // Gambia\n 'GH': [7.9465, -1.0232], // Ghana\n 'GN': [9.9456, -9.6966], // Guinea\n 'GW': [11.8037, -15.1804], // Guinea-Bissau\n 'CI': [7.5400, -5.5471], // Ivory Coast\n 'KE': [-0.0236, 37.9062], // Kenya\n 'LS': [-29.6100, 28.2336], // Lesotho\n 'LR': [6.4281, -9.4295], // Liberia\n 'LY': [26.3351, 17.2283], // Libya\n 'MG': [-18.7669, 46.8691], // Madagascar\n 'MW': [-13.2543, 34.3015], // Malawi\n 'ML': [17.5707, -3.9962], // Mali\n 'MR': [21.0079, -10.9408], // Mauritania\n 'MU': [-20.3484, 57.5522], // Mauritius\n 'MA': [31.7917, -7.0926], // Morocco\n 'MZ': [-18.6657, 35.5296], // Mozambique\n 'NA': [-22.9576, 18.4904], // Namibia\n 'NE': [17.6078, 8.0817], // Niger\n 'NG': [9.0820, 8.6753], // Nigeria\n 'RW': [-1.9403, 29.8739], // Rwanda\n 'ST': [0.1864, 6.6131], // Sao Tome and Principe\n 'SN': [14.4974, -14.4524], // Senegal\n 'SC': [-4.6796, 55.4920], // Seychelles\n 'SL': [8.4606, -11.7799], // Sierra Leone\n 'SO': [5.1521, 46.1996], // Somalia\n 'ZA': [-30.5595, 22.9375], // South Africa\n 'SS': [6.8770, 31.3070], // South Sudan\n 'SD': [12.8628, 30.2176], // Sudan\n 'TZ': [-6.3690, 34.8888], // Tanzania\n 'TG': [8.6195, 0.8248], // Togo\n 'TN': [33.8869, 9.5375], // Tunisia\n 'UG': [1.3733, 32.2903], // Uganda\n 'ZM': [-13.1339, 27.8493], // Zambia\n 'ZW': [-19.0154, 29.1549], // Zimbabwe\n\n // Americas\n 'AG': [17.0608, -61.7964], // Antigua and Barbuda\n 'AR': [-38.4161, -63.6167], // Argentina\n 'BS': [25.0343, -77.3963], // Bahamas\n 'BB': [13.1939, -59.5432], // Barbados\n 'BZ': [17.1899, -88.4976], // Belize\n 'BO': [-16.2902, -63.5887], // Bolivia\n 'BR': [-14.2350, -51.9253], // Brazil\n 'CA': [56.1304, -106.3468], // Canada\n 'CL': [-35.6751, -71.5430], // Chile\n 'CO': [4.5709, -74.2973], // Colombia\n 'CR': [9.7489, -83.7534], // Costa Rica\n 'CU': [21.5218, -77.7812], // Cuba\n 'DM': [15.4150, -61.3710], // Dominica\n 'DO': [18.7357, -70.1627], // Dominican Republic\n 'EC': [-1.8312, -78.1834], // Ecuador\n 'SV': [13.7942, -88.8965], // El Salvador\n 'GD': [12.2628, -61.6042], // Grenada\n 'GT': [15.7835, -90.2308], // Guatemala\n 'GY': [4.8604, -58.9302], // Guyana\n 'HT': [18.9712, -72.2852], // Haiti\n 'HN': [15.2000, -86.2419], // Honduras\n 'JM': [18.1096, -77.2975], // Jamaica\n 'MX': [23.6345, -102.5528], // Mexico\n 'NI': [12.8654, -85.2072], // Nicaragua\n 'PA': [8.5380, -80.7821], // Panama\n 'PY': [-23.4425, -58.4438], // Paraguay\n 'PE': [-9.1900, -75.0152], // Peru\n 'KN': [17.3578, -62.7830], // Saint Kitts and Nevis\n 'LC': [13.9094, -60.9789], // Saint Lucia\n 'VC': [12.9843, -61.2872], // Saint Vincent and the Grenadines\n 'SR': [3.9193, -56.0278], // Suriname\n 'TT': [10.6918, -61.2225], // Trinidad and Tobago\n 'US': [39.8283, -98.5795], // United States\n 'UY': [-32.5228, -55.7658], // Uruguay\n 'VE': [6.4238, -66.5897], // Venezuela\n\n // Asia\n 'AF': [33.9391, 67.7100], // Afghanistan\n 'AM': [40.0691, 45.0382], // Armenia\n 'AZ': [40.1431, 47.5769], // Azerbaijan\n 'BH': [26.0667, 50.5577], // Bahrain\n 'BD': [23.6850, 90.3563], // Bangladesh\n 'BT': [27.5142, 90.4336], // Bhutan\n 'BN': [4.5353, 114.7277], // Brunei\n 'KH': [12.5657, 104.9910], // Cambodia\n 'CN': [35.8617, 104.1954], // China\n 'CY': [35.1264, 33.4299], // Cyprus\n 'GE': [42.3154, 43.3569], // Georgia\n 'IN': [20.5937, 78.9629], // India\n 'ID': [-0.7893, 113.9213], // Indonesia\n 'IQ': [33.2232, 43.6793], // Iraq\n 'IL': [31.0461, 34.8516], // Israel\n 'JP': [36.2048, 138.2529], // Japan\n 'JO': [30.5852, 36.2384], // Jordan\n 'KZ': [48.0196, 66.9237], // Kazakhstan\n 'KW': [29.3117, 47.4818], // Kuwait\n 'KG': [41.2044, 74.7661], // Kyrgyzstan\n 'LA': [19.8563, 102.4955], // Laos\n 'LB': [33.8547, 35.8623], // Lebanon\n 'MY': [4.2105, 101.9758], // Malaysia\n 'MV': [3.2028, 73.2207], // Maldives\n 'MN': [46.8625, 103.8467], // Mongolia\n 'MM': [21.9162, 95.9560], // Myanmar\n 'NP': [28.3949, 84.1240], // Nepal\n 'OM': [21.5129, 55.9233], // Oman\n 'PK': [30.3753, 69.3451], // Pakistan\n 'PH': [12.8797, 121.7740], // Philippines\n 'QA': [25.3548, 51.1839], // Qatar\n 'SA': [23.8859, 45.0792], // Saudi Arabia\n 'SG': [1.3521, 103.8198], // Singapore\n 'KR': [35.9078, 127.7669], // South Korea\n 'LK': [7.8731, 80.7718], // Sri Lanka\n 'SY': [34.8021, 38.9968], // Syria\n 'TJ': [38.8610, 71.2761], // Tajikistan\n 'TH': [15.8700, 100.9925], // Thailand\n 'TL': [-8.8742, 125.7275], // Timor-Leste\n 'TM': [38.9697, 59.5563], // Turkmenistan\n 'AE': [23.4241, 53.8478], // United Arab Emirates\n 'UZ': [41.3775, 64.5853], // Uzbekistan\n 'VN': [14.0583, 108.2772], // Vietnam\n 'YE': [15.5527, 48.5164], // Yemen\n\n // Europe\n 'AL': [41.1533, 20.1683], // Albania\n 'AD': [42.5063, 1.5218], // Andorra\n 'AT': [47.5162, 14.5501], // Austria\n 'BY': [53.7098, 27.9534], // Belarus\n 'BE': [50.5039, 4.4699], // Belgium\n 'BA': [43.9159, 17.6791], // Bosnia and Herzegovina\n 'BG': [42.7339, 25.4858], // Bulgaria\n 'HR': [45.1000, 15.2000], // Croatia\n 'CZ': [49.8175, 15.4730], // Czech Republic\n 'DK': [56.2639, 9.5018], // Denmark\n 'EE': [58.5953, 25.0136], // Estonia\n 'FI': [61.9241, 25.7482], // Finland\n 'FR': [46.2276, 2.2137], // France\n 'DE': [51.1657, 10.4515], // Germany\n 'GR': [39.0742, 21.8243], // Greece\n 'HU': [47.1625, 19.5033], // Hungary\n 'IS': [64.9631, -19.0208], // Iceland\n 'IE': [53.4129, -8.2439], // Ireland\n 'IT': [41.8719, 12.5674], // Italy\n 'XK': [42.6026, 20.9030], // Kosovo\n 'LV': [56.8796, 24.6032], // Latvia\n 'LI': [47.1660, 9.5554], // Liechtenstein\n 'LT': [55.1694, 23.8813], // Lithuania\n 'LU': [49.8153, 6.1296], // Luxembourg\n 'MT': [35.9375, 14.3754], // Malta\n 'MD': [47.4116, 28.3699], // Moldova\n 'MC': [43.7384, 7.4246], // Monaco\n 'ME': [42.7087, 19.3744], // Montenegro\n 'NL': [52.1326, 5.2913], // Netherlands\n 'MK': [41.6086, 21.7453], // North Macedonia\n 'NO': [60.4720, 8.4689], // Norway\n 'PL': [51.9194, 19.1451], // Poland\n 'PT': [39.3999, -8.2245], // Portugal\n 'RO': [45.9432, 24.9668], // Romania\n 'RU': [61.5240, 105.3188], // Russia\n 'SM': [43.9424, 12.4578], // San Marino\n 'RS': [44.0165, 21.0059], // Serbia\n 'SK': [48.6690, 19.6990], // Slovakia\n 'SI': [46.1512, 14.9955], // Slovenia\n 'ES': [40.4637, -3.7492], // Spain\n 'SE': [60.1282, 18.6435], // Sweden\n 'CH': [46.8182, 8.2275], // Switzerland\n 'TR': [38.9637, 35.2433], // Turkey\n 'UA': [48.3794, 31.1656], // Ukraine\n 'GB': [55.3781, -3.4360], // United Kingdom\n 'VA': [41.9029, 12.4534], // Vatican City\n\n // Oceania\n 'AU': [-25.2744, 133.7751], // Australia\n 'FJ': [-16.5782, 179.4144], // Fiji\n 'KI': [-3.3704, -168.7340], // Kiribati\n 'MH': [7.1315, 171.1845], // Marshall Islands\n 'FM': [7.4256, 150.5508], // Micronesia\n 'NR': [-0.5228, 166.9315], // Nauru\n 'NZ': [-40.9006, 174.8860], // New Zealand\n 'PW': [7.5150, 134.5825], // Palau\n 'PG': [-6.3150, 143.9555], // Papua New Guinea\n 'WS': [-13.7590, -172.1046],// Samoa\n 'SB': [-9.6457, 160.1562], // Solomon Islands\n 'TO': [-21.1790, -175.1982],// Tonga\n 'TV': [-7.1095, 177.6493], // Tuvalu\n 'VU': [-15.3767, 166.9592] // Vanuatu\n };\n\n return countryCoordinatesCache;\n}\n\n// Initialize outage map with error handling\nfunction initializeOutageMap(pulseData) {\n try {\n // Check if Leaflet is loaded\n if (typeof L === 'undefined') {\n console.error('Leaflet library not loaded');\n setTimeout(() => initializeOutageMap(pulseData), 100);\n return;\n }\n\n // Check if map container exists\n const mapContainer = document.getElementById('outage-map');\n if (!mapContainer) {\n console.error('Map container not found');\n return;\n }\n\n // Initialize the map\n console.log('Initializing outage map...');\n const map = L.map('outage-map', MAP_CONFIG);\n\n // Add tile layer without labels using CartoDB Positron No Labels\n const tileLayer = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}{r}.png', {\n attribution: '© OpenStreetMap contributors © CARTO',\n subdomains: 'abcd',\n maxZoom: MAP_CONFIG.maxZoom,\n minZoom: MAP_CONFIG.minZoom,\n tileSize: MAP_CONFIG.tileSize,\n zoomOffset: 0,\n detectRetina: false,\n crossOrigin: true,\n noWrap: false,\n bounds: [[-90, -180], [90, 180]]\n });\n\n // Add error handling for tile loading\n tileLayer.on('tileerror', function(error) {\n console.error('Tile loading error:', error);\n // Could implement fallback tile server here\n });\n\n tileLayer.addTo(map);\n\n // Force a resize to ensure proper rendering\n setTimeout(() => {\n map.invalidateSize();\n console.log('Map size invalidated');\n }, 100);\n\n // Process and display pulse data\n if (pulseData && Object.keys(pulseData).length > 0) {\n displayPulseData(map, pulseData);\n } else {\n console.log('No pulse data available');\n }\n\n // Setup pulse form handler\n setupPulseFormHandler();\n\n } catch (error) {\n console.error('Error initializing outage map:', error);\n showMapError('Failed to initialize map. Please refresh the page.');\n }\n}\n\n// Display pulse data on the map\nfunction displayPulseData(map, pulseData) {\n // Parse pulse data to get country codes and counts\n const countryReports = {};\n const maxCount = Math.max(...Object.values(pulseData));\n\n Object.entries(pulseData).forEach(([countryDataKey, count]) => {\n try {\n const countryData = JSON.parse(countryDataKey);\n const countryCode = countryData[1]; // Use country code (e.g., 'US', 'GB')\n if (countryCode) {\n countryReports[countryCode] = count;\n }\n } catch (error) {\n console.error('Error parsing country data:', error);\n }\n });\n\n console.log('Country reports:', countryReports);\n\n // Fetch and display GeoJSON data\n fetch(GEOJSON_URL)\n .then(response => {\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n return response.json();\n })\n .then(data => {\n console.log('GeoJSON loaded successfully');\n // Log sample feature to see structure\n if (data.features && data.features.length > 0) {\n console.log('Sample GeoJSON feature properties:', data.features[0].properties);\n console.log('Total features:', data.features.length);\n\n }\n\n // Create GeoJSON layer with choropleth styling\n const geojsonLayer = L.geoJSON(data, {\n style: function(feature) {\n const properties = feature.properties || {};\n let count = 0;\n\n // Try multiple fields for country code matching\n const possibleCodes = [\n properties.id, // Some GeoJSON files use 'id' for ISO2\n properties['ISO_A2'], // Common field name\n properties['ISO3166-1-Alpha-2'],\n properties.iso2,\n properties.ISO_A2,\n properties.iso_a2,\n properties.ISO2,\n properties.code,\n properties.iso_code,\n properties.country_code,\n properties.alpha2,\n properties.Alpha2,\n properties.ALPHA2,\n properties['iso-a2'],\n properties['ISO-A2']\n ].filter(Boolean);\n\n // Special handling for known problematic countries\n // France in Natural Earth data sometimes has -99 for ISO_A2\n if (!possibleCodes.length || possibleCodes.every(code => code === '-99' || code === '-1')) {\n const countryName = properties.ADMIN || properties.NAME || properties.name || '';\n const nameMapping = {\n 'France': 'FR',\n 'United Kingdom': 'GB',\n 'United States of America': 'US',\n 'United States': 'US'\n };\n const mappedCode = nameMapping[countryName];\n if (mappedCode) {\n possibleCodes.push(mappedCode);\n }\n }\n\n // Check each possible code\n for (const code of possibleCodes) {\n if (countryReports[code]) {\n count = countryReports[code];\n break;\n }\n }\n\n const fillColor = getCountryColor(count, maxCount);\n\n return {\n fillColor: fillColor,\n weight: 0,\n opacity: 0,\n color: 'transparent',\n fillOpacity: count > 0 ? CHOROPLETH_CONFIG.FILL_OPACITY : 0.3\n };\n },\n onEachFeature: function(feature, layer) {\n const properties = feature.properties || {};\n const countryName = properties.ADMIN || properties.NAME || properties.name || 'Unknown';\n\n // Get count using same logic as style function\n let count = 0;\n const possibleCodes = [\n properties.id,\n properties['ISO_A2'],\n properties['ISO3166-1-Alpha-2'],\n properties.iso2,\n properties.ISO_A2,\n properties.iso_a2,\n properties.ISO2,\n properties.code,\n properties.iso_code,\n properties.country_code,\n properties.alpha2,\n properties.Alpha2,\n properties.ALPHA2,\n properties['iso-a2'],\n properties['ISO-A2']\n ].filter(Boolean);\n\n // Special handling for known problematic countries\n if (!possibleCodes.length || possibleCodes.every(code => code === '-99' || code === '-1')) {\n const countryName = properties.ADMIN || properties.NAME || properties.name || '';\n const nameMapping = {\n 'France': 'FR',\n 'United Kingdom': 'GB',\n 'United States of America': 'US',\n 'United States': 'US'\n };\n const mappedCode = nameMapping[countryName];\n if (mappedCode) {\n possibleCodes.push(mappedCode);\n }\n }\n\n for (const code of possibleCodes) {\n if (countryReports[code]) {\n count = countryReports[code];\n break;\n }\n }\n }\n });\n\n geojsonLayer.addTo(map);\n\n // Store reference for interaction\n map.geojsonLayer = geojsonLayer;\n\n // Fit map to show countries with reports\n const boundsArray = [];\n geojsonLayer.eachLayer(function(layer) {\n const properties = layer.feature.properties || {};\n\n // Use same logic to find country code\n const possibleCodes = [\n properties.id,\n properties['ISO_A2'],\n properties['ISO3166-1-Alpha-2'],\n properties.iso2,\n properties.ISO_A2,\n properties.iso_a2,\n properties.ISO2,\n properties.code,\n properties.iso_code,\n properties.country_code,\n properties.alpha2,\n properties.Alpha2,\n properties.ALPHA2,\n properties['iso-a2'],\n properties['ISO-A2']\n ].filter(Boolean);\n\n // Special handling for known problematic countries\n if (!possibleCodes.length || possibleCodes.every(code => code === '-99' || code === '-1')) {\n const countryName = properties.ADMIN || properties.NAME || properties.name || '';\n const nameMapping = {\n 'France': 'FR',\n 'United Kingdom': 'GB',\n 'United States of America': 'US',\n 'United States': 'US'\n };\n const mappedCode = nameMapping[countryName];\n if (mappedCode) {\n possibleCodes.push(mappedCode);\n }\n }\n\n // Check if any of the possible codes have reports\n for (const code of possibleCodes) {\n if (countryReports[code] && countryReports[code] > 0) {\n boundsArray.push(layer.getBounds());\n break;\n }\n }\n });\n\n if (boundsArray.length > 0) {\n const combinedBounds = boundsArray.reduce((acc, bounds) => acc.extend(bounds), L.latLngBounds(boundsArray[0]));\n map.fitBounds(combinedBounds, { padding: [50, 50] });\n }\n })\n .catch(error => {\n console.error('Error loading GeoJSON data:', error);\n showMapError('Failed to load country data. Please refresh the page.');\n });\n}\n\n// Get country color based on count\nfunction getCountryColor(count, maxCount) {\n if (count === 0) {\n return COLOR_THRESHOLDS.COLORS.NONE;\n }\n\n const ratio = count / maxCount;\n\n if (ratio > COLOR_THRESHOLDS.HIGH) {\n return COLOR_THRESHOLDS.COLORS.HIGH;\n } else if (ratio > COLOR_THRESHOLDS.MEDIUM) {\n return COLOR_THRESHOLDS.COLORS.MEDIUM;\n } else {\n return COLOR_THRESHOLDS.COLORS.LOW;\n }\n}\n\n\n\n// Setup pulse form handler\nfunction setupPulseFormHandler() {\n const pulseForm = document.getElementById('js-pulse');\n if (!pulseForm) return;\n\n // Handle Rails UJS ajax success\n if (typeof $ !== 'undefined') {\n $('#js-pulse').on('ajax:success', function() {\n $('#js-pulse').hide();\n $('#js-pulse-done').removeClass('hidden');\n });\n }\n}\n\n// Show error message on map container\nfunction showMapError(message) {\n const mapContainer = document.getElementById('outage-map');\n if (mapContainer) {\n mapContainer.innerHTML = `\n
\n
\n \n \n \n

${message}

\n
\n
\n `;\n }\n}\n\n// Export initialization function for use in ERB\nwindow.initializeOutageMap = initializeOutageMap;"],"names":[],"sourceRoot":""}