1var FWP_MAP = FWP_MAP || {}; 2 3(function($) { 4 5 FWP_MAP.markersArray = []; 6 FWP_MAP.markerLookup = {}; 7 FWP_MAP.is_filtering = false; 8 FWP_MAP.is_zooming = false; 9 FWP_MAP.map_loaded = false; 10 11 // Get markers for a given post ID 12 FWP_MAP.get_post_markers = function(post_id) { 13 var output = []; 14 if ('undefined' !== typeof FWP_MAP.markerLookup[post_id]) { 15 var arrayOfIndexes = FWP_MAP.markerLookup[post_id]; 16 for (var i = 0; i < arrayOfIndexes.length; i++) { 17 var index = FWP_MAP.markerLookup[post_id][i]; 18 output.push(FWP_MAP.markersArray[index]); 19 } 20 } 21 return output; 22 } 23 24 FWP.hooks.addAction('facetwp/refresh/map', function($this, facet_name) { 25 26 var selected_values = []; 27 28 // Set URL values when Enable Map Filtering is enabled 29 if ( FWP_MAP.is_filtering && ! FWP.is_popstate && FWP_MAP.map_loaded ) { 30 var currentZoom = FWP_MAP.map.getZoom(); 31 selected_values = FWP_MAP.map.getBounds().toUrlValue().split(','); 32 selected_values.push(currentZoom.toString()); // Add zoom level to be used to correct zooms on back button 33 } 34 35 // Using back button when Enable Map Filtering is enabled 36 else if ( FWP_MAP.is_filtering && FWP.is_popstate === true ) { 37 38 // Get bounds and zoom from URL. 39 // Needed because FWP.facets[facet_name] is undefined. Not sure why, seems related to async loading. 40 FWP.loadFromHash(); 41 42 // Load map from URL as long as there are URL values when using back button 43 if ( FWP.facets[facet_name] && FWP.facets[facet_name].length > 0 ) { 44 do_refresh_from_url(FWP.facets[facet_name]); 45 } 46 47 // On the last back button refresh, when URL is empty, reset Enable Map Filtering button and is_popstate 48 // Prevents further complications when starting anew with zooming/panning while Enable Map Filtering is enabled 49 else { 50 FWP.is_popstate = undefined;
51 $('.facetwp-map-filtering').trigger('click'); 52 } 53 } 54 55 FWP.facets[facet_name] = selected_values; 56 FWP.frozen_facets[facet_name] = 'hard'; 57 58 }); 59 60 FWP.hooks.addAction('facetwp/reset', function() { 61 $.each(FWP.facet_type, function(type, name) { 62 if ('map' === type) { 63 FWP.frozen_facets[name] = 'hard'; 64 } 65 }); 66 }); 67 68 FWP.hooks.addFilter('facetwp/selections/map', function(label, params) { 69 return FWP_JSON['map']['resetText']; 70 }); 71 72 function do_refresh() { 73 if (FWP_MAP.is_filtering && ! FWP_MAP.is_zooming) { 74 FWP.autoload(); 75 } 76 77 FWP_MAP.is_zooming = false; 78 } 79 80 function do_refresh_from_url( selected_values ) { 81 82 const sw = { 83 lat: parseFloat(selected_values[0]), 84 lng: parseFloat(selected_values[1]) 85 }; 86 const ne = { 87 lat: parseFloat(selected_values[2]), 88 lng: parseFloat(selected_values[3]) 89 }; 90 91 const popstate_bounds = new google.maps.LatLngBounds(sw, ne); 92 93 FWP_MAP.map.fitBounds( popstate_bounds ); 94 95 FWP_MAP.is_zooming = true; // Prevent do_refresh() 96 97 // fitBounds should work by itself in theory, but zooms are off for some reason (Google related I think) 98 // So we get the zoom from the URL also and set it after the fitBounds 99 // setZoom does not work directly because of fitbounds triggers async zooming, so added in a listener 100 google.maps.event.addListenerOnce(FWP_MAP.map, 'bounds_changed', function() { 101 FWP_MAP.map.setZoom( Number(selected_values[4]) ); 102 }); 103 } 104
105 $().on('click', '.facetwp-map-filtering', function() { 106 var $this = $(this); 107 108 if ($this.hasClass('enabled')) { 109 $this.text(FWP_JSON['map']['filterText']); 110 FWP_MAP.is_filtering = false; 111 FWP.autoload(); 112 } 113 else { 114 $this.text(FWP_JSON['map']['resetText']); 115 FWP_MAP.is_filtering = true; 116 FWP.autoload(); 117 } 118 119 $this.toggleClass('enabled'); 120 }); 121 122 FWP_MAP.createIcon = function(icon) { // back compat for icon arg for marker pin 123 const imgTag = document.createElement("img"); 124 if ( 'string' == typeof icon ) { 125 imgTag.src = icon; 126 return imgTag; 127 } 128 if ( 'string' == typeof icon.url ) { 129 imgTag.src = icon.url; 130 if ( icon.scaledSize ) { 131 if ( icon.scaledSize.width ) imgTag.width = icon.scaledSize.width; 132 if ( icon.scaledSize.height ) imgTag.height = icon.scaledSize.height; 133 } 134 return imgTag; 135 } 136 return false; 137 } 138 139 FWP_MAP.init = async function() { 140 if ('undefined' === typeof FWP.settings.map || '' === FWP.settings.map) { 141 return; 142 } 143 144 if ('object' !== typeof google || 'object' !== typeof google.maps) { 145 return; 146 } 147 148 var config = FWP.settings.map.config; 149 150 const { Map, InfoWindow } = await google.maps.importLibrary("maps"); 151 const { AdvancedMarkerElement, PinElement } = await google.maps.importLibrary("marker"); 152 153 // back compat for example 154 // https://facetwp.com/help-center/facets/facet-types/map/advanced-map-customizations/#example-4-change-the-marker-icon-when-an-infowindow-is-open 155 AdvancedMarkerElement.prototype.setIcon = function(icon) { 156 if ( !icon ) { 157 let pin = new PinElement(); 158 this.content = pin.element; 159 } else { 160 newIcon = FWP_MAP.createIcon(icon); 161 this.content = newIcon; 162 } 163 } 164 165 if (! FWP_MAP.map_loaded ) { 166 167 FWP_MAP.map = new Map(document.getElementById('facetwp-map'), FWP.settings.map.init); 168 FWP_MAP.infoWindow = new InfoWindow(); 169 170 FWP_MAP.map.addListener('dragend', function() { 171 do_refresh(); 172 }); 173 174 FWP_MAP.map.addListener('zoom_changed', function() { 175 do_refresh(); 176 }); 177 178 window.addEventListener('resize', FWP.helper.debounce(function() { 179 var center = FWP_MAP.map.getCenter(); 180 google.maps.event.trigger(FWP_MAP.map, 'resize'); 181 FWP_MAP.map.setCenter(center); 182 }, 500)); 183 184 google.maps.event.addListener(FWP_MAP.map, 'click', function() { 185 FWP_MAP.infoWindow.close(); 186 }); 187 188 FWP_MAP.oms = new OverlappingMarkerSpiderfier(FWP_MAP.map, config.spiderfy); 189 190 // If first page load has map URL values 191 var selected_values = FWP.facets[FWP_JSON.map.facet_name]; 192 if (selected_values && selected_values.length > 0) { 193 194 FWP_MAP.is_filtering = true; // Button was already enabled with PHP 195 do_refresh_from_url( selected_values ); 196 } 197 198 FWP_MAP.map_loaded = true; 199 200 } else if ( 'undefined' !== FWP.is_popstate && false === FWP.is_popstate ) { 201 202 // restore map 203 let mapHTML = FWP_MAP.map.getDiv(); 204 let mapContainer = document.getElementById( 'facetwp-map'); 205 let mapParent = mapContainer.parentElement; 206 mapParent.replaceChild(mapHTML,mapContainer); 207 clearOverlays(); // Prevent cluster count issue when using back button and reset 208 209 } else { 210 clearOverlays(); 211 } 212 213 // this needs to re-init on each refresh 214 FWP_MAP.bounds = new google.maps.LatLngBounds(); 215 FWP_MAP.contentCache = {}; 216 217 $.each(FWP.settings.map.locations, function(obj, idx) { 218 219 const args = { position: obj.position, zIndex : obj.zIndex, title : obj.title }; // advanced marker properties 220 args.gmpDraggable = obj.draggable ?? obj.gmpDraggable; // back compat for legacy draggable 221 222 // setting content in facetwp_map/marker/content short circuits other settings for marker 223 let content = FWP.hooks.applyFilters('facetwp_map/marker/content', null, obj, PinElement); 224 225 if ( !content && obj.markerHtml ) { // new argument for creating pin from custom html 226 227 content = document.createElement("div"); 228 content.innerHTML = obj.markerHtml; 229 230 } 231 232 if ( !content && obj.icon ) { // back compat for icon setting for legacy markers 233 content = FWP_MAP.createIcon(obj.icon); 234 235 } 236 237 if ( !content ) { // create a new pinv 238 239 const { label = '' } = obj; 240 const { glyphHtml = '', pinClass = '', ...pinOptions } = obj.pinOptions || {}; 241 242 if ( label ) { // back compat for label setting for legacy markers 243 pinOptions.glyph = label.text; 244 if ( label.color ) pinOptions.glyphColor = label.color; 245 } 246 247 if ( glyphHtml ) { // new argument for HTML in glyph 248 const glyph = document.createElement("div"); 249 glyph.innerHTML = glyphHtml; 250 pinOptions.glyph = glyph; 251 } 252 253 let pin = new PinElement(pinOptions); 254 255 if ( pinClass ) { // new argument for class name in pin 256 pin.element.classList.add(pinClass); 257 } 258 259 content = pin.element; 260 261 } 262 263 args.content = content; 264 265 const marker = new AdvancedMarkerElement(args); 266 267 marker.post_id = obj.post_id; 268 marker.infoWindowContent = obj.infoWindowContent; 269 marker.clickable = 'undefined' !== typeof obj.clickable ? obj.clickable : true; 270 271 var isProgrammaticClick = false;
272 273 google.maps.event.addListener(marker, 'spider_click', function() { 274 275 if ( false == marker.clickable ) { 276 // Custom click handler 277 FWP.hooks.doAction('facetwp_map/marker/click', marker); 278 return; 279 } 280 281 if ('undefined' !== typeof marker.infoWindowContent) { 282 FWP_MAP.infoWindow.setContent(marker.infoWindowContent); 283 } 284 else if ('undefined' === typeof FWP_MAP.contentCache[marker.post_id]) { 285 FWP_MAP.infoWindow.setContent('Loading...'); 286 FWP_MAP.infoWindow.open(FWP_MAP.map, marker); 287 288 return $.post(FWP_JSON.map.ajaxurl, { 289 action: 'facetwp_map_marker_content', 290 facet_name: FWP_JSON.map.facet_name, 291 post_id: marker.post_id 292 }, { 293 dataType: 'text', 294 contentType: 'application/x-www-form-urlencoded', 295 done: (resp) => { 296 FWP_MAP.contentCache[marker.post_id] = resp; 297 FWP_MAP.infoWindow.setContent(resp); 298 FWP.hooks.doAction('facetwp_map/marker/click', marker); 299 300 // Trigger extra click to reposition marker/infowindow withint viewport 301 // Check if the click event was triggered programmatically, to prevent recursion. 302 if (!isProgrammaticClick) { 303 isProgrammaticClick = true; 304 // Trigger a click programmatically after the marker has been clicked 305 google.maps.event.trigger(marker, 'click'); 306 isProgrammaticClick = false; // Reset the flag after the programmatic click 307 } 308 } 309 }); 310 } 311 else { 312 var data = FWP_MAP.contentCache[marker.post_id]; 313 FWP_MAP.infoWindow.setContent(data); 314 } 315 316 FWP_MAP.infoWindow.open(FWP_MAP.map, marker); 317 318 // Custom click handler 319 FWP.hooks.doAction('facetwp_map/marker/click', marker); 320 }); 321 322 // Custom mouseover handler 323 marker.content.addEventListener('mouseenter', function(e) { 324 FWP.hooks.doAction('facetwp_map/marker/mouseover', marker); 325 }); 326 327 // Custom mouseout handler 328 marker.content.addEventListener('mouseleave', function(e) { 329 FWP.hooks.doAction('facetwp_map/marker/mouseout', marker); 330 }); 331 332 FWP_MAP.oms.addMarker(marker); 333 FWP_MAP.markersArray.push(marker); 334 FWP_MAP.bounds.extend(marker.position); 335 336 // Create an object to lookup markers based on post ID 337 if ( 'undefined' !== typeof FWP_MAP.markerLookup[obj.post_id]) { 338 FWP_MAP.markerLookup[obj.post_id].push(idx); 339 } 340 else { 341 FWP_MAP.markerLookup[obj.post_id] = [idx]; 342 } 343 344 FWP.hooks.doAction('facetwp_map/marker/added', marker, obj); 345 }); 346 347 var has_results = FWP.settings.map.locations.length > 0; 348 var fit_bounds = FWP.hooks.applyFilters('facetwp_map/fit_bounds', ! FWP_MAP.is_filtering); 349 350 if (fit_bounds && has_results) { 351 FWP_MAP.map.fitBounds(FWP_MAP.bounds); 352 } 353 else if (! FWP_MAP.is_filtering && (0 !== config.default_lat || 0 !== config.default_lng)) { 354 FWP_MAP.map.setCenter({ 355 lat: parseFloat(config.default_lat), 356 lng: parseFloat(config.default_lng) 357 }); 358 FWP_MAP.is_zooming = true; 359 FWP_MAP.map.setZoom(config.default_zoom); 360 } 361 362 if ( 'undefined' != typeof markerClusterer && 'undefined' !== typeof config.cluster ) { 363 // default args 364 var clusterargs = { 365 algorithmOptions: { 366 // backwards compatibility 367 maxZoom: config.cluster.maxZoom, 368 minPoints: config.cluster.minimumClusterSize 369 }, 370 }; 371 372 if ( false == config.cluster.zoomOnClick ) { 373 clusterargs.onClusterClick = false; // backwards compatibility 374 } 375 376 clusterargs = Object.assign( FWP.hooks.applyFilters('facetwp_map/clusterer',
376clusterargs, config.cluster ), { markers: FWP_MAP.markersArray, map: FWP_MAP.map } ); 377 378 FWP_MAP.mc = new markerClusterer.MarkerClusterer( clusterargs ); 379 } 380 381 $().trigger('facetwp-maps-loaded'); 382 }; 383 384 FWP.hooks.addFilter('facetwp_map/clusterer', function (clusterargs, clusterconfig) { 385 386 if (typeof clusterconfig.imagePath !== 'undefined' && clusterconfig.imagePath !== '' && typeof clusterconfig.imageExtension !== 'undefined' && clusterconfig.imageExtension !== '') { 387 388 // Backwards compatible custom cluster icon set with config.cluster.imagePath and config.cluster.imageExtension. 389 var imageRenderer = { 390 391 render({ count, position }, stats) { 392 393 const sizes = [53, 56, 66, 78, 90]; 394 let i = 0; 395 var dv = count; 396 while (dv !== 0) { 397 dv = parseInt(dv / 10, 10); 398 i++; 399 } 400 i = Math.min(i, 5); 401 402 const size = sizes[i - 1]; 403 404 const imgsrc = clusterconfig.imagePath + i + '.' + clusterconfig.imageExtension; 405 406 const title = `Cluster of ${count} markers`; 407 408 const zIndex = 1000000 + count; // There is no equivalent of google.maps.Marker.MAX_ZINDEX. This is the legacy value. 409 410 const pinImgString = ` 411 <div style="position: relative;"> 412 <img src="${imgsrc}" width="${size}" height="${size}" alt="clustericon"/> 413 <span class="count" style="position: absolute; left: 0; top: 0; display: block; width: 100%; text-align: center; font-size: 11px; line-height:${size}px; color: #000000; font-weight: bold;">${count}</span> 414 </div>`; 415 416 const parser = new DOMParser(); 417 const pinImg = parser.parseFromString(pinImgString, 'text/html').body.firstChild; 418 419 const clusterOptions = { 420 map : clusterconfig.map, 421 position, 422 zIndex, 423 title, 424 content: pinImg, 425 }; 426 427 return new google.maps.marker.AdvancedMarkerElement(clusterOptions); 428 } 429 } 430 431 clusterargs.renderer = imageRenderer; 432 433 } else if (typeof clusterconfig.styles !== 'undefined' && clusterconfig.styles !== '') { 434 435 // Backwards compatible custom cluster icon set with args['config']['cluster']['styles'][]. 436 var stylesRenderer = { 437 438 render({ count, position }, stats) { 439 let i = 0; 440 var dv = count; 441 while (dv !== 0) { 442 dv = parseInt(dv / 10, 10); 443 i++; 444 } 445 i = Math.min(i, 5); 446 447 const styles = clusterconfig.styles[i - 1]; 448 const width = styles.width; 449 const height = styles.height; 450 const textColor = styles.textColor ?? '#000000'; 451 const textSize = styles.textSize ?? 11; 452 453 const imgsrc = styles.url; 454 455 const title = `Cluster of ${count} markers`; 456 457 const zIndex = 1000000 + count; // There is no equivalent of google.maps.Marker.MAX_ZINDEX. This is the legacy value. 458 459 const pinImgString = ` 460 <div style="position: relative;"> 461 <img src="${imgsrc}" width="${width}" height="${height}" alt="clustericon"/> 462 <span class="count" style="position: absolute; left: 0; top: 0; display: block; width: 100%; text-align: center; font-size: ${textSize}px; line-height:${height}px; color: ${textColor}; font-weight: bold;">${count}</span> 463 </div>`; 464 465 const parser = new DOMParser(); 466 const pinImg = parser.parseFromString(pinImgString, 'text/html').body.firstChild; 467 468 const clusterOptions = { 469 map : clusterconfig.map, 470 position, 471 zIndex, 472 title, 473 content: pinImg, 474 }; 475 476 return new google.maps.marker.AdvancedMarkerElement(clusterOptions); 477 } 478 } 479 480 clusterargs.renderer = stylesRenderer; 481 } 482 483 return clusterargs; 484 485 }); 486 487 $().on('facetwp-loaded', function() { 488 FWP_MAP.init(); 489 }); 490 491 // Clear markers 492 function clearOverlays() { 493 FWP_MAP.oms.removeAllMarkers(); 494 FWP_MAP.markersArray = [];
495 FWP_MAP.markerLookup = {}; 496 497 // clear clusters 498 if ('undefined' !== typeof FWP_MAP.mc) { 499 FWP_MAP.mc.clearMarkers(); 500 } 501 } 502 503})(fUtil);
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.