PageSourceSearch

https://miwf.org/wp-content/plugins/facetwp/assets/js/src/map.js?ver=2.0

js miwf.org collected 2026-10-02 04:11:45 UTC 19,638 bytes, 503 lines download raw bytes

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.