{"version":3,"file":"leaflet.markercluster-src.js","sources":["../src/MarkerClusterGroup.js","../src/MarkerCluster.js","../src/MarkerOpacity.js","../src/DistanceGrid.js","../src/MarkerCluster.QuickHull.js","../src/MarkerCluster.Spiderfier.js","../src/MarkerClusterGroup.Refresh.js"],"sourcesContent":["/*\n * L.MarkerClusterGroup extends L.FeatureGroup by clustering the markers contained within\n */\n\nexport var MarkerClusterGroup = L.MarkerClusterGroup = L.FeatureGroup.extend({\n\n\toptions: {\n\t\tmaxClusterRadius: 80, //A cluster will cover at most this many pixels from its center\n\t\ticonCreateFunction: null,\n\t\tclusterPane: L.Marker.prototype.options.pane,\n\n\t\tspiderfyOnEveryZoom: false,\n\t\tspiderfyOnMaxZoom: true,\n\t\tshowCoverageOnHover: true,\n\t\tzoomToBoundsOnClick: true,\n\t\tsingleMarkerMode: false,\n\n\t\tdisableClusteringAtZoom: null,\n\n\t\t// Setting this to false prevents the removal of any clusters outside of the viewpoint, which\n\t\t// is the default behaviour for performance reasons.\n\t\tremoveOutsideVisibleBounds: true,\n\n\t\t// Set to false to disable all animations (zoom and spiderfy).\n\t\t// If false, option animateAddingMarkers below has no effect.\n\t\t// If L.DomUtil.TRANSITION is falsy, this option has no effect.\n\t\tanimate: true,\n\n\t\t//Whether to animate adding markers after adding the MarkerClusterGroup to the map\n\t\t// If you are adding individual markers set to true, if adding bulk markers leave false for massive performance gains.\n\t\tanimateAddingMarkers: false,\n\n\t\t// Make it possible to provide custom function to calculate spiderfy shape positions\n\t\tspiderfyShapePositions: null,\n\n\t\t//Increase to increase the distance away that spiderfied markers appear from the center\n\t\tspiderfyDistanceMultiplier: 1,\n\n\t\t// Make it possible to specify a polyline options on a spider leg\n\t\tspiderLegPolylineOptions: { weight: 1.5, color: '#222', opacity: 0.5 },\n\n\t\t// When bulk adding layers, adds markers in chunks. Means addLayers may not add all the layers in the call, others will be loaded during setTimeouts\n\t\tchunkedLoading: false,\n\t\tchunkInterval: 200, // process markers for a maximum of ~ n milliseconds (then trigger the chunkProgress callback)\n\t\tchunkDelay: 50, // at the end of each interval, give n milliseconds back to system/browser\n\t\tchunkProgress: null, // progress callback: function(processed, total, elapsed) (e.g. for a progress indicator)\n\n\t\t//Options to pass to the L.Polygon constructor\n\t\tpolygonOptions: {}\n\t},\n\n\tinitialize: function (options) {\n\t\tL.Util.setOptions(this, options);\n\t\tif (!this.options.iconCreateFunction) {\n\t\t\tthis.options.iconCreateFunction = this._defaultIconCreateFunction;\n\t\t}\n\n\t\tthis._featureGroup = L.featureGroup();\n\t\tthis._featureGroup.addEventParent(this);\n\n\t\tthis._nonPointGroup = L.featureGroup();\n\t\tthis._nonPointGroup.addEventParent(this);\n\n\t\tthis._inZoomAnimation = 0;\n\t\tthis._needsClustering = [];\n\t\tthis._needsRemoving = []; //Markers removed while we aren't on the map need to be kept track of\n\t\t//The bounds of the currently shown area (from _getExpandedVisibleBounds) Updated on zoom/move\n\t\tthis._currentShownBounds = null;\n\n\t\tthis._queue = [];\n\n\t\tthis._childMarkerEventHandlers = {\n\t\t\t'dragstart': this._childMarkerDragStart,\n\t\t\t'move': this._childMarkerMoved,\n\t\t\t'dragend': this._childMarkerDragEnd,\n\t\t};\n\n\t\t// Hook the appropriate animation methods.\n\t\tvar animate = L.DomUtil.TRANSITION && this.options.animate;\n\t\tL.extend(this, animate ? this._withAnimation : this._noAnimation);\n\t\t// Remember which MarkerCluster class to instantiate (animated or not).\n\t\tthis._markerCluster = animate ? L.MarkerCluster : L.MarkerClusterNonAnimated;\n\t},\n\n\taddLayer: function (layer) {\n\n\t\tif (layer instanceof L.LayerGroup) {\n\t\t\treturn this.addLayers([layer]);\n\t\t}\n\n\t\t//Don't cluster non point data\n\t\tif (!layer.getLatLng) {\n\t\t\tthis._nonPointGroup.addLayer(layer);\n\t\t\tthis.fire('layeradd', { layer: layer });\n\t\t\treturn this;\n\t\t}\n\n\t\tif (!this._map) {\n\t\t\tthis._needsClustering.push(layer);\n\t\t\tthis.fire('layeradd', { layer: layer });\n\t\t\treturn this;\n\t\t}\n\n\t\tif (this.hasLayer(layer)) {\n\t\t\treturn this;\n\t\t}\n\n\n\t\t//If we have already clustered we'll need to add this one to a cluster\n\n\t\tif (this._unspiderfy) {\n\t\t\tthis._unspiderfy();\n\t\t}\n\n\t\tthis._addLayer(layer, this._maxZoom);\n\t\tthis.fire('layeradd', { layer: layer });\n\n\t\t// Refresh bounds and weighted positions.\n\t\tthis._topClusterLevel._recalculateBounds();\n\n\t\tthis._refreshClustersIcons();\n\n\t\t//Work out what is visible\n\t\tvar visibleLayer = layer,\n\t\t currentZoom = this._zoom;\n\t\tif (layer.__parent) {\n\t\t\twhile (visibleLayer.__parent._zoom >= currentZoom) {\n\t\t\t\tvisibleLayer = visibleLayer.__parent;\n\t\t\t}\n\t\t}\n\n\t\tif (this._currentShownBounds.contains(visibleLayer.getLatLng())) {\n\t\t\tif (this.options.animateAddingMarkers) {\n\t\t\t\tthis._animationAddLayer(layer, visibleLayer);\n\t\t\t} else {\n\t\t\t\tthis._animationAddLayerNonAnimated(layer, visibleLayer);\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t},\n\n\tremoveLayer: function (layer) {\n\n\t\tif (layer instanceof L.LayerGroup) {\n\t\t\treturn this.removeLayers([layer]);\n\t\t}\n\n\t\t//Non point layers\n\t\tif (!layer.getLatLng) {\n\t\t\tthis._nonPointGroup.removeLayer(layer);\n\t\t\tthis.fire('layerremove', { layer: layer });\n\t\t\treturn this;\n\t\t}\n\n\t\tif (!this._map) {\n\t\t\tif (!this._arraySplice(this._needsClustering, layer) && this.hasLayer(layer)) {\n\t\t\t\tthis._needsRemoving.push({ layer: layer, latlng: layer._latlng });\n\t\t\t}\n\t\t\tthis.fire('layerremove', { layer: layer });\n\t\t\treturn this;\n\t\t}\n\n\t\tif (!layer.__parent) {\n\t\t\treturn this;\n\t\t}\n\n\t\tif (this._unspiderfy) {\n\t\t\tthis._unspiderfy();\n\t\t\tthis._unspiderfyLayer(layer);\n\t\t}\n\n\t\t//Remove the marker from clusters\n\t\tthis._removeLayer(layer, true);\n\t\tthis.fire('layerremove', { layer: layer });\n\n\t\t// Refresh bounds and weighted positions.\n\t\tthis._topClusterLevel._recalculateBounds();\n\n\t\tthis._refreshClustersIcons();\n\n\t\tlayer.off(this._childMarkerEventHandlers, this);\n\n\t\tif (this._featureGroup.hasLayer(layer)) {\n\t\t\tthis._featureGroup.removeLayer(layer);\n\t\t\tif (layer.clusterShow) {\n\t\t\t\tlayer.clusterShow();\n\t\t\t}\n\t\t}\n\n\t\treturn this;\n\t},\n\n\t//Takes an array of markers and adds them in bulk\n\taddLayers: function (layersArray, skipLayerAddEvent) {\n\t\tif (!L.Util.isArray(layersArray)) {\n\t\t\treturn this.addLayer(layersArray);\n\t\t}\n\n\t\tvar fg = this._featureGroup,\n\t\t npg = this._nonPointGroup,\n\t\t chunked = this.options.chunkedLoading,\n\t\t chunkInterval = this.options.chunkInterval,\n\t\t chunkProgress = this.options.chunkProgress,\n\t\t l = layersArray.length,\n\t\t offset = 0,\n\t\t originalArray = true,\n\t\t m;\n\n\t\tif (this._map) {\n\t\t\tvar started = (new Date()).getTime();\n\t\t\tvar process = L.bind(function () {\n\t\t\t\tvar start = (new Date()).getTime();\n\n\t\t\t\t// Make sure to unspiderfy before starting to add some layers\n\t\t\t\tif (this._map && this._unspiderfy) {\n\t\t\t\t\tthis._unspiderfy();\n\t\t\t\t}\n\n\t\t\t\tfor (; offset < l; offset++) {\n\t\t\t\t\tif (chunked && offset % 200 === 0) {\n\t\t\t\t\t\t// every couple hundred markers, instrument the time elapsed since processing started:\n\t\t\t\t\t\tvar elapsed = (new Date()).getTime() - start;\n\t\t\t\t\t\tif (elapsed > chunkInterval) {\n\t\t\t\t\t\t\tbreak; // been working too hard, time to take a break :-)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tm = layersArray[offset];\n\n\t\t\t\t\t// Group of layers, append children to layersArray and skip.\n\t\t\t\t\t// Side effects:\n\t\t\t\t\t// - Total increases, so chunkProgress ratio jumps backward.\n\t\t\t\t\t// - Groups are not included in this group, only their non-group child layers (hasLayer).\n\t\t\t\t\t// Changing array length while looping does not affect performance in current browsers:\n\t\t\t\t\t// http://jsperf.com/for-loop-changing-length/6\n\t\t\t\t\tif (m instanceof L.LayerGroup) {\n\t\t\t\t\t\tif (originalArray) {\n\t\t\t\t\t\t\tlayersArray = layersArray.slice();\n\t\t\t\t\t\t\toriginalArray = false;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tthis._extractNonGroupLayers(m, layersArray);\n\t\t\t\t\t\tl = layersArray.length;\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\t//Not point data, can't be clustered\n\t\t\t\t\tif (!m.getLatLng) {\n\t\t\t\t\t\tnpg.addLayer(m);\n\t\t\t\t\t\tif (!skipLayerAddEvent) {\n\t\t\t\t\t\t\tthis.fire('layeradd', { layer: m });\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tif (this.hasLayer(m)) {\n\t\t\t\t\t\tcontinue;\n\t\t\t\t\t}\n\n\t\t\t\t\tthis._addLayer(m, this._maxZoom);\n\t\t\t\t\tif (!skipLayerAddEvent) {\n\t\t\t\t\t\tthis.fire('layeradd', { layer: m });\n\t\t\t\t\t}\n\n\t\t\t\t\t//If we just made a cluster of size 2 then we need to remove the other marker from the map (if it is) or we never will\n\t\t\t\t\tif (m.__parent) {\n\t\t\t\t\t\tif (m.__parent.getChildCount() === 2) {\n\t\t\t\t\t\t\tvar markers = m.__parent.getAllChildMarkers(),\n\t\t\t\t\t\t\t otherMarker = markers[0] === m ? markers[1] : markers[0];\n\t\t\t\t\t\t\tfg.removeLayer(otherMarker);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tif (chunkProgress) {\n\t\t\t\t\t// report progress and time elapsed:\n\t\t\t\t\tchunkProgress(offset, l, (new Date()).getTime() - started);\n\t\t\t\t}\n\n\t\t\t\t// Completed processing all markers.\n\t\t\t\tif (offset === l) {\n\n\t\t\t\t\t// Refresh bounds and weighted positions.\n\t\t\t\t\tthis._topClusterLevel._recalculateBounds();\n\n\t\t\t\t\tthis._refreshClustersIcons();\n\n\t\t\t\t\tthis._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds);\n\t\t\t\t} else {\n\t\t\t\t\tsetTimeout(process, this.options.chunkDelay);\n\t\t\t\t}\n\t\t\t}, this);\n\n\t\t\tprocess();\n\t\t} else {\n\t\t\tvar needsClustering = this._needsClustering;\n\n\t\t\tfor (; offset < l; offset++) {\n\t\t\t\tm = layersArray[offset];\n\n\t\t\t\t// Group of layers, append children to layersArray and skip.\n\t\t\t\tif (m instanceof L.LayerGroup) {\n\t\t\t\t\tif (originalArray) {\n\t\t\t\t\t\tlayersArray = layersArray.slice();\n\t\t\t\t\t\toriginalArray = false;\n\t\t\t\t\t}\n\t\t\t\t\tthis._extractNonGroupLayers(m, layersArray);\n\t\t\t\t\tl = layersArray.length;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\t//Not point data, can't be clustered\n\t\t\t\tif (!m.getLatLng) {\n\t\t\t\t\tnpg.addLayer(m);\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tif (this.hasLayer(m)) {\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tneedsClustering.push(m);\n\t\t\t}\n\t\t}\n\t\treturn this;\n\t},\n\n\t//Takes an array of markers and removes them in bulk\n\tremoveLayers: function (layersArray) {\n\t\tvar i, m,\n\t\t l = layersArray.length,\n\t\t fg = this._featureGroup,\n\t\t npg = this._nonPointGroup,\n\t\t originalArray = true;\n\n\t\tif (!this._map) {\n\t\t\tfor (i = 0; i < l; i++) {\n\t\t\t\tm = layersArray[i];\n\n\t\t\t\t// Group of layers, append children to layersArray and skip.\n\t\t\t\tif (m instanceof L.LayerGroup) {\n\t\t\t\t\tif (originalArray) {\n\t\t\t\t\t\tlayersArray = layersArray.slice();\n\t\t\t\t\t\toriginalArray = false;\n\t\t\t\t\t}\n\t\t\t\t\tthis._extractNonGroupLayers(m, layersArray);\n\t\t\t\t\tl = layersArray.length;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthis._arraySplice(this._needsClustering, m);\n\t\t\t\tnpg.removeLayer(m);\n\t\t\t\tif (this.hasLayer(m)) {\n\t\t\t\t\tthis._needsRemoving.push({ layer: m, latlng: m._latlng });\n\t\t\t\t}\n\t\t\t\tthis.fire('layerremove', { layer: m });\n\t\t\t}\n\t\t\treturn this;\n\t\t}\n\n\t\tif (this._unspiderfy) {\n\t\t\tthis._unspiderfy();\n\n\t\t\t// Work on a copy of the array, so that next loop is not affected.\n\t\t\tvar layersArray2 = layersArray.slice(),\n\t\t\t l2 = l;\n\t\t\tfor (i = 0; i < l2; i++) {\n\t\t\t\tm = layersArray2[i];\n\n\t\t\t\t// Group of layers, append children to layersArray and skip.\n\t\t\t\tif (m instanceof L.LayerGroup) {\n\t\t\t\t\tthis._extractNonGroupLayers(m, layersArray2);\n\t\t\t\t\tl2 = layersArray2.length;\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\n\t\t\t\tthis._unspiderfyLayer(m);\n\t\t\t}\n\t\t}\n\n\t\tfor (i = 0; i < l; i++) {\n\t\t\tm = layersArray[i];\n\n\t\t\t// Group of layers, append children to layersArray and skip.\n\t\t\tif (m instanceof L.LayerGroup) {\n\t\t\t\tif (originalArray) {\n\t\t\t\t\tlayersArray = layersArray.slice();\n\t\t\t\t\toriginalArray = false;\n\t\t\t\t}\n\t\t\t\tthis._extractNonGroupLayers(m, layersArray);\n\t\t\t\tl = layersArray.length;\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tif (!m.__parent) {\n\t\t\t\tnpg.removeLayer(m);\n\t\t\t\tthis.fire('layerremove', { layer: m });\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tthis._removeLayer(m, true, true);\n\t\t\tthis.fire('layerremove', { layer: m });\n\n\t\t\tif (fg.hasLayer(m)) {\n\t\t\t\tfg.removeLayer(m);\n\t\t\t\tif (m.clusterShow) {\n\t\t\t\t\tm.clusterShow();\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Refresh bounds and weighted positions.\n\t\tthis._topClusterLevel._recalculateBounds();\n\n\t\tthis._refreshClustersIcons();\n\n\t\t//Fix up the clusters and markers on the map\n\t\tthis._topClusterLevel._recursivelyAddChildrenToMap(null, this._zoom, this._currentShownBounds);\n\n\t\treturn this;\n\t},\n\n\t//Removes all layers from the MarkerClusterGroup\n\tclearLayers: function () {\n\t\t//Need our own special implementation as the LayerGroup one doesn't work for us\n\n\t\t//If we aren't on the map (yet), blow away the markers we know of\n\t\tif (!this._map) {\n\t\t\tthis._needsClustering = [];\n\t\t\tthis._needsRemoving = [];\n\t\t\tdelete this._gridClusters;\n\t\t\tdelete this._gridUnclustered;\n\t\t}\n\n\t\tif (this._noanimationUnspiderfy) {\n\t\t\tthis._noanimationUnspiderfy();\n\t\t}\n\n\t\t//Remove all the visible layers\n\t\tthis._featureGroup.clearLayers();\n\t\tthis._nonPointGroup.clearLayers();\n\n\t\tthis.eachLayer(function (marker) {\n\t\t\tmarker.off(this._childMarkerEventHandlers, this);\n\t\t\tdelete marker.__parent;\n\t\t}, this);\n\n\t\tif (this._map) {\n\t\t\t//Reset _topClusterLevel and the DistanceGrids\n\t\t\tthis._generateInitialClusters();\n\t\t}\n\n\t\treturn this;\n\t},\n\n\t//Override FeatureGroup.getBounds as it doesn't work\n\tgetBounds: function () {\n\t\tvar bounds = new L.LatLngBounds();\n\n\t\tif (this._topClusterLevel) {\n\t\t\tbounds.extend(this._topClusterLevel._bounds);\n\t\t}\n\n\t\tfor (var i = this._needsClustering.length - 1; i >= 0; i--) {\n\t\t\tbounds.extend(this._needsClustering[i].getLatLng());\n\t\t}\n\n\t\tbounds.extend(this._nonPointGroup.getBounds());\n\n\t\treturn bounds;\n\t},\n\n\t//Overrides LayerGroup.eachLayer\n\teachLayer: function (method, context) {\n\t\tvar markers = this._needsClustering.slice(),\n\t\t\tneedsRemoving = this._needsRemoving,\n\t\t\tthisNeedsRemoving, i, j;\n\n\t\tif (this._topClusterLevel) {\n\t\t\tthis._topClusterLevel.getAllChildMarkers(markers);\n\t\t}\n\n\t\tfor (i = markers.length - 1; i >= 0; i--) {\n\t\t\tthisNeedsRemoving = true;\n\n\t\t\tfor (j = needsRemoving.length - 1; j >= 0; j--) {\n\t\t\t\tif (needsRemoving[j].layer === markers[i]) {\n\t\t\t\t\tthisNeedsRemoving = false;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif (thisNeedsRemoving) {\n\t\t\t\tmethod.call(context, markers[i]);\n\t\t\t}\n\t\t}\n\n\t\tthis._nonPointGroup.eachLayer(method, context);\n\t},\n\n\t//Overrides LayerGroup.getLayers\n\tgetLayers: function () {\n\t\tvar layers = [];\n\t\tthis.eachLayer(function (l) {\n\t\t\tlayers.push(l);\n\t\t});\n\t\treturn layers;\n\t},\n\n\t//Overrides LayerGroup.getLayer, WARNING: Really bad performance\n\tgetLayer: function (id) {\n\t\tvar result = null;\n\n\t\tid = parseInt(id, 10);\n\n\t\tthis.eachLayer(function (l) {\n\t\t\tif (L.stamp(l) === id) {\n\t\t\t\tresult = l;\n\t\t\t}\n\t\t});\n\n\t\treturn result;\n\t},\n\n\t//Returns true if the given layer is in this MarkerClusterGroup\n\thasLayer: function (layer) {\n\t\tif (!layer) {\n\t\t\treturn false;\n\t\t}\n\n\t\tvar i, anArray = this._needsClustering;\n\n\t\tfor (i = anArray.length - 1; i >= 0; i--) {\n\t\t\tif (anArray[i] === layer) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\tanArray = this._needsRemoving;\n\t\tfor (i = anArray.length - 1; i >= 0; i--) {\n\t\t\tif (anArray[i].layer === layer) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\treturn !!(layer.__parent && layer.__parent._group === this) || this._nonPointGroup.hasLayer(layer);\n\t},\n\n\t//Zoom down to show the given layer (spiderfying if necessary) then calls the callback\n\tzoomToShowLayer: function (layer, callback) {\n\n\t\tvar map = this._map;\n\n\t\tif (typeof callback !== 'function') {\n\t\t\tcallback = function () {};\n\t\t}\n\n\t\tvar showMarker = function () {\n\t\t\t// Assumes that map.hasLayer checks for direct appearance on map, not recursively calling\n\t\t\t// hasLayer on Layer Groups that are on map (typically not calling this MarkerClusterGroup.hasLayer, which would always return true)\n\t\t\tif ((map.hasLayer(layer) || map.hasLayer(layer.__parent)) && !this._inZoomAnimation) {\n\t\t\t\tthis._map.off('moveend', showMarker, this);\n\t\t\t\tthis.off('animationend', showMarker, this);\n\n\t\t\t\tif (map.hasLayer(layer)) {\n\t\t\t\t\tcallback();\n\t\t\t\t} else if (layer.__parent._icon) {\n\t\t\t\t\tthis.once('spiderfied', callback, this);\n\t\t\t\t\tlayer.__parent.spiderfy();\n\t\t\t\t}\n\t\t\t}\n\t\t};\n\n\t\tif (layer._icon && this._map.getBounds().contains(layer.getLatLng())) {\n\t\t\t//Layer is visible ond on screen, immediate return\n\t\t\tcallback();\n\t\t} else if (layer.__parent._zoom < Math.round(this._map._zoom)) {\n\t\t\t//Layer should be visible at this zoom level. It must not be on screen so just pan over to it\n\t\t\tthis._map.on('moveend', showMarker, this);\n\t\t\tthis._map.panTo(layer.getLatLng());\n\t\t} else {\n\t\t\tthis._map.on('moveend', showMarker, this);\n\t\t\tthis.on('animationend', showMarker, this);\n\t\t\tlayer.__parent.zoomToBounds();\n\t\t}\n\t},\n\n\t//Overrides FeatureGroup.onAdd\n\tonAdd: function (map) {\n\t\tthis._map = map;\n\t\tvar i, l, layer;\n\n\t\tif (!isFinite(this._map.getMaxZoom())) {\n\t\t\tthrow \"Map has no maxZoom specified\";\n\t\t}\n\n\t\tthis._featureGroup.addTo(map);\n\t\tthis._nonPointGroup.addTo(map);\n\n\t\tif (!this._gridClusters) {\n\t\t\tthis._generateInitialClusters();\n\t\t}\n\n\t\tthis._maxLat = map.options.crs.projection.MAX_LATITUDE;\n\n\t\t//Restore all the positions as they are in the MCG before removing them\n\t\tfor (i = 0, l = this._needsRemoving.length; i < l; i++) {\n\t\t\tlayer = this._needsRemoving[i];\n\t\t\tlayer.newlatlng = layer.layer._latlng;\n\t\t\tlayer.layer._latlng = layer.latlng;\n\t\t}\n\t\t//Remove them, then restore their new positions\n\t\tfor (i = 0, l = this._needsRemoving.length; i < l; i++) {\n\t\t\tlayer = this._needsRemoving[i];\n\t\t\tthis._removeLayer(layer.layer, true);\n\t\t\tlayer.layer._latlng = layer.newlatlng;\n\t\t}\n\t\tthis._needsRemoving = [];\n\n\t\t//Remember the current zoom level and bounds\n\t\tthis._zoom = Math.round(this._map._zoom);\n\t\tthis._currentShownBounds = this._getExpandedVisibleBounds();\n\n\t\tthis._map.on('zoomend', this._zoomEnd, this);\n\t\tthis._map.on('moveend', this._moveEnd, this);\n\n\t\tif (this._spiderfierOnAdd) { //TODO FIXME: Not sure how to have spiderfier add something on here nicely\n\t\t\tthis._spiderfierOnAdd();\n\t\t}\n\n\t\tthis._bindEvents();\n\n\t\t//Actually add our markers to the map:\n\t\tl = this._needsClustering;\n\t\tthis._needsClustering = [];\n\t\tthis.addLayers(l, true);\n\t},\n\n\t//Overrides FeatureGroup.onRemove\n\tonRemove: function (map) {\n\t\tmap.off('zoomend', this._zoomEnd, this);\n\t\tmap.off('moveend', this._moveEnd, this);\n\n\t\tthis._unbindEvents();\n\n\t\t//In case we are in a cluster animation\n\t\tthis._map._mapPane.className = this._map._mapPane.className.replace(' leaflet-cluster-anim', '');\n\n\t\tif (this._spiderfierOnRemove) { //TODO FIXME: Not sure how to have spiderfier add something on here nicely\n\t\t\tthis._spiderfierOnRemove();\n\t\t}\n\n\t\tdelete this._maxLat;\n\n\t\t//Clean up all the layers we added to the map\n\t\tthis._hideCoverage();\n\t\tthis._featureGroup.remove();\n\t\tthis._nonPointGroup.remove();\n\n\t\tthis._featureGroup.clearLayers();\n\n\t\tthis._map = null;\n\t},\n\n\tgetVisibleParent: function (marker) {\n\t\tvar vMarker = marker;\n\t\twhile (vMarker && !vMarker._icon) {\n\t\t\tvMarker = vMarker.__parent;\n\t\t}\n\t\treturn vMarker || null;\n\t},\n\n\t//Remove the given object from the given array\n\t_arraySplice: function (anArray, obj) {\n\t\tfor (var i = anArray.length - 1; i >= 0; i--) {\n\t\t\tif (anArray[i] === obj) {\n\t\t\t\tanArray.splice(i, 1);\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\t},\n\n\t/**\n\t * Removes a marker from all _gridUnclustered zoom levels, starting at the supplied zoom.\n\t * @param marker to be removed from _gridUnclustered.\n\t * @param z integer bottom start zoom level (included)\n\t * @private\n\t */\n\t_removeFromGridUnclustered: function (marker, z) {\n\t\tvar map = this._map,\n\t\t gridUnclustered = this._gridUnclustered,\n\t\t\tminZoom = Math.floor(this._map.getMinZoom());\n\n\t\tfor (; z >= minZoom; z--) {\n\t\t\tif (!gridUnclustered[z].removeObject(marker, map.project(marker.getLatLng(), z))) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t},\n\n\t_childMarkerDragStart: function (e) {\n\t\te.target.__dragStart = e.target._latlng;\n\t},\n\n\t_childMarkerMoved: function (e) {\n\t\tif (!this._ignoreMove && !e.target.__dragStart) {\n\t\t\tvar isPopupOpen = e.target._popup && e.target._popup.isOpen();\n\n\t\t\tthis._moveChild(e.target, e.oldLatLng, e.latlng);\n\n\t\t\tif (isPopupOpen) {\n\t\t\t\te.target.openPopup();\n\t\t\t}\n\t\t}\n\t},\n\n\t_moveChild: function (layer, from, to) {\n\t\tlayer._latlng = from;\n\t\tthis.removeLayer(layer);\n\n\t\tlayer._latlng = to;\n\t\tthis.addLayer(layer);\n\t},\n\n\t_childMarkerDragEnd: function (e) {\n\t\tvar dragStart = e.target.__dragStart;\n\t\tdelete e.target.__dragStart;\n\t\tif (dragStart) {\n\t\t\tthis._moveChild(e.target, dragStart, e.target._latlng);\n\t\t}\t\t\n\t},\n\n\n\t//Internal function for removing a marker from everything.\n\t//dontUpdateMap: set to true if you will handle updating the map manually (for bulk functions)\n\t_removeLayer: function (marker, removeFromDistanceGrid, dontUpdateMap) {\n\t\tvar gridClusters = this._gridClusters,\n\t\t\tgridUnclustered = this._gridUnclustered,\n\t\t\tfg = this._featureGroup,\n\t\t\tmap = this._map,\n\t\t\tminZoom = Math.floor(this._map.getMinZoom());\n\n\t\t//Remove the marker from distance clusters it might be in\n\t\tif (removeFromDistanceGrid) {\n\t\t\tthis._removeFromGridUnclustered(marker, this._maxZoom);\n\t\t}\n\n\t\t//Work our way up the clusters removing them as we go if required\n\t\tvar cluster = marker.__parent,\n\t\t\tmarkers = cluster._markers,\n\t\t\totherMarker;\n\n\t\t//Remove the marker from the immediate parents marker list\n\t\tthis._arraySplice(markers, marker);\n\n\t\twhile (cluster) {\n\t\t\tcluster._childCount--;\n\t\t\tcluster._boundsNeedUpdate = true;\n\n\t\t\tif (cluster._zoom < minZoom) {\n\t\t\t\t//Top level, do nothing\n\t\t\t\tbreak;\n\t\t\t} else if (removeFromDistanceGrid && cluster._childCount <= 1) { //Cluster no longer required\n\t\t\t\t//We need to push the other marker up to the parent\n\t\t\t\totherMarker = cluster._markers[0] === marker ? cluster._markers[1] : cluster._markers[0];\n\n\t\t\t\t//Update distance grid\n\t\t\t\tgridClusters[cluster._zoom].removeObject(cluster, map.project(cluster._cLatLng, cluster._zoom));\n\t\t\t\tgridUnclustered[cluster._zoom].addObject(otherMarker, map.project(otherMarker.getLatLng(), cluster._zoom));\n\n\t\t\t\t//Move otherMarker up to parent\n\t\t\t\tthis._arraySplice(cluster.__parent._childClusters, cluster);\n\t\t\t\tcluster.__parent._markers.push(otherMarker);\n\t\t\t\totherMarker.__parent = cluster.__parent;\n\n\t\t\t\tif (cluster._icon) {\n\t\t\t\t\t//Cluster is currently on the map, need to put the marker on the map instead\n\t\t\t\t\tfg.removeLayer(cluster);\n\t\t\t\t\tif (!dontUpdateMap) {\n\t\t\t\t\t\tfg.addLayer(otherMarker);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tcluster._iconNeedsUpdate = true;\n\t\t\t}\n\n\t\t\tcluster = cluster.__parent;\n\t\t}\n\n\t\tdelete marker.__parent;\n\t},\n\n\t_isOrIsParent: function (el, oel) {\n\t\twhile (oel) {\n\t\t\tif (el === oel) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t\toel = oel.parentNode;\n\t\t}\n\t\treturn false;\n\t},\n\n\t//Override L.Evented.fire\n\tfire: function (type, data, propagate) {\n\t\tif (data && data.layer instanceof L.MarkerCluster) {\n\t\t\t//Prevent multiple clustermouseover/off events if the icon is made up of stacked divs (Doesn't work in ie <= 8, no relatedTarget)\n\t\t\tif (data.originalEvent && this._isOrIsParent(data.layer._icon, data.originalEvent.relatedTarget)) {\n\t\t\t\treturn;\n\t\t\t}\n\t\t\ttype = 'cluster' + type;\n\t\t}\n\n\t\tL.FeatureGroup.prototype.fire.call(this, type, data, propagate);\n\t},\n\n\t//Override L.Evented.listens\n\tlistens: function (type, propagate) {\n\t\treturn L.FeatureGroup.prototype.listens.call(this, type, propagate) || L.FeatureGroup.prototype.listens.call(this, 'cluster' + type, propagate);\n\t},\n\n\t//Default functionality\n\t_defaultIconCreateFunction: function (cluster) {\n\t\tvar childCount = cluster.getChildCount();\n\n\t\tvar c = ' marker-cluster-';\n\t\tif (childCount < 10) {\n\t\t\tc += 'small';\n\t\t} else if (childCount < 100) {\n\t\t\tc += 'medium';\n\t\t} else {\n\t\t\tc += 'large';\n\t\t}\n\n\t\treturn new L.DivIcon({ html: '