1/** 2 * Emergency Map KZ - Panel Module 3 * Panel collapse, event popup, event cards, events list 4 */ 5 6(function($, window) { 7 'use strict'; 8 9 window.EmergencyMapKZ = window.EmergencyMapKZ || {}; 10 var KZ = window.EmergencyMapKZ; 11 12 // ========== Panel collapse ========== 13 var isPanelCollapsed = sessionStorage.getItem('kzMapPanelCollapsed') === 'true'; 14 var MOBILE_PANEL_QUERY = '(max-width: 600px)'; 15 16 function isMobilePanel() { 17 return window.matchMedia && window.matchMedia(MOBILE_PANEL_QUERY).matches; 18 } 19 20 function getExpandedPanelCss() { 21 if (isMobilePanel()) { 22 return { 23 'left': '12px', 24 'right': '12px', 25 'width': 'auto', 26 'max-height': 'calc(100% - 88px)', 27 'overflow': 'visible' 28 }; 29 } 30 31 return { 32 'left': 'auto', 33 'right': '24px', 34 'width': '400px', 35 'max-height': '500px', 36 'overflow': 'visible' 37 }; 38 } 39 40 function collapsePanel() { 41 $('#kz-events-panel') 42 .addClass('is-collapsed') 43 .css({ 'left': 'auto', 'width': '48px', 'max-height': '48px', 'overflow': 'hidden' }); 44 $('#kz-filters, #kz-events-list, #kz-events-footer').css('opacity', '0').hide(); 45 $('#kz-panel-title').hide(); 46 $('#kz-events-header').css({ 'border-bottom': 'none', 'padding': '10px' }); 47 $('.collapse-icon').hide(); 48 $('.expand-icon').css('display', 'inline'); 49 sessionStorage.setItem('kzMapPanelCollapsed', 'true'); 50 } 51 52 function expandPanel() { 53 $('#kz-events-panel') 54 .removeClass('is-collapsed') 55 .css(getExpandedPanelCss()); 56 $('#kz-filters, #kz-events-list, #kz-events-footer').show().css('opacity', '1'); 57 $('#kz-panel-title').show(); 58 $('#kz-events-header').css({ 'border-bottom': '1px solid #dee2e6', 'padding': '16px' }); 59 $('.collapse-icon').css('display', 'inline'); 60 $('.expand-icon').hide(); 61 sessionStorage.setItem('kzMapPanelCollapsed', 'false'); 62 } 63 64 // ========== Event popup ========== 65 KZ.showEventPopup = function(eventItem) { 66 var riskColor = KZ.riskColors[eventItem.risk_level] || '#9e9e9e'; 67 var riskLabel = KZ.riskLabels[eventItem.risk_level] || ''; 68 var i18n = KZ.config.i18n; 69 70 var stageBadge = ''; 71 if (eventItem.stage === 'signal') { 72 stageBadge = '<span style="padding: 4px 12px; background: #fff3cd; color: #856404; border-radius: 3px; font-size: 12px; font-weight: 600;">' + i18n.signal + '</span>'; 73 } else { 74 stageBadge = '<span style="padding: 4px 12px; background: #d4edda; color: #155724; border-radius: 3px; font-size: 12px; font-weight: 600;">' + i18n.confirmed + '</span>'; 75 } 76 77 var content = '<div style="padding: 24px;">'; 78 content += '<button id="kz-event-close" style="position: absolute; top: 12px; right: 12px; width: 24px; height: 24px; background: transparent; border: none; cursor: pointer; font-size: 24px; color: #999; line-height: 1; padding: 0;">×</button>'; 79 content += '<h2 style="margin: 0 0 16px 0; font-size: 20px; font-weight: 600; color: #333; padding-right: 30px;">' + (eventItem.title || '') + '</h2>'; 80 81 content += '<div style="display: flex; gap: 12px; margin-bottom: 16px;">'; 82 content += stageBadge; 83 if (riskLabel) { 84 content += '<div style="display: flex; align-items: center; gap: 8px;">'; 85 content += '<span style="width: 16px; height: 16px; background: ' + riskColor + '; border-radius: 50%; border: 2px solid white; box-shadow: 0 2px 4px rgba(0,0,0,0.2);"></span>'; 86 content += '<span style="font-size: 13px; color: #666;">' + i18n.riskLevel + ' ' + riskLabel + '</span>'; 87 content += '</div>'; 88 } 89 content += '</div>'; 90 91 if (eventItem.location || eventItem.region_name) { 92 content += '<div style="margin-bottom: 12px;">'; 93 content += '<strong style="color: #666; font-size: 13px;">' + i18n.location + ':</strong> '; 94 content += '<span style="color: #333; font-size: 14px;">' + (eventItem.location || eventItem.region_name) + '</span>'; 95 content += '</div>'; 96 } 97 98 if (eventItem.date) { 99 var dateOnly = KZ.formatDate(eventItem.date); 100 content += '<div style="margin-bottom: 16px;">'; 101 content += '<strong style="color: #666; font-size: 13px;">' + i18n.date + ':</strong> '; 102 content += '<span style="color: #333; font-size: 14px;">' + dateOnly + '</span>'; 103 content += '</div>'; 104 } 105 106 var sourcesHtml = KZ.buildSourcesHtml(eventItem, '14px'); 107 if (sourcesHtml) { 108 content += '<div style="padding-top: 16px; border-top: 1px solid #dee2e6;">'; 109 content += sourcesHtml; 110 content += '</div>'; 111 } 112 113 content += '</div>'; 114 115 $('#kz-event-popup').html(content); 116 $('#kz-event-overlay').fadeIn(200); 117 $('#kz-event-popup').fadeIn(200); 118 }; 119 120 // ========== Event card ========== 121 KZ.renderEventCard = function(eventItem) { 122 var riskColor = KZ.riskColors[eventItem.risk_level] || '#9e9e9e'; 123 var riskLabel = KZ.riskLabels[eventItem.risk_level] || ''; 124 var i18n = KZ.config.i18n; 125 126 var stageBadge = eventItem.stage === 'signal' 127 ? '<span style="padding: 2px 8px; background: #fff3cd; color: #856404; border-radius: 3px; font-size: 11px; font-weight: 600;">
127' + i18n.signal + '</span>' 128 : '<span style="padding: 2px 8px; background: #d4edda; color: #155724; border-radius: 3px; font-size: 11px; font-weight: 600;">' + i18n.confirmed + '</span>'; 129 130 var title = eventItem.title || eventItem.threat_name || ''; 131 var shortTitle = title.length > 100 ? title.substring(0, 100) + '...' : title; 132 var dateOnly = KZ.formatDate(eventItem.date); 133 134 var card = '<div class="kz-event-card" data-event-id="' + eventItem.id + '" data-lat="' + (eventItem.lat || '') + '" data-lng="' + (eventItem.lng || '') + '" style="padding: 12px; border: 1px solid #dee2e6; border-radius: 6px; margin-bottom: 8px; cursor: pointer; transition: box-shadow 0.2s;" onmouseover="this.style.boxShadow=\'0 2px 8px rgba(0,0,0,0.1)\'" onmouseout="this.style.boxShadow=\'none\'">'; 135 136 card += '<div style="display: flex; align-items: center; gap: 8px; margin-bottom: 8px;">'; 137 card += stageBadge; 138 card += '<span style="display: flex; align-items: center; gap: 6px;">'; 139 card += '<span style="width: 12px; height: 12px; background: ' + riskColor + '; border-radius: 50%; border: 2px solid white; box-shadow: 0 1px 3px rgba(0,0,0,0.2);"></span>'; 140 card += '<span style="font-size: 11px; color: #666;">' + riskLabel + '</span>'; 141 card += '</span>'; 142 card += '</div>'; 143 144 card += '<div style="font-weight: 600; font-size: 14px; color: #333; margin-bottom: 4px;">' + shortTitle + '</div>'; 145 card += '<div style="font-size: 13px; color: #666; margin-bottom: 6px;">' + (eventItem.location || eventItem.region_name || '') + '</div>'; 146 card += '<div style="font-size: 12px; color: #777; margin-bottom: 6px;">' + dateOnly + '</div>'; 147 148 var cardSourcesHtml = KZ.buildSourcesHtml(eventItem, '12px'); 149 if (cardSourcesHtml) { 150 card += '<div style="margin-bottom: 6px;">' + cardSourcesHtml + '</div>'; 151 } 152 153 card += '<div>'; 154 card += '<a href="#" class="kz-event-details-link" data-event-id="' + eventItem.id + '" style="font-size: 12px; color: #007bff; text-decoration: none; font-weight: 500;" onclick="event.preventDefault();">' + i18n.moreDetails + '</a>'; 155 card += '</div>'; 156 157 card += '</div>'; 158 return card; 159 }; 160 161 // ========== Render events list ========== 162 KZ.renderEventsList = function(events) { 163 var $list = $('#kz-events-list'); 164 if (!events || events.length === 0) { 165 $list.html('<div style="text-align: center; padding: 40px 20px; color: #999;">' + KZ.config.i18n.noEvents + '</div>'); 166 return; 167 } 168 169 var html = ''; 170 events.forEach(function(ev) { 171 html += KZ.renderEventCard(ev); 172 }); 173 $list.html(html); 174 }; 175 176 // ========== Click handlers ========== 177 $(document).ready(function() { 178 // Panel toggle 179 if (isPanelCollapsed) { 180 collapsePanel(); 181 } else { 182 expandPanel(); 183 } 184 185 $('#kz-panel-toggle').on('click', function(e) { 186 e.stopPropagation(); 187 if (sessionStorage.getItem('kzMapPanelCollapsed') === 'true') { 188 expandPanel(); 189 } else { 190 collapsePanel(); 191 } 192 }); 193 194 // Close event popup 195 $(document).on('click', '#kz-event-close, #kz-event-overlay', function() { 196 $('#kz-event-overlay').fadeOut(200); 197 $('#kz-event-popup').fadeOut(200); 198 }); 199 200 // Click on event card â pan to marker + highlight active card 201 $(document).on('click', '.kz-event-card', function(e) { 202 if ($(e.target).hasClass('kz-event-details-link') || $(e.target).closest('a').length) return; 203 var lat = parseFloat($(this).data('lat')); 204 var lng = parseFloat($(this).data('lng')); 205 // Highlight active card 206 $('.kz-event-card').css({'border-color': '#dee2e6', 'background': 'white'}); 207 $(this).css({'border-color': '#007bff', 'background': '#f0f7ff'}); 208 if (!isNaN(lat) && !isNaN(lng) && typeof KZ.focusOnMarker === 'function') { 209 KZ.focusOnMarker(lat, lng); 210 } 211 }); 212 213 // Click "More details" 214 $(document).on('click', '.kz-event-details-link', function(e) { 215 e.preventDefault(); 216 e.stopPropagation(); 217 var eventId = $(this).data('event-id'); 218 var eventItem = KZ.allMapData.find(function(ev) { return ev.id == eventId; }); 219 if (eventItem) KZ.showEventPopup(eventItem); 220 }); 221 222 $(window).on('resize', function() { 223 if (sessionStorage.getItem('kzMapPanelCollapsed') !== 'true') { 224 $('#kz-events-panel').css(getExpandedPanelCss()); 225 } 226 }); 227 });
228 229})(jQuery, window);
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.