1import {Controller} from '@hotwired/stimulus' 2import MarkerClusterer from '@googlemaps/markerclustererplus' 3import {CommonGoogleMaps} from 'common/google_maps' 4import logRequest from 'sites/log_requests' 5import URI from 'urijs' 6 7export default class extends Controller { 8 static values = {jobSearchParams: Object, postMarkersUrl:String} 9 10 connect() { 11 if (window.js_vars.bot) { 12 this.element.classList.add('map-placeholder') 13 } else { 14 if (window.js_vars.google_maps) { 15 this.loadGoogleMapsAndSetUp() 16 } 17 } 18 } 19 20 loadGoogleMapsAndSetUp() { 21 import('@googlemaps/js-api-loader').then(({setOptions, importLibrary}) => { 22 setOptions({key: window.js_vars.google_maps.api_key}) 23 Promise.all([importLibrary('maps'), importLibrary('core'), importLibrary('marker')]) 24 .then(([maps, core, markerLib]) => { 25 const googleMaps = {...maps, ...core, ...markerLib} 26 this.setUpMapWhenVisible(googleMaps) 27 }) 28 }) 29 } 30 31 setUpMapWhenVisible(googleMaps) { 32 const observer = new IntersectionObserver((entries, observer) => { 33 for (const entry of entries) { 34 if (entry.intersectionRatio > 0) { 35 this.initMap(googleMaps) 36 this.addStaticOrAsyncMarkers(googleMaps) 37 observer.disconnect() 38 } 39 } 40 }) 41 observer.observe(this.element) 42 } 43 44 initMap(googleMaps) { 45 logRequest('start_google_map') 46 const mapOptions = JSON.parse(this.element.dataset.mapOptions) 47 this.map = new googleMaps.Map(this.element, mapOptions) 48 this.map.setCenter(this.buildLocation(googleMaps, this.element.dataset.defaultCentreLat, this.element.dataset.defaultCentreLng)) 49 this.bounds = new googleMaps.LatLngBounds() 50 this.markerClusterer = new MarkerClusterer(this.map, [], { 51 styles: [ 52 { width: 30, height: 30, className: 'cluster-icon-1' }, 53 { width: 40, height: 40, className: 'cluster-icon-2' }, 54 { width: 50, height: 50, className: 'cluster-icon-3' }, 55 ], 56 clusterClass: 'cluster-icon', 57 maxZoom: 14, 58 }) 59 this.infoWindow = new googleMaps.InfoWindow() 60 this.map.addListener('click', () => this.infoWindow.close()) 61 } 62 63 addStaticOrAsyncMarkers(googleMaps) { 64 const markerData = this.element.dataset.markerData 65 if (markerData) { 66 this.updateMarkers(googleMaps, JSON.parse(markerData)) 67 const nearbyMarkerData = this.element.dataset.nearbyMarkerData 68 if (nearbyMarkerData) { 69 this.addMarkers(googleMaps, JSON.parse(nearbyMarkerData), '/images/google_maps/spotlight-poi2-blue.png') 70 } 71 } else { 72 this.fetchMarkersAndUpdateMap(googleMaps) 73 document.addEventListener('clinch:job_search_results_updated',
73({detail: {jobSearchParamsAsJson}}) => { 74 this.jobSearchParamsValue = jobSearchParamsAsJson ? JSON.parse(jobSearchParamsAsJson) : {} 75 this.fetchMarkersAndUpdateMap(googleMaps) 76 }) 77 } 78 } 79 80 fetchMarkersAndUpdateMap(googleMaps) { 81 if (this.hasPostMarkersUrlValue) { 82 const markerApiPathWithParams= URI(this.postMarkersUrlValue).addSearch(this.jobSearchParamsValue).toString() 83 fetch(markerApiPathWithParams) 84 .then(response => response.json()) 85 .then(({markers}) => this.updateMarkers(googleMaps, markers)) 86 } 87 } 88 89 buildLocation(googleMaps, lat, lng) { 90 return new googleMaps.LatLng(parseFloat(lat || 0.0), parseFloat(lng || 0.0)) 91 } 92 93 updateMarkers(googleMaps, allMarkerData) { 94 this.markerClusterer.clearMarkers() 95 this.addMarkers(googleMaps, allMarkerData) 96 } 97 98 addMarkers(googleMaps, allMarkerData, icon = null) { 99 const markers = allMarkerData.map((markerData) => this.createMarker(googleMaps, markerData, icon)) 100 101 if (markers.length) { 102 this.markerClusterer.addMarkers(markers) 103 this.bounds = new googleMaps.LatLngBounds() 104 for (const marker of this.markerClusterer.getMarkers()) { 105 this.bounds.extend(marker.getPosition()) 106 } 107 this.map.setCenter(this.bounds.getCenter()) 108 CommonGoogleMaps.setZoom(this.map, this.bounds, parseInt(this.element.dataset.defaultZoom)) 109 } 110 } 111 112 createMarker(googleMaps, {lat, lng, info_window_content}, icon) { 113 const marker = new googleMaps.Marker({position: this.buildLocation(googleMaps, lat, lng), icon: icon}) 114 115 marker.addListener('click', () => { 116 this.infoWindow.close() 117 this.infoWindow.setContent(info_window_content) 118 this.infoWindow.open(this.map, marker) 119 }) 120 return marker 121 } 122}
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.