{"version":3,"file":"Src_Scripts_components_map_js.0e17c54972f87bbf52e3.js","sources":["webpack://haveselskabet/./Src/Scripts/components/map.js","webpack://haveselskabet/./Src/Scripts/functionality-classes/Locator-1-Markercluster.js","webpack://haveselskabet/./Src/Scripts/functionality-classes/google-maps.js","webpack://haveselskabet/./node_modules/load-google-maps-api/index.js"],"sourcesContent":["import GoogleMaps from '../functionality-classes/google-maps';\nimport MapLocation from '../models/map-location-types/map-location';\n\n/*\n The Map class in an abstraction layer on top of GoogleMaps class, and is used to instaniate the GoogleMaps class in a safe manner.\n It can also be used as an refactoring entrypoint to abstract the GoogleMaps class away and use another map provider.\n The Map class takes a list of locations, that can be either \"typed-locations\" or \"untyped-locations\".\n Untyped locations are regular data objects, where typed locations have been instantiated to a class that inherits from MapLocation.\n This class ensures that all locations are typed, by instantiating them if needed using a component repository.\n\n ARGS:\n -googleMaps: object that contains args for GoogleMaps component (locations can be excluded since they are overridden by the location or typedLocation property below)\n -locations : array of objects that the Map will try to instantiate to MapLocation instances for GoogleMaps\n -typedLocations (optional): array of instances of MapLocation or subclasses thereof. If this is passed, it will be used instead of \"locations\"\n -fitBoundsOnLocationSet (optional): boolean that indicates if the map view should be changed to fit bounds of locations when locations are set. Defaults to true.\n -mapClassRepository (optional): instance of ComponentRepository\n*/\nexport default class Map {\n constructor(elm, args) {\n const self = this;\n this.fitBoundsOnLocationSet = typeof args.fitBoundsOnLocationSet === 'boolean' ? args.fitBoundsOnLocationSet : true;\n this.mapClassRepository = args.mapClassRepository || window.cr;\n\n args.clickEvents.forEach(x => document.querySelector(`input[id=\"${x.Id}\"]`).addEventListener('input', y => {\n if (y.target.checked) {\n //Delay pan to first filter out the results before panning (which also updates the inbound results). To avoid async issues there (not the prettiest solution, but it works).\n setTimeout(() => {\n self.gMaps.panTo({ zoom: x.Zoom, lat: x.Latitude, lng: x.Longitude })\n }, 10)\n }\n }))\n\n const instantiateMap = function (mapLocationsArray) {\n self.gMaps = new GoogleMaps(elm.querySelector('[data-map-google-maps'), {\n googleMapsAPIKey: args.googleMaps.googleMapsAPIKey,\n locations: mapLocationsArray,\n clusterIcon: args.googleMaps.clusterIcon,\n defaultMarkerImage: args.googleMaps.defaultMarkerImage,\n updateListView: args.updateListView,\n gMapsOptions: {\n center: args.googleMaps.googleMapsOptions.center,\n zoom: args.googleMaps.googleMapsOptions.zoom,\n mapTypeControl: false,\n streetViewControl: false,\n styles: null,\n },\n });\n\n //if (!args.googleMaps.googleMapsOptions.center && (mapLocationsArray && mapLocationsArray.length > 0)) {\n // self.gMaps.fitBounds();\n //}\n }\n\n // If the map has been instantiated with locations that have been pre-instantiated to valid MapLocation types\n // Instaniate the map\n if (args.typedLocations) {\n instantiateMap(args.typedLocations);\n } else { // Else instantiate all maps with a type and then instantiate the map\n this.instantiateAllMapLocations(args.locations)\n .then(instantiateMap);\n }\n }\n\n /*\n Loops through all map locations and instantiates by the locations component type\n */\n instantiateAllMapLocations(locationDataArray) {\n const instantiateMapLocationPromiseArray = [];\n\n // If no locations, return resolved promise\n if (!locationDataArray) {\n return new Promise((resolve) => {\n resolve();\n });\n }\n //console.log(locationDataArray)\n for (let i = 0; i < locationDataArray.length; i++) {\n instantiateMapLocationPromiseArray.push(this.instantiateMapLocation(locationDataArray[i]));\n }\n\n return Promise.all(instantiateMapLocationPromiseArray);\n }\n\n /*\n Gets the Class for a specific location based on its \"component\" property. If unset it will default to MapLocation class.\n This function loads the classes from the ComponentRepository, but it could in theory load them in a different way.\n */\n instantiateMapLocation(locationData) {\n return this.mapClassRepository.loadComponentClass(locationData.component)\n .then(MapTypeClass => {\n return new MapTypeClass(locationData);\n })\n .catch(() => {\n return new MapLocation(locationData);\n });\n }\n\n setLocations(locations, args, fitBounds) {\n return this.instantiateAllMapLocations(locations)\n .then(instantiatedLocations => {\n if (this.gMaps) {\n this.gMaps.setLocations(instantiatedLocations, args);\n //if (fitbounds !== undefined && fitbounds) {\n // this.gmaps.fitbounds();\n //} else if (fitbounds === undefined && this.fitboundsonlocationset) {\n // this.gmaps.fitbounds();\n //}\n }\n });\n }\n\n /*\n Same as setLocations, but this one requires that all locations have been instantiated to MapLocation or subclass thereof\n */\n setTypedLocations(locations, args, fitBounds) {\n this.gMaps.setLocations(locations, args);\n //if (fitBounds !== undefined && fitBounds) {\n // this.gMaps.fitBounds();\n //} else if (fitBounds === undefined && this.fitBoundsOnLocationSet) {\n // this.gMaps.fitBounds();\n //}\n }\n}\n","// ==ClosureCompiler==\n// @compilation_level ADVANCED_OPTIMIZATIONS\n// @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/maps/google_maps_api_v3_3.js\n// ==/ClosureCompiler==\n\n/**\n * @name MarkerClusterer for Google Maps v3\n * @version version 1.0.3\n * @author Luke Mahe\n * @fileoverview\n * The library creates and manages per-zoom-level clusters for large amounts of\n * markers.\n */\n\n/**\n * @license\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n\n/**\n * A Marker Clusterer that clusters markers.\n *\n * @param {google.maps.Map} map The Google map to attach to.\n * @param {Array.=} opt_markers Optional markers to add to\n * the cluster.\n * @param {Object=} opt_options support the following options:\n * 'gridSize': (number) The grid size of a cluster in pixels.\n * 'maxZoom': (number) The maximum zoom level that a marker can be part of a\n * cluster.\n * 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a\n * cluster is to zoom into it.\n * 'imagePath': (string) The base URL where the images representing\n * clusters will be found. The full URL will be:\n * {imagePath}[1-5].{imageExtension}\n * Default: '../images/m'.\n * 'imageExtension': (string) The suffix for images URL representing\n * clusters will be found. See _imagePath_ for details.\n * Default: 'png'.\n * 'averageCenter': (boolean) Whether the center of each cluster should be\n * the average of all markers in the cluster.\n * 'minimumClusterSize': (number) The minimum number of markers to be in a\n * cluster before the markers are hidden and a count\n * is shown.\n * 'styles': (object) An object that has style properties:\n * 'url': (string) The image url.\n * 'height': (number) The image height.\n * 'width': (number) The image width.\n * 'anchor': (Array) The anchor position of the label text.\n * 'textColor': (string) The text color.\n * 'textSize': (number) The text size.\n * 'backgroundPosition': (string) The position of the backgound x, y.\n * @constructor\n * @extends google.maps.OverlayView\n */\nfunction MarkerClusterer(map, opt_markers, opt_options) {\n // MarkerClusterer implements google.maps.OverlayView interface. We use the\n // extend function to extend MarkerClusterer with google.maps.OverlayView\n // because it might not always be available when the code is defined so we\n // look for it at the last possible moment. If it doesn't exist now then\n // there is no point going ahead :)\n this.extend(MarkerClusterer, google.maps.OverlayView);\n this.map_ = map;\n\n /**\n * @type {Array.}\n * @private\n */\n this.markers_ = [];\n\n /**\n * @type {Array.}\n */\n this.clusters_ = [];\n\n this.sizes = [53, 56, 66, 78, 90];\n\n /**\n * @private\n */\n this.styles_ = [];\n\n /**\n * @type {boolean}\n * @private\n */\n this.ready_ = false;\n\n const options = opt_options || {};\n\n /**\n * @type {number}\n * @private\n */\n this.gridSize_ = options.gridSize || 60;\n\n /**\n * @private\n */\n this.minClusterSize_ = options.minimumClusterSize || 2;\n\n\n /**\n * @type {?number}\n * @private\n */\n this.maxZoom_ = options.maxZoom || null;\n\n this.styles_ = options.styles || [];\n\n /**\n * @type {string}\n * @private\n */\n this.imagePath_ = options.imagePath ||\n this.MARKER_CLUSTER_IMAGE_PATH_;\n\n /**\n * @type {string}\n * @private\n */\n this.imageExtension_ = options.imageExtension ||\n this.MARKER_CLUSTER_IMAGE_EXTENSION_;\n\n /**\n * @type {boolean}\n * @private\n */\n this.zoomOnClick_ = true;\n\n if (options.zoomOnClick != undefined) {\n this.zoomOnClick_ = options.zoomOnClick;\n }\n\n /**\n * @type {boolean}\n * @private\n */\n this.averageCenter_ = false;\n\n if (options.averageCenter != undefined) {\n this.averageCenter_ = options.averageCenter;\n }\n\n this.setupStyles_();\n\n this.setMap(map);\n\n /**\n * @type {number}\n * @private\n */\n this.prevZoom_ = this.map_.getZoom();\n\n // Add the map event listeners\n const that = this;\n google.maps.event.addListener(this.map_, 'zoom_changed', function () {\n // Determines map type and prevent illegal zoom levels\n let zoom = that.map_.getZoom();\n const minZoom = that.map_.minZoom || 0;\n const maxZoom = Math.min(that.map_.maxZoom || 100,\n that.map_.mapTypes[that.map_.getMapTypeId()]?.maxZoom);\n zoom = Math.min(Math.max(zoom, minZoom), maxZoom);\n\n if (that.prevZoom_ != zoom) {\n that.prevZoom_ = zoom;\n that.resetViewport();\n }\n });\n\n google.maps.event.addListener(this.map_, 'idle', function () {\n that.redraw();\n });\n\n // Finally, add the markers\n if (opt_markers && (opt_markers.length || Object.keys(opt_markers).length)) {\n this.addMarkers(opt_markers, false);\n }\n}\n\n\n/**\n * The marker cluster image path.\n *\n * @type {string}\n * @private\n */\nMarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = '../images/m';\n\n\n/**\n * The marker cluster image path.\n *\n * @type {string}\n * @private\n */\nMarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png';\n\n\n/**\n * Extends a objects prototype by anothers.\n *\n * @param {Object} obj1 The object to be extended.\n * @param {Object} obj2 The object to extend with.\n * @return {Object} The new extended object.\n * @ignore\n */\nMarkerClusterer.prototype.extend = function (obj1, obj2) {\n return function (object) {\n for (const property in object.prototype) {\n this.prototype[property] = object.prototype[property];\n }\n return this;\n }.apply(obj1, [obj2]);\n};\n\n\n/**\n * Implementaion of the interface method.\n * @ignore\n */\nMarkerClusterer.prototype.onAdd = function () {\n this.setReady_(true);\n};\n\n/**\n * Implementaion of the interface method.\n * @ignore\n */\nMarkerClusterer.prototype.draw = function () { };\n\n/**\n * Sets up the styles object.\n *\n * @private\n */\nMarkerClusterer.prototype.setupStyles_ = function () {\n if (this.styles_.length) {\n return;\n }\n\n for (var i = 0, size; size = this.sizes[i]; i++) {\n this.styles_.push({\n url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_,\n height: size,\n width: size,\n });\n }\n};\n\n/**\n * Fit the map to the bounds of the markers in the clusterer.\n */\nMarkerClusterer.prototype.fitMapToMarkers = function () {\n const markers = this.getMarkers();\n const bounds = new google.maps.LatLngBounds();\n for (var i = 0, marker; marker = markers[i]; i++) {\n bounds.extend(marker.getPosition());\n }\n\n this.map_.fitBounds(bounds);\n};\n\n\n/**\n * Sets the styles.\n *\n * @param {Object} styles The style to set.\n */\nMarkerClusterer.prototype.setStyles = function (styles) {\n this.styles_ = styles;\n};\n\n\n/**\n * Gets the styles.\n *\n * @return {Object} The styles object.\n */\nMarkerClusterer.prototype.getStyles = function () {\n return this.styles_;\n};\n\n\n/**\n * Whether zoom on click is set.\n *\n * @return {boolean} True if zoomOnClick_ is set.\n */\nMarkerClusterer.prototype.isZoomOnClick = function () {\n return this.zoomOnClick_;\n};\n\n/**\n * Whether average center is set.\n *\n * @return {boolean} True if averageCenter_ is set.\n */\nMarkerClusterer.prototype.isAverageCenter = function () {\n return this.averageCenter_;\n};\n\n\n/**\n * Returns the array of markers in the clusterer.\n *\n * @return {Array.} The markers.\n */\nMarkerClusterer.prototype.getMarkers = function () {\n return this.markers_;\n};\n\n\n/**\n * Returns the number of markers in the clusterer\n *\n * @return {Number} The number of markers.\n */\nMarkerClusterer.prototype.getTotalMarkers = function () {\n return this.markers_.length;\n};\n\n\n/**\n * Sets the max zoom for the clusterer.\n *\n * @param {number} maxZoom The max zoom level.\n */\nMarkerClusterer.prototype.setMaxZoom = function (maxZoom) {\n this.maxZoom_ = maxZoom;\n};\n\n\n/**\n * Gets the max zoom for the clusterer.\n *\n * @return {number} The max zoom level.\n */\nMarkerClusterer.prototype.getMaxZoom = function () {\n return this.maxZoom_;\n};\n\n\n/**\n * The function for calculating the cluster icon image.\n *\n * @param {Array.} markers The markers in the clusterer.\n * @param {number} numStyles The number of styles available.\n * @return {Object} A object properties: 'text' (string) and 'index' (number).\n * @private\n */\nMarkerClusterer.prototype.calculator_ = function (markers, numStyles) {\n let index = 0;\n const count = markers.length;\n let dv = count;\n while (dv !== 0) {\n dv = parseInt(dv / 10, 10);\n index++;\n }\n\n index = Math.min(index, numStyles);\n return {\n text: count,\n index: index,\n };\n};\n\n\n/**\n * Set the calculator function.\n *\n * @param {function(Array, number)} calculator The function to set as the\n * calculator. The function should return a object properties:\n * 'text' (string) and 'index' (number).\n *\n */\nMarkerClusterer.prototype.setCalculator = function (calculator) {\n this.calculator_ = calculator;\n};\n\n\n/**\n * Get the calculator function.\n *\n * @return {function(Array, number)} the calculator function.\n */\nMarkerClusterer.prototype.getCalculator = function () {\n return this.calculator_;\n};\n\n\n/**\n * Add an array of markers to the clusterer.\n *\n * @param {Array.} markers The markers to add.\n * @param {boolean=} opt_nodraw Whether to redraw the clusters.\n */\nMarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) {\n if (markers.length) {\n for (var i = 0, marker; marker = markers[i]; i++) {\n this.pushMarkerTo_(marker);\n }\n } else if (Object.keys(markers).length) {\n for (var marker in markers) {\n this.pushMarkerTo_(markers[marker]);\n }\n }\n if (!opt_nodraw) {\n this.redraw();\n }\n};\n\n\n/**\n * Pushes a marker to the clusterer.\n *\n * @param {google.maps.Marker} marker The marker to add.\n * @private\n */\nMarkerClusterer.prototype.pushMarkerTo_ = function (marker) {\n marker.isAdded = false;\n if (marker.draggable) {\n // If the marker is draggable add a listener so we update the clusters on\n // the drag end.\n const that = this;\n google.maps.event.addListener(marker, 'dragend', function () {\n marker.isAdded = false;\n that.repaint();\n });\n }\n this.markers_.push(marker);\n};\n\n\n/**\n * Adds a marker to the clusterer and redraws if needed.\n *\n * @param {google.maps.Marker} marker The marker to add.\n * @param {boolean=} opt_nodraw Whether to redraw the clusters.\n */\nMarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) {\n this.pushMarkerTo_(marker);\n if (!opt_nodraw) {\n this.redraw();\n }\n};\n\n\n/**\n * Removes a marker and returns true if removed, false if not\n *\n * @param {google.maps.Marker} marker The marker to remove\n * @return {boolean} Whether the marker was removed or not\n * @private\n */\nMarkerClusterer.prototype.removeMarker_ = function (marker) {\n let index = -1;\n if (this.markers_.indexOf) {\n index = this.markers_.indexOf(marker);\n } else {\n for (var i = 0, m; m = this.markers_[i]; i++) {\n if (m == marker) {\n index = i;\n break;\n }\n }\n }\n\n if (index == -1) {\n // Marker is not in our list of markers.\n return false;\n }\n\n marker.setMap(null);\n\n this.markers_.splice(index, 1);\n\n return true;\n};\n\n\n/**\n * Remove a marker from the cluster.\n *\n * @param {google.maps.Marker} marker The marker to remove.\n * @param {boolean=} opt_nodraw Optional boolean to force no redraw.\n * @return {boolean} True if the marker was removed.\n */\nMarkerClusterer.prototype.removeMarker = function (marker, opt_nodraw) {\n const removed = this.removeMarker_(marker);\n\n if (!opt_nodraw && removed) {\n this.resetViewport();\n this.redraw();\n return true;\n }\n return false;\n\n};\n\n\n/**\n * Removes an array of markers from the cluster.\n *\n * @param {Array.} markers The markers to remove.\n * @param {boolean=} opt_nodraw Optional boolean to force no redraw.\n */\nMarkerClusterer.prototype.removeMarkers = function (markers, opt_nodraw) {\n // create a local copy of markers if required\n // (removeMarker_ modifies the getMarkers() array in place)\n const markersCopy = markers === this.getMarkers() ? markers.slice() : markers;\n let removed = false;\n\n for (var i = 0, marker; marker = markersCopy[i]; i++) {\n const r = this.removeMarker_(marker);\n removed = removed || r;\n }\n\n if (!opt_nodraw && removed) {\n this.resetViewport();\n this.redraw();\n return true;\n }\n};\n\n\n/**\n * Sets the clusterer's ready state.\n *\n * @param {boolean} ready The state.\n * @private\n */\nMarkerClusterer.prototype.setReady_ = function (ready) {\n if (!this.ready_) {\n this.ready_ = ready;\n this.createClusters_();\n }\n};\n\n\n/**\n * Returns the number of clusters in the clusterer.\n *\n * @return {number} The number of clusters.\n */\nMarkerClusterer.prototype.getTotalClusters = function () {\n return this.clusters_.length;\n};\n\n\n/**\n * Returns the google map that the clusterer is associated with.\n *\n * @return {google.maps.Map} The map.\n */\nMarkerClusterer.prototype.getMap = function () {\n return this.map_;\n};\n\n\n/**\n * Sets the google map that the clusterer is associated with.\n *\n * @param {google.maps.Map} map The map.\n */\nMarkerClusterer.prototype.setMap = function (map) {\n this.map_ = map;\n};\n\n\n/**\n * Returns the size of the grid.\n *\n * @return {number} The grid size.\n */\nMarkerClusterer.prototype.getGridSize = function () {\n return this.gridSize_;\n};\n\n\n/**\n * Sets the size of the grid.\n *\n * @param {number} size The grid size.\n */\nMarkerClusterer.prototype.setGridSize = function (size) {\n this.gridSize_ = size;\n};\n\n\n/**\n * Returns the min cluster size.\n *\n * @return {number} The grid size.\n */\nMarkerClusterer.prototype.getMinClusterSize = function () {\n return this.minClusterSize_;\n};\n\n/**\n * Sets the min cluster size.\n *\n * @param {number} size The grid size.\n */\nMarkerClusterer.prototype.setMinClusterSize = function (size) {\n this.minClusterSize_ = size;\n};\n\n\n/**\n * Extends a bounds object by the grid size.\n *\n * @param {google.maps.LatLngBounds} bounds The bounds to extend.\n * @return {google.maps.LatLngBounds} The extended bounds.\n */\nMarkerClusterer.prototype.getExtendedBounds = function (bounds) {\n const projection = this.getProjection();\n\n // Turn the bounds into latlng.\n const tr = new google.maps.LatLng(bounds.getNorthEast().lat(),\n bounds.getNorthEast().lng());\n const bl = new google.maps.LatLng(bounds.getSouthWest().lat(),\n bounds.getSouthWest().lng());\n\n // Convert the points to pixels and the extend out by the grid size.\n const trPix = projection.fromLatLngToDivPixel(tr);\n trPix.x += this.gridSize_;\n trPix.y -= this.gridSize_;\n\n const blPix = projection.fromLatLngToDivPixel(bl);\n blPix.x -= this.gridSize_;\n blPix.y += this.gridSize_;\n\n // Convert the pixel points back to LatLng\n const ne = projection.fromDivPixelToLatLng(trPix);\n const sw = projection.fromDivPixelToLatLng(blPix);\n\n // Extend the bounds to contain the new bounds.\n bounds.extend(ne);\n bounds.extend(sw);\n\n return bounds;\n};\n\n\n/**\n * Determins if a marker is contained in a bounds.\n *\n * @param {google.maps.Marker} marker The marker to check.\n * @param {google.maps.LatLngBounds} bounds The bounds to check against.\n * @return {boolean} True if the marker is in the bounds.\n * @private\n */\nMarkerClusterer.prototype.isMarkerInBounds_ = function (marker, bounds) {\n return bounds.contains(marker.getPosition());\n};\n\n\n/**\n * Clears all clusters and markers from the clusterer.\n */\nMarkerClusterer.prototype.clearMarkers = function () {\n this.resetViewport(true);\n\n // Set the markers a empty array.\n this.markers_ = [];\n};\n\n\n/**\n * Clears all existing clusters and recreates them.\n * @param {boolean} opt_hide To also hide the marker.\n */\nMarkerClusterer.prototype.resetViewport = function (opt_hide) {\n // Remove all the clusters\n for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {\n cluster.remove();\n }\n\n // Reset the markers to not be added and to be invisible.\n for (var i = 0, marker; marker = this.markers_[i]; i++) {\n marker.isAdded = false;\n if (opt_hide) {\n marker.setMap(null);\n }\n }\n\n this.clusters_ = [];\n};\n\n/**\n *\n */\nMarkerClusterer.prototype.repaint = function () {\n const oldClusters = this.clusters_.slice();\n this.clusters_.length = 0;\n this.resetViewport();\n this.redraw();\n\n // Remove the old clusters.\n // Do it in a timeout so the other clusters have been drawn first.\n window.setTimeout(function () {\n for (var i = 0, cluster; cluster = oldClusters[i]; i++) {\n cluster.remove();\n }\n }, 0);\n};\n\n\n/**\n * Redraws the clusters.\n */\nMarkerClusterer.prototype.redraw = function () {\n this.createClusters_();\n};\n\n\n/**\n * Calculates the distance between two latlng locations in km.\n * @see http://www.movable-type.co.uk/scripts/latlong.html\n *\n * @param {google.maps.LatLng} p1 The first lat lng point.\n * @param {google.maps.LatLng} p2 The second lat lng point.\n * @return {number} The distance between the two points in km.\n * @private\n*/\nMarkerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) {\n if (!p1 || !p2) {\n return 0;\n }\n\n const R = 6371; // Radius of the Earth in km\n const dLat = (p2.lat() - p1.lat()) * Math.PI / 180;\n const dLon = (p2.lng() - p1.lng()) * Math.PI / 180;\n const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +\n Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) *\n Math.sin(dLon / 2) * Math.sin(dLon / 2);\n const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));\n const d = R * c;\n return d;\n};\n\n\n/**\n * Add a marker to a cluster, or creates a new cluster.\n *\n * @param {google.maps.Marker} marker The marker to add.\n * @private\n */\nMarkerClusterer.prototype.addToClosestCluster_ = function (marker) {\n let distance = 40000; // Some large number\n let clusterToAddTo = null;\n const pos = marker.getPosition();\n for (var i = 0, cluster; cluster = this.clusters_[i]; i++) {\n const center = cluster.getCenter();\n if (center) {\n const d = this.distanceBetweenPoints_(center, marker.getPosition());\n if (d < distance) {\n distance = d;\n clusterToAddTo = cluster;\n }\n }\n }\n\n if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) {\n clusterToAddTo.addMarker(marker);\n } else {\n var cluster = new Cluster(this);\n cluster.addMarker(marker);\n this.clusters_.push(cluster);\n }\n};\n\n\n/**\n * Creates the clusters.\n *\n * @private\n */\nMarkerClusterer.prototype.createClusters_ = function () {\n if (!this.ready_) {\n return;\n }\n\n // Get our current map view bounds.\n // Create a new bounds object so we don't affect the map.\n const mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(),\n this.map_.getBounds().getNorthEast());\n const bounds = this.getExtendedBounds(mapBounds);\n\n for (var i = 0, marker; marker = this.markers_[i]; i++) {\n if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) {\n this.addToClosestCluster_(marker);\n }\n }\n};\n\n\n/**\n * A cluster that contains markers.\n *\n * @param {MarkerClusterer} markerClusterer The markerclusterer that this\n * cluster is associated with.\n * @constructor\n * @ignore\n */\nfunction Cluster(markerClusterer) {\n this.markerClusterer_ = markerClusterer;\n this.map_ = markerClusterer.getMap();\n this.gridSize_ = markerClusterer.getGridSize();\n this.minClusterSize_ = markerClusterer.getMinClusterSize();\n this.averageCenter_ = markerClusterer.isAverageCenter();\n this.center_ = null;\n this.markers_ = [];\n this.bounds_ = null;\n this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(),\n markerClusterer.getGridSize());\n}\n\n/**\n * Determins if a marker is already added to the cluster.\n *\n * @param {google.maps.Marker} marker The marker to check.\n * @return {boolean} True if the marker is already added.\n */\nCluster.prototype.isMarkerAlreadyAdded = function (marker) {\n if (this.markers_.indexOf) {\n return this.markers_.indexOf(marker) != -1;\n }\n for (var i = 0, m; m = this.markers_[i]; i++) {\n if (m == marker) {\n return true;\n }\n }\n\n return false;\n};\n\n\n/**\n * Add a marker the cluster.\n *\n * @param {google.maps.Marker} marker The marker to add.\n * @return {boolean} True if the marker was added.\n */\nCluster.prototype.addMarker = function (marker) {\n if (this.isMarkerAlreadyAdded(marker)) {\n return false;\n }\n\n if (!this.center_) {\n this.center_ = marker.getPosition();\n this.calculateBounds_();\n } else {\n if (this.averageCenter_) {\n const l = this.markers_.length + 1;\n const lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l;\n const lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l;\n this.center_ = new google.maps.LatLng(lat, lng);\n this.calculateBounds_();\n }\n }\n\n marker.isAdded = true;\n this.markers_.push(marker);\n\n const len = this.markers_.length;\n if (len < this.minClusterSize_ && marker.getMap() != this.map_) {\n // Min cluster size not reached so show the marker.\n marker.setMap(this.map_);\n }\n\n if (len == this.minClusterSize_) {\n // Hide the markers that were showing.\n for (let i = 0; i < len; i++) {\n this.markers_[i].setMap(null);\n }\n }\n\n if (len >= this.minClusterSize_) {\n marker.setMap(null);\n }\n\n this.updateIcon();\n return true;\n};\n\n\n/**\n * Returns the marker clusterer that the cluster is associated with.\n *\n * @return {MarkerClusterer} The associated marker clusterer.\n */\nCluster.prototype.getMarkerClusterer = function () {\n return this.markerClusterer_;\n};\n\n\n/**\n * Returns the bounds of the cluster.\n *\n * @return {google.maps.LatLngBounds} the cluster bounds.\n */\nCluster.prototype.getBounds = function () {\n const bounds = new google.maps.LatLngBounds(this.center_, this.center_);\n const markers = this.getMarkers();\n for (var i = 0, marker; marker = markers[i]; i++) {\n bounds.extend(marker.getPosition());\n }\n return bounds;\n};\n\n\n/**\n * Removes the cluster\n */\nCluster.prototype.remove = function () {\n this.clusterIcon_.remove();\n this.markers_.length = 0;\n delete this.markers_;\n};\n\n\n/**\n * Returns the number of markers in the cluster.\n *\n * @return {number} The number of markers in the cluster.\n */\nCluster.prototype.getSize = function () {\n return this.markers_.length;\n};\n\n\n/**\n * Returns a list of the markers in the cluster.\n *\n * @return {Array.} The markers in the cluster.\n */\nCluster.prototype.getMarkers = function () {\n return this.markers_;\n};\n\n\n/**\n * Returns the center of the cluster.\n *\n * @return {google.maps.LatLng} The cluster center.\n */\nCluster.prototype.getCenter = function () {\n return this.center_;\n};\n\n\n/**\n * Calculated the extended bounds of the cluster with the grid.\n *\n * @private\n */\nCluster.prototype.calculateBounds_ = function () {\n const bounds = new google.maps.LatLngBounds(this.center_, this.center_);\n this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds);\n};\n\n\n/**\n * Determines if a marker lies in the clusters bounds.\n *\n * @param {google.maps.Marker} marker The marker to check.\n * @return {boolean} True if the marker lies in the bounds.\n */\nCluster.prototype.isMarkerInClusterBounds = function (marker) {\n return this.bounds_.contains(marker.getPosition());\n};\n\n\n/**\n * Returns the map that the cluster is associated with.\n *\n * @return {google.maps.Map} The map.\n */\nCluster.prototype.getMap = function () {\n return this.map_;\n};\n\n\n/**\n * Updates the cluster icon\n */\nCluster.prototype.updateIcon = function () {\n const zoom = this.map_.getZoom();\n const mz = this.markerClusterer_.getMaxZoom();\n\n if (mz && zoom > mz) {\n // The zoom is greater than our max zoom so show all the markers in cluster.\n for (var i = 0, marker; marker = this.markers_[i]; i++) {\n marker.setMap(this.map_);\n }\n return;\n }\n\n if (this.markers_.length < this.minClusterSize_) {\n // Min cluster size not yet reached.\n this.clusterIcon_.hide();\n return;\n }\n\n const numStyles = this.markerClusterer_.getStyles().length;\n const sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles);\n this.clusterIcon_.setCenter(this.center_);\n this.clusterIcon_.setSums(sums);\n this.clusterIcon_.show();\n};\n\n\n/**\n * A cluster icon\n *\n * @param {Cluster} cluster The cluster to be associated with.\n * @param {Object} styles An object that has style properties:\n * 'url': (string) The image url.\n * 'height': (number) The image height.\n * 'width': (number) The image width.\n * 'anchor': (Array) The anchor position of the label text.\n * 'textColor': (string) The text color.\n * 'textSize': (number) The text size.\n * 'backgroundPosition: (string) The background postition x, y.\n * @param {number=} opt_padding Optional padding to apply to the cluster icon.\n * @constructor\n * @extends google.maps.OverlayView\n * @ignore\n */\nfunction ClusterIcon(cluster, styles, opt_padding) {\n cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView);\n\n this.styles_ = styles;\n this.padding_ = opt_padding || 0;\n this.cluster_ = cluster;\n this.center_ = null;\n this.map_ = cluster.getMap();\n this.div_ = null;\n this.sums_ = null;\n this.visible_ = false;\n\n this.setMap(this.map_);\n}\n\n\n/**\n * Triggers the clusterclick event and zoom's if the option is set.\n */\nClusterIcon.prototype.triggerClusterClick = function () {\n const markerClusterer = this.cluster_.getMarkerClusterer();\n\n // Trigger the clusterclick event.\n google.maps.event.trigger(markerClusterer.map_, 'clusterclick', this.cluster_);\n\n if (markerClusterer.isZoomOnClick()) {\n // Zoom into the cluster.\n this.map_.fitBounds(this.cluster_.getBounds());\n }\n};\n\n\n/**\n * Adding the cluster icon to the dom.\n * @ignore\n */\nClusterIcon.prototype.onAdd = function () {\n this.div_ = document.createElement('DIV');\n if (this.visible_) {\n const pos = this.getPosFromLatLng_(this.center_);\n this.div_.style.cssText = this.createCss(pos);\n this.div_.innerHTML = this.sums_.text;\n }\n\n const panes = this.getPanes();\n panes.overlayMouseTarget.appendChild(this.div_);\n\n const that = this;\n this.div_.addEventListener('click', function () {\n that.triggerClusterClick();\n });\n};\n\n\n/**\n * Returns the position to place the div dending on the latlng.\n *\n * @param {google.maps.LatLng} latlng The position in latlng.\n * @return {google.maps.Point} The position in pixels.\n * @private\n */\nClusterIcon.prototype.getPosFromLatLng_ = function (latlng) {\n const pos = this.getProjection().fromLatLngToDivPixel(latlng);\n pos.x -= parseInt(this.width_ / 2, 10);\n pos.y -= parseInt(this.height_ / 2, 10);\n return pos;\n};\n\n\n/**\n * Draw the icon.\n * @ignore\n */\nClusterIcon.prototype.draw = function () {\n if (this.visible_) {\n const pos = this.getPosFromLatLng_(this.center_);\n this.div_.style.top = pos.y + 'px';\n this.div_.style.left = pos.x + 'px';\n this.div_.style.zIndex = google.maps.Marker.MAX_ZINDEX + 1;\n }\n};\n\n\n/**\n * Hide the icon.\n */\nClusterIcon.prototype.hide = function () {\n if (this.div_) {\n this.div_.style.display = 'none';\n }\n this.visible_ = false;\n};\n\n\n/**\n * Position and show the icon.\n */\nClusterIcon.prototype.show = function () {\n if (this.div_) {\n const pos = this.getPosFromLatLng_(this.center_);\n this.div_.style.cssText = this.createCss(pos);\n this.div_.style.display = '';\n }\n this.visible_ = true;\n};\n\n\n/**\n * Remove the icon from the map\n */\nClusterIcon.prototype.remove = function () {\n this.setMap(null);\n};\n\n\n/**\n * Implementation of the onRemove interface.\n * @ignore\n */\nClusterIcon.prototype.onRemove = function () {\n if (this.div_ && this.div_.parentNode) {\n this.hide();\n this.div_.parentNode.removeChild(this.div_);\n this.div_ = null;\n }\n};\n\n\n/**\n * Set the sums of the icon.\n *\n * @param {Object} sums The sums containing:\n * 'text': (string) The text to display in the icon.\n * 'index': (number) The style index of the icon.\n */\nClusterIcon.prototype.setSums = function (sums) {\n this.sums_ = sums;\n this.text_ = sums.text;\n this.index_ = sums.index;\n if (this.div_) {\n this.div_.innerHTML = sums.text;\n }\n\n this.useStyle();\n};\n\n\n/**\n * Sets the icon to the the styles.\n */\nClusterIcon.prototype.useStyle = function () {\n let index = Math.max(0, this.sums_.index - 1);\n index = Math.min(this.styles_.length - 1, index);\n const style = this.styles_[index];\n this.url_ = style.url;\n this.height_ = style.height;\n this.width_ = style.width;\n this.textColor_ = style.textColor;\n this.anchor_ = style.anchor;\n this.textSize_ = style.textSize;\n this.backgroundPosition_ = style.backgroundPosition;\n};\n\n\n/**\n * Sets the center of the icon.\n *\n * @param {google.maps.LatLng} center The latlng to set as the center.\n */\nClusterIcon.prototype.setCenter = function (center) {\n this.center_ = center;\n};\n\n\n/**\n * Create the css text based on the position of the icon.\n *\n * @param {google.maps.Point} pos The position.\n * @return {string} The css style text.\n */\nClusterIcon.prototype.createCss = function (pos) {\n const style = [];\n style.push('background-image:url(' + this.url_ + ');');\n const backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0';\n style.push('background-position:' + backgroundPosition + ';');\n\n if (typeof this.anchor_ === 'object') {\n if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 &&\n this.anchor_[0] < this.height_) {\n style.push('height:' + (this.height_ - this.anchor_[0]) +\n 'px; padding-top:' + this.anchor_[0] + 'px;');\n } else {\n style.push('height:' + this.height_ + 'px; line-height:' + this.height_ +\n 'px;');\n }\n if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 &&\n this.anchor_[1] < this.width_) {\n style.push('width:' + (this.width_ - this.anchor_[1]) +\n 'px; padding-left:' + this.anchor_[1] + 'px;');\n } else {\n style.push('width:' + this.width_ + 'px; text-align:center;');\n }\n } else {\n style.push('height:' + this.height_ + 'px; line-height:' +\n this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;');\n }\n\n const txtColor = this.textColor_ ? this.textColor_ : 'black';\n const txtSize = this.textSize_ ? this.textSize_ : 11;\n\n style.push('cursor:pointer; top:' + pos.y + 'px; left:' +\n pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' +\n txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold');\n return style.join('');\n};\n\n\n// Export Symbols for Closure\n// If you are not going to compile with closure then you can remove the\n// code below.\nvar window = window || {};\nwindow.MarkerClusterer = MarkerClusterer;\nMarkerClusterer.prototype.addMarker = MarkerClusterer.prototype.addMarker;\nMarkerClusterer.prototype.addMarkers = MarkerClusterer.prototype.addMarkers;\nMarkerClusterer.prototype.clearMarkers =\n MarkerClusterer.prototype.clearMarkers;\nMarkerClusterer.prototype.fitMapToMarkers =\n MarkerClusterer.prototype.fitMapToMarkers;\nMarkerClusterer.prototype.getCalculator =\n MarkerClusterer.prototype.getCalculator;\nMarkerClusterer.prototype.getGridSize =\n MarkerClusterer.prototype.getGridSize;\nMarkerClusterer.prototype.getExtendedBounds =\n MarkerClusterer.prototype.getExtendedBounds;\nMarkerClusterer.prototype.getMap = MarkerClusterer.prototype.getMap;\nMarkerClusterer.prototype.getMarkers = MarkerClusterer.prototype.getMarkers;\nMarkerClusterer.prototype.getMaxZoom = MarkerClusterer.prototype.getMaxZoom;\nMarkerClusterer.prototype.getStyles = MarkerClusterer.prototype.getStyles;\nMarkerClusterer.prototype.getTotalClusters =\n MarkerClusterer.prototype.getTotalClusters;\nMarkerClusterer.prototype.getTotalMarkers =\n MarkerClusterer.prototype.getTotalMarkers;\nMarkerClusterer.prototype.redraw = MarkerClusterer.prototype.redraw;\nMarkerClusterer.prototype.removeMarker =\n MarkerClusterer.prototype.removeMarker;\nMarkerClusterer.prototype.removeMarkers =\n MarkerClusterer.prototype.removeMarkers;\nMarkerClusterer.prototype.resetViewport =\n MarkerClusterer.prototype.resetViewport;\nMarkerClusterer.prototype.repaint =\n MarkerClusterer.prototype.repaint;\nMarkerClusterer.prototype.setCalculator =\n MarkerClusterer.prototype.setCalculator;\nMarkerClusterer.prototype.setGridSize =\n MarkerClusterer.prototype.setGridSize;\nMarkerClusterer.prototype.setMaxZoom =\n MarkerClusterer.prototype.setMaxZoom;\nMarkerClusterer.prototype.onAdd = MarkerClusterer.prototype.onAdd;\nMarkerClusterer.prototype.draw = MarkerClusterer.prototype.draw;\n\nCluster.prototype.getCenter = Cluster.prototype.getCenter;\nCluster.prototype.getSize = Cluster.prototype.getSize;\nCluster.prototype.getMarkers = Cluster.prototype.getMarkers;\n\nClusterIcon.prototype.onAdd = ClusterIcon.prototype.onAdd;\nClusterIcon.prototype.draw = ClusterIcon.prototype.draw;\nClusterIcon.prototype.onRemove = ClusterIcon.prototype.onRemove;\n\nObject.keys = Object.keys || function (o) {\n const result = [];\n for (const name in o) {\n if (o.hasOwnProperty(name)) { result.push(name); }\n }\n return result;\n};\n\nif (typeof module === 'object') {\n module.exports = MarkerClusterer;\n}\n","const loadGoogleMapsApi = require('load-google-maps-api');\nimport MarkerClusterer from './Locator-1-Markercluster';\n\n/*\n The GoogleMaps class serves as a layer on top of the official Google Maps API and provides helper methods for centering,\n and adding locations to the map.\n This class is not meant to be instantiated directly through the ComponentRepository, but rather by another class like Map.\n The GoogleMaps class expects the locations to be an instance of either MapLocation or a subclass thereof,\n but it is not strictly required.\n\n ARGS:\n -googleMapsAPIKey: string API key for the Google Maps API\n -locations: array of MapLocation instances\n -defaultMarkerImage: object that specifies the default marker used for locations, if the location itself does not override it. Ex: { url: '/Static/assets/images/marker.png', width: 50, height: 70}\n -gMapsOptions: object with google maps options as specified in the documentation: https://developers.google.com/maps/documentation/javascript/reference/map#MapOptions\n*/\nexport default class GoogleMaps {\n constructor(elm, args) {\n args.gMapsOptions = args.gMapsOptions || {};\n args.gMapsOptions.zoom = args.gMapsOptions.zoom || 10;\n args.defaultMarkerImage = args.defaultMarkerImage || { url: null, width: 0, height: 0 };\n this.breakpoint = window.matchMedia('(min-width:992px)');\n\n const self = this;\n this.lastPan = 0;\n this.initializedPromiseResolve = null;\n this.initializedPromiseReject = null;\n this.initializedPromise = new Promise((resolve, reject) => {\n this.initializedPromiseResolve = resolve;\n this.initializedPromiseReject = reject;\n });\n\n self.updateListView = args.updateListView;\n this.defaultMarkerImage;\n this.clusterIcon;\n this.map;\n this.locations = [];\n this.infoWindow = null;\n this.markerCluster = null;\n this.inboundMarkers = [];\n\n this.startZoom = args.gMapsOptions.zoom;\n this.startLat = args.gMapsOptions.center.lat;\n this.startLng = args.gMapsOptions.center.lng;\n this.lastNodeidSum = 0;\n this.markers = [];\n self.setMarkersInbound = this.setMarkersInbound;\n const throttleListViewUpdate = throttle(() => self.setMarkersInbound(self), 1000);\n\n loadGoogleMapsApi({\n key: args.googleMapsAPIKey,\n })\n .then(function (googleMaps) {\n self.defaultMarkerImage = new google.maps.MarkerImage(args.defaultMarkerImage.url, null, null, null, new google.maps.Size(args.defaultMarkerImage.width, args.defaultMarkerImage.height));\n self.map = new googleMaps.Map(elm, args.gMapsOptions);\n\n self.map.addListener('bounds_changed', () => {\n throttleListViewUpdate();\n });\n\n self.map.addListener('zoom_changed', () => {\n throttleListViewUpdate();\n });\n\n self.infoWindow = new google.maps.InfoWindow({\n pixelOffset: new google.maps.Size(0, 0),\n });\n self.initializedPromiseResolve();\n });\n\n // call public functions that await the initializedPromise before loadGoogleMapsApi resolves to have it queued up before any external invocations\n self.setLocations(args.locations, args);\n\n function throttle(func, timeFrame) {\n let lastTime = 0;\n return () => {\n const now = Date.now();\n if (now - lastTime >= timeFrame) {\n func();\n lastTime = now;\n }\n };\n }\n }\n // m.isAdded &&\n setMarkersInbound(self) {\n if (self && this.breakpoint.matches) {\n const bounds = self.map.getBounds();\n if (bounds) {\n const newInboundMarkers = self.markers.filter(m => bounds.contains(m.getPosition()));\n if (newInboundMarkers.length == 0) {\n if (self.lastNodeidSum != 0) {\n self.inboundMarkers = self.markers;\n self.updateListView(self.markers);\n self.lastNodeidSum = 0;\n }\n } else if (self.lastNodeidSum != newInboundMarkers.reduce((a, b) => a + b.nodeId, 0)) {\n self.inboundMarkers = newInboundMarkers;\n self.updateListView(newInboundMarkers);\n self.lastNodeidSum = newInboundMarkers.reduce((a, b) => a + b.nodeId, 0);\n }\n }\n }\n }\n\n /*\n Zooms in the map to contain all locations\n */\n fitBounds() {\n this.initializedPromise\n .then(() => {\n const bounds = new google.maps.LatLngBounds();\n for (let i = 0; i < this.locations.length; i++) {\n bounds.extend(this.locations[i].getPosition());\n }\n\n this.map.fitBounds(bounds);\n });\n }\n\n /*\n Sets the center of the map to the specified coordinate.\n CENTER:\n -lat: float\n -lng: float\n */\n setCenter(center) {\n this.initializedPromise\n .then(() => {\n this.map.setCenter(new google.maps.LatLng(center.lat, center.lng));\n });\n }\n\n /*\n Sets the center of the map to the specified coordinate.\n CENTER:\n -lat: float\n -lng: float\n */\n panTo(location) {\n this.initializedPromise\n .then(() => {\n this.lastPan = Date.now();\n this.map.panTo(new google.maps.LatLng(location.lat, location.lng));\n this.map.setZoom(location.zoom);\n });\n }\n\n /*\n newLocations: array of MapLocation instances\n */\n setLocations(newLocations, args) {\n this.initializedPromise\n .then(() => {\n // clear all markers\n for (let i = 0; i < this.locations.length; i++) {\n this.locations[i].setMap(null);\n }\n\n this.locations = [];\n const map = this.map;\n const infoWindow = this.infoWindow;\n this.markers = [];\n\n if (!newLocations) {\n return;\n }\n\n // Add new markers\n for (let i = 0; i < newLocations.length; i++) {\n if (newLocations[i].latitude != null && newLocations[i].longitude != null) {\n const marker = new google.maps.Marker({\n position: new google.maps.LatLng(newLocations[i].latitude, newLocations[i].longitude),\n map: this.map,\n nodeId: newLocations[i].nodeId,\n icon: newLocations[i].pinIcon ? new google.maps.MarkerImage(newLocations[i].pinIcon.url, null, null, null, new google.maps.Size(newLocations[i].pinIcon.width, newLocations[i].pinIcon.height)) : this.defaultMarkerImage,\n });\n\n function onMapClick(newLocation, infoWindow) {\n if (newLocation.onMapClick) {\n newLocation.onMapClick(infoWindow, event);\n } else {\n infoWindow.close();\n }\n }\n\n function onMarkerClick(marker, map, newLocation, infoWindow) {\n const infoWindowContent = newLocation.getInfoWindowContent ? newLocation.getInfoWindowContent : null;\n if (newLocation.onMarkerClick) {\n newLocation.onMarkerClick(infoWindow, marker, map, event);\n } else {\n infoWindowContent(newLocation).then(content => {\n infoWindow.pixelOffset.width = 0;\n infoWindow.pixelOffset.height = 0;\n infoWindow.setContent(content);\n infoWindow.open(map, marker);\n });\n }\n }\n\n map.addListener('click', () => {\n onMapClick(newLocations[i], infoWindow);\n });\n\n marker.addListener('click', () => {\n onMarkerClick(marker, map, newLocations[i], infoWindow);\n });\n\n this.markers.push(marker);\n this.locations.push(marker);\n }\n }\n const markerOptions = {\n styles: [{\n textColor: '#222222',\n textSize: 20,\n url: args.map.googleMaps.clusterIcon.url,\n width: 35,\n height: 35,\n }],\n };\n\n if (this.markerCluster) {\n this.markerCluster.clearMarkers();\n this.markerCluster.addMarkers(this.markers, false);\n } else {\n this.markerCluster = new MarkerClusterer(map, this.markers, markerOptions);\n }\n\n this.setMarkersInbound(this);\n if (this.lastNodeidSum == 0 && newLocations.length > 0) {\n if (this.markerCluster && this.markerCluster.map) {\n this.panTo({ zoom: this.startZoom, lat: this.startLat, lng: this.startLng });\n }\n }\n });\n }\n}\n","const API_URL = 'https://maps.googleapis.com/maps/api/js'\nconst CALLBACK_NAME = '__googleMapsApiOnLoadCallback'\n\nconst optionsKeys = ['channel', 'client', 'key', 'language', 'region', 'v']\n\nlet promise = null\n\nmodule.exports = function (options = {}) {\n promise =\n promise ||\n new Promise(function (resolve, reject) {\n // Reject the promise after a timeout\n const timeoutId = setTimeout(function () {\n window[CALLBACK_NAME] = function () {} // Set the on load callback to a no-op\n reject(new Error('Could not load the Google Maps API'))\n }, options.timeout || 10000)\n\n // Hook up the on load callback\n window[CALLBACK_NAME] = function () {\n if (timeoutId !== null) {\n clearTimeout(timeoutId)\n }\n resolve(window.google.maps)\n delete window[CALLBACK_NAME]\n }\n\n // Prepare the `script` tag to be inserted into the page\n const scriptElement = document.createElement('script')\n const params = [`callback=${CALLBACK_NAME}`]\n optionsKeys.forEach(function (key) {\n if (options[key]) {\n params.push(`${key}=${options[key]}`)\n }\n })\n if (options.libraries && options.libraries.length) {\n params.push(`libraries=${options.libraries.join(',')}`)\n }\n scriptElement.src = `${options.apiUrl || API_URL}?${params.join('&')}`\n\n // Insert the `script` tag\n document.body.appendChild(scriptElement)\n })\n return promise\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AACA;AAAA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;;A;;;;;;;;;;;;;;AC1HA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAGA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AAEA;AACA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAGA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AACA;AACA;AACA;AAEA;AAEA;AACA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AACA;AACA;AACA;AAEA;AACA;AACA;;A;;;;;;;;;;;;;;;;;;;;ACtyCA;AAAA;AADA;AAGA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AACA;AACA;AAEA;AACA;AACA;AACA;AAAA;AAAA;AAAA;AAAA;AACA;AACA;AACA;AACA;AACA;AAAA;;A;;;;;;;;;;;AC7OA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;;A;;A","sourceRoot":""}