1var COMMENTS_MAP_DEFAULT_WIDTH = 600; 2var COMMENTS_MAP_DEFAULT_ZOOM = 13; 3var COMMENTS_MAP_CENTER = { lat: 37, lng: -95 }; 4 5// Comments Map Pin Scaling Configuration 6var COMMENTS_MAP_SCALING = { 7 baseSizes: { 8 xs: { width: 15, height: 15 }, 9 sm: { width: 20, height: 20 }, 10 md: { width: 25, height: 25 }, 11 lg: { width: 30, height: 30 }, 12 xl: { width: 35, height: 35 } 13 }, 14 zoomScaling: { 15 minZoom: 3, 16 maxZoom: 18, 17 minScale: 0.5, 18 maxScale: 2.0, 19 defaultScale: 1.0 20 } 21}; 22 23// Calculate zoom-responsive scale factor for comments map 24function calculateCommentsMapZoomScale(zoom) { 25 var config = COMMENTS_MAP_SCALING.zoomScaling; 26 zoom = Math.max(config.minZoom, Math.min(config.maxZoom, zoom)); 27 var zoomRange = config.maxZoom - config.minZoom; 28 var zoomProgress = (zoom - config.minZoom) / zoomRange; 29 var scaleRange = config.maxScale - config.minScale; 30 return config.minScale + zoomProgress * scaleRange; 31} 32 33// Get scaled size for comments map pins 34function getCommentsMapScaledPinSize(sizeCategory, zoom) { 35 var baseSize; 36 if (typeof sizeCategory === "string") { 37 baseSize = 38 COMMENTS_MAP_SCALING.baseSizes[sizeCategory] || 39 COMMENTS_MAP_SCALING.baseSizes.sm; 40 } else if (sizeCategory && sizeCategory.width && sizeCategory.height) { 41 baseSize = sizeCategory; 42 } else { 43 baseSize = COMMENTS_MAP_SCALING.baseSizes.sm; // Default to small for comments 44 } 45 46 var scaleFactor = calculateCommentsMapZoomScale(zoom); 47 return { 48 width: Math.round(baseSize.width * scaleFactor), 49 height: Math.round(baseSize.height * scaleFactor) 50 }; 51} 52 53function CommentsMap(config) { 54 var self = this; 55 self.pollComments = config.pollComments || []; 56 self.$container = config.$container; 57 self.mapWidth = config.mapWidth || COMMENTS_MAP_DEFAULT_WIDTH; 58 self.zoom = config.zoom || COMMENTS_MAP_DEFAULT_ZOOM; 59 self.kmlFiles = config.kmlFiles || []; 60 61 //This is the Cityzen Map Item associated with this poll. See Map.cs 62 self.chart = config.chart || {}; 63 self.insight = config.insight || config.chart.insight || {}; 64 65 self.$mapContainer = self.$container.find(".map-container"); 66 self.$container.find(".ModeSwitch").remove(); 67 self.$mapContainer = 68 self.$mapContainer || 69 $("<div></div>") 70 .css("maxWidth", self.mapWidth) 71 .css("margin", "10px auto") 72 .height(self._getMapHeight()) 73 .insertBefore($("center", self.$container)); 74 75 self.marks = []; 76 $.each(self.pollComments, function (ind, commentData) { 77 if (commentData.parentId === 0) { 78 self.marks.push({ 79 infoWindowHtml: self.createInfoWindowHtml(commentData), 80 commentText: commentData.commentText, 81 lat: commentData.lat, 82 lng: commentData.long, 83 linePoints: commentData.linePoints 84 ? JSON.parse(commentData.linePoints) 85 : [], 86 pinImageId: commentData.pinImageId 87 }); 88 } 89 }); 90 91 var defaultBasemap = "roadmap"; 92 if (self.chart && self.chart.getMapOptions) { 93 defaultBasemap = self.chart.getMapOptions().defaultBaseMap; 94 } 95 96 // AB#9979: name the map region before Maps takes the element over (see map-a11y.js). 97 window.piMapA11y && 98 window.piMapA11y.describe( 99 self.$mapContainer, 100 "Comment location map", 101 self.marks.length 102 ? "Map plotting the location of " + 103 self.marks.length + 104 (self.marks.length === 1 ? " comment." : " comments.") 105 : "Map of comment locations. No located comments to display." 106 ); 107 108 self.map = new google.maps.Map(self.$mapContainer[0], { 109 zoom: self.zoom, 110 center: COMMENTS_MAP_CENTER, 111 zoomControl: true, 112 mapTypeId: defaultBasemap, 113 gestureHandling: "cooperative", 114 mapTypeControl: false, 115 clickableIcons: false, 116 streetViewControl: false, 117 fullscreenControl: true 118 }); 119 120 $.each(self.kmlFiles, function (ind, kmlFile) { 121 var kmlLayer = new google.maps.KmlLayer({ 122 url: kmlFile.serverFullPathAndFileName, 123 map: self.map, 124 preserveViewport: true, 125 zIndex: 1 126 });
127 }); 128 129 var bounds = new google.maps.LatLngBounds(); 130 131 self.infowindow = null; 132 self.markers = []; 133 self.polylines = []; 134 $.each(self.marks, function (ind, mark) { 135 if (mark.linePoints.length) { 136 if ($.trim(mark.commentText) !== "") { 137 var polyline = new google.maps.Polyline({ 138 path: mark.linePoints, 139 strokeOpacity: 1.0, 140 strokeColor: "#0000BF", 141 strokeWeight: 3, 142 map: self.map, 143 zIndex: 2 144 }); 145 146 polyline.addListener( 147 "click", 148 function () { 149 if (this.infowindow) { 150 this.infowindow.close(); 151 } 152 153 this.infowindow = new google.maps.InfoWindow({ 154 content: mark.infoWindowHtml, 155 position: { lat: mark.lat, lng: mark.lng } 156 }); 157 158 this.infowindow.open(this.map, polyline); 159 }.bind(self) 160 ); 161 162 self.polylines.push(polyline); 163 164 $.each(mark.linePoints, function (ind, point) { 165 bounds.extend(point); 166 }); 167 } else { 168 var polyline = new google.maps.Polyline({ 169 path: mark.linePoints, 170 strokeOpacity: 0.5, 171 strokeColor: "#0000BF", 172 strokeWeight: 3, 173 map: self.map, 174 zIndex: 2 175 }); 176 177 self.polylines.push(polyline); 178 179 $.each(mark.linePoints, function (ind, point) { 180 bounds.extend(point); 181 }); 182 } 183 } else { 184 const pinIcon = { 185 path: "M348.748,106.141C348.748,47.532,301.212,0,242.604,0c-58.609,0-106.139,47.532-106.139,106.141 c0, 53.424, 39.61, 97.196, 90.976, 104.598v274.473h30.327V210.739C309.119, 203.337, 348.748, 159.565, 348.748, 106.141z", 186 fillColor: "#0000ff", 187 fillOpacity: 1, 188 scale: 0.05, 189 strokeColor: "#0000ff", 190 strokeOpacity: 1, 191 strokeWeight: 1 192 }; 193 194 if (mark.lat == 0 || mark.lng == 0) return; 195 196 if ($.trim(mark.commentText) !== "") { 197 var currentZoom = self.map.getZoom() || COMMENTS_MAP_DEFAULT_ZOOM; 198 var markerIcon; 199 200 if (mark.pinImageId == "-1") { 201 markerIcon = pinIcon; 202 } else { 203 // Calculate scaled size for custom pin 204 var scaledSize = getCommentsMapScaledPinSize( 205 { width: 20, height: 20 }, 206 currentZoom 207 ); 208 markerIcon = { 209 url: "/img/" + mark.pinImageId + "_20_20", 210 scaledSize: new google.maps.Size( 211 scaledSize.width, 212 scaledSize.height 213 ) 214 }; 215 } 216 217 const marker = new google.maps.Marker({ 218 position: mark, 219 map: self.map, 220 icon: markerIcon, 221 zIndex: 3 222 }); 223 224 // Store original pin info for scaling 225 if (mark.pinImageId != "-1") { 226 marker._originalPinSize = { width: 20, height: 20 }; 227 marker._pinImageUrl = "/img/" + mark.pinImageId + "_20_20"; 228 } 229 230 marker.addListener( 231 "click", 232 function () { 233 if (this.infowindow) { 234 this.infowindow.close(); 235 } 236 237 this.infowindow = new google.maps.InfoWindow({ 238 content: mark.infoWindowHtml 239 }); 240 241 this.infowindow.open(this.map, marker); 242 }.bind(self) 243 ); 244 245 self.markers.push(marker); 246 bounds.extend(marker.getPosition()); 247 } else { 248 pinIcon.fillColor = "#ffffff"; 249 250 var currentZoom = self.map.getZoom() || COMMENTS_MAP_DEFAULT_ZOOM; 251 var markerIcon; 252 253 if (mark.pinImageId == "-1") { 254 markerIcon = pinIcon; 255 } else { 256 // Calculate scaled size for custom pin 257 var scaledSize = getCommentsMapScaledPinSize( 258 { width: 20, height: 20 }, 259 currentZoom 260 ); 261 markerIcon = { 262 url: "/img/" + mark.pinImageId + "_20_20", 263 scaledSize: new google.maps.Size( 264 scaledSize.width, 265 scaledSize.height 266 ) 267 }; 268 } 269 270 const marker = new google.maps.Marker({ 271 position: mark, 272 map: self.map, 273 icon: markerIcon, 274 zIndex: 3 275 }); 276 277 // Store original pin info for scaling 278 if (mark.pinImageId != "-1") { 279 marker._originalPinSize = { width: 20, height: 20 }; 280 marker._pinImageUrl = "/img/" + mark.pinImageId + "_20_20"; 281 } 282 283 self.markers.push(marker); 284 bounds.extend(marker.getPosition()); 285 } 286 } 287 }); 288 self.chart.map = self.map; 289 self.chart.lookupPresetMapBoundsOrApplyDefaults(bounds); 290 if (self.needsZoomInit) self.applyZoomToMapImages(); 291 self.chart.addDefaultMapListeners(); 292 self.chart.addMapLayerManager(); 293 294 // Add zoom change listener for pin scaling 295 self.map.addListener("zoom_changed", function () { 296 self.updatePinSizes(); 297 }); 298} 299 300// Update pin sizes based on current zoom level 301CommentsMap.prototype.updatePinSizes = function () { 302 var self = this; 303 var currentZoom = this.map.getZoom(); 304 305 // Update all markers with stored pin size information 306 if (this.markers && Array.isArray(this.markers)) {
307 this.markers.forEach(function (marker) { 308 if (marker._originalPinSize && marker._pinImageUrl) { 309 var scaledSize = getCommentsMapScaledPinSize( 310 marker._originalPinSize, 311 currentZoom 312 ); 313 314 marker.setIcon({ 315 url: marker._pinImageUrl, 316 scaledSize: new google.maps.Size(scaledSize.width, scaledSize.height) 317 }); 318 } 319 }); 320 } 321}; 322 323CommentsMap.prototype.addShadedBoundary = function () { 324 // _redrawBoundaries in mappoll.js 325 var self = this; 326 var allWorld = [ 327 new google.maps.LatLng(85, 180), 328 new google.maps.LatLng(85, 90), 329 new google.maps.LatLng(85, 0), 330 new google.maps.LatLng(85, -90), 331 new google.maps.LatLng(85, -180), 332 new google.maps.LatLng(0, -180), 333 new google.maps.LatLng(-85, -180), 334 new google.maps.LatLng(-85, -90), 335 new google.maps.LatLng(-85, 0), 336 new google.maps.LatLng(-85, 90), 337 new google.maps.LatLng(-85, 180), 338 new google.maps.LatLng(0, 180), 339 new google.maps.LatLng(85, 180) 340 ]; 341 342 self.map.data.setStyle({ 343 //cursor: "not-allowed", 344 fillColor: "#000000", 345 fillOpacity: 0.25, 346 strokeWeight: 0 347 }); 348 if (self.boundaryArray.length > 0) { 349 self._boundariesPolygon = new google.maps.Polygon({ 350 paths: self.boundaryArray 351 }); 352 353 self.map.data.add({ 354 geometry: new google.maps.Data.Polygon([allWorld, self.boundaryArray]) 355 }); 356 } else { 357 this._boundariesPolygon = null; 358 } 359}; 360 361// map must fit to screen height 362CommentsMap.prototype._getMapHeight = function () { 363 return Math.min(screen.height - 50, 500); 364}; 365 366CommentsMap.prototype.createInfoWindowHtml = function (commentData) { 367 var self = this; 368 var result = ""; 369 if ( 370 commentData.attachmentsJSON != null && 371 commentData.attachmentsJSON.length 372 ) { 373 var attachmentInfo = JSON.parse(commentData.attachmentsJSON); 374 if (attachmentInfo.Id && attachmentInfo.Id.length) { 375 result += 376 '<div class="comment-info-img"><img src="/img/' + 377 attachmentInfo.Id + 378 "_300_300\" data-action='zoom' data-zoom-src=\"/img/" + 379 attachmentInfo.Id + 380 '"></div>'; 381 self.needsZoomInit = true; 382 } 383 } 384 result += 385 '<div class="comment-info-text">' + commentData.commentText + "</div>"; 386 return result; 387}; 388 389CommentsMap.prototype.applyZoomToMapImages = function () { 390 var self = this; 391 ScriptLoader.loadScriptArray( 392 ScriptLoader.scriptArrays.magnificPopup, 393 function () { 394 window.TOOLS && 395 TOOLS.requireCSSFile("/lib/magnific-popup.js/magnific-popup.min.css"); 396 self.$container.find("[data-action='zoom']").each(function () { 397 var $img = $(this); 398 if ($img.parent().is("a")) return; 399 var zoomSrc = 400 $img.data("original") || $img.data("zoom-src") || $img.attr("src"); 401 var $a = $("<a>") 402 .attr("href", zoomSrc) 403 .attr("title", $img.attr("title") || $img.attr("alt")) 404 .attr( 405 "aria-label", 406 "Zoom image " + ($img.attr("title") || $img.attr("alt")) 407 ) 408 .addClass("image-zoom-popup"); 409 $img.wrap($a); 410 }); 411 self.$container.find(".image-zoom-popup").magnificPopup({ 412 closeOnContentClick: false, 413 type: "image", 414 closeBtnInside: true, 415 autoFocusLast: true, 416 image: { 417 verticalFit: true 418 }, 419 zoom: { 420 enabled: true, 421 duration: 300 // don't foget to change the duration also in CSS 422 } 423 }); 424 } 425 ); 426};
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.