1/** 2 * Extends the L.MarkerClusterGroup class by mainly overriding methods for 3 * addition/removal of layers, so that they can also be directly added/removed 4 * from the map later on while still clustering in this group. 5 * @type {L.MarkerClusterGroup} 6 */ 7L.MarkerClusterGroup.LayerSupport = L.MarkerClusterGroup.extend({ 8 9 options: { 10 // Buffer single addLayer and removeLayer requests for efficiency. 11 singleAddRemoveBufferDuration: 0 // in ms. 12 }, 13 14 initialize: function (options) { 15 L.MarkerClusterGroup.prototype.initialize.call(this, options); 16 17 // Replace the MCG internal featureGroup's so that they directly 18 // access the map add/removal methods, bypassing the switch agent. 19 this._featureGroup = new _ByPassingFeatureGroup(); 20 this._featureGroup.addEventParent(this); 21 22 this._nonPointGroup = new _ByPassingFeatureGroup(); 23 this._nonPointGroup.addEventParent(this); 24 25 // Keep track of what should be "represented" on map (can be clustered). 26 this._layers = {}; 27 this._proxyLayerGroups = {}; 28 this._proxyLayerGroupsNeedRemoving = {}; 29 30 // Buffer single addLayer and removeLayer requests. 31 this._singleAddRemoveBuffer = []; 32 }, 33 34 /** 35 * Stamps the passed layers as being part of this group, but without adding 36 * them to the map right now. 37 * @param layers L.Layer|Array(L.Layer) layer(s) to be stamped. 38 * @returns {L.MarkerClusterGroup.LayerSupport} this. 39 */ 40 checkIn: function (layers) { 41 var layersArray = this._toArray(layers); 42 43 this._checkInGetSeparated(layersArray); 44 45 return this; 46 }, 47 48 /** 49 * Un-stamps the passed layers from being part of this group. It has to 50 * remove them from map (if they are) since they will no longer cluster. 51 * @param layers L.Layer|Array(L.Layer) layer(s) to be un-stamped. 52 * @returns {L.MarkerClusterGroup.LayerSupport} this. 53 */ 54 checkOut: function (layers) { 55 var layersArray = this._toArray(layers), 56 separated = this._separateSingleFromGroupLayers(layersArray, { 57 groups: [], 58 singles: [] 59 }), 60 groups = separated.groups, 61 singles = separated.singles, 62 i, layer; 63 64 // Un-stamp single layers. 65 for (i = 0; i < singles.length; i++) { 66 layer = singles[i]; 67 delete this._layers[L.stamp(layer)]; 68 delete layer._mcgLayerSupportGroup; 69 } 70 71 // Batch remove single layers from MCG. 72 // Note: as for standard MCG, if single layers have been added to 73 // another MCG in the meantime, their __parent will have changed, 74 // so weird things would happen. 75 this._originalRemoveLayers(singles); 76 77 // Dismiss Layer Groups. 78 for (i = 0; i < groups.length; i++) { 79 layer = groups[i]; 80 this._dismissProxyLayerGroup(layer); 81 } 82 83 return this; 84 }, 85 86 /** 87 * Checks in and adds an array of layers to this group. 88 * Layer Groups are also added to the map to fire their event. 89 * @param layers (L.Layer|L.Layer[]) single and/or group layers to be added. 90 * @returns {L.MarkerClusterGroup.LayerSupport} this. 91 */ 92 addLayers: function (layers) { 93 var layersArray = this._toArray(layers), 94 separated = this._checkInGetSeparated(layersArray), 95 groups = separated.groups, 96 i, group, id; 97 98 // Batch add all single layers. 99 this._originalAddLayers(separated.singles); 100 101 // Add Layer Groups to the map so that they are registered there and 102 // the map fires 'layeradd' events for them as well. 103 for (i = 0; i < groups.length; i++) { 104 group = groups[i]; 105 id = L.stamp(group); 106 this._proxyLayerGroups[id] = group; 107 delete this._proxyLayerGroupsNeedRemoving[id]; 108 if (this._map) { 109 this._map._originalAddLayer(group); 110 } 111 } 112 }, 113 addLayer: function (layer) { 114 this._bufferSingleAddRemove(layer, "addLayers"); 115 return this; 116 }, 117 _originalAddLayer: L.MarkerClusterGroup.prototype.addLayer, 118 _originalAddLayers: L.MarkerClusterGroup.prototype.addLayers, 119 120 /** 121 * Removes layers from this group but without check out. 122 * Layer Groups are also removed from the map to fire their event. 123 * @param layers (L.Layer|L.Layer[]) single and/or group layers to be removed. 124 * @returns {L.MarkerClusterGroup.LayerSupport} this. 125 */ 126 removeLayers: function (layers) { 127 var layersArray = this._toArray(layers), 128 separated = this._separateSingleFromGroupLayers(layersArray, {
129 groups: [], 130 singles: [] 131 }), 132 groups = separated.groups, 133 singles = separated.singles, 134 i = 0, 135 group, id; 136 137 // Batch remove single layers from MCG. 138 this._originalRemoveLayers(singles); 139 140 // Remove Layer Groups from the map so that they are un-registered 141 // there and the map fires 'layerremove' events for them as well. 142 for (; i < groups.length; i++) { 143 group = groups[i]; 144 id = L.stamp(group); 145 delete this._proxyLayerGroups[id]; 146 if (this._map) { 147 this._map._originalRemoveLayer(group); 148 } else { 149 this._proxyLayerGroupsNeedRemoving[id] = group; 150 } 151 } 152 153 return this; 154 }, 155 removeLayer: function (layer) { 156 this._bufferSingleAddRemove(layer, "removeLayers"); 157 return this; 158 }, 159 _originalRemoveLayer: L.MarkerClusterGroup.prototype.removeLayer, 160 _originalRemoveLayers: L.MarkerClusterGroup.prototype.removeLayers, 161 162 onAdd: function (map) { 163 // Replace the map addLayer and removeLayer methods to place the 164 // switch agent that redirects layers when required. 165 map._originalAddLayer = map._originalAddLayer || map.addLayer; 166 map._originalRemoveLayer = map._originalRemoveLayer || map.removeLayer; 167 L.extend(map, _layerSwitchMap); 168 169 // As this plugin allows the Application to add layers on map, some 170 // checked in layers might have been added already, whereas LayerSupport 171 // did not have a chance to inject the switch agent in to the map 172 // (if it was never added to map before). Therefore we need to 173 // remove all checked in layers from map! 174 var toBeReAdded = this._removePreAddedLayers(map), 175 id, group, i; 176 177 // Normal MCG onAdd. 178 this._originalOnAdd.call(this, map); 179 180 // If layer Groups are added/removed from this group while it is not 181 // on map, Control.Layers gets out of sync until this is added back. 182 183 // Restore proxy Layer Groups that may have been added to this 184 // group while it was off map. 185 for (id in this._proxyLayerGroups) { 186 group = this._proxyLayerGroups[id]; 187 map._originalAddLayer(group); 188 } 189 190 // Remove proxy Layer Groups that may have been removed from this 191 // group while it was off map. 192 for (id in this._proxyLayerGroupsNeedRemoving) { 193 group = this._proxyLayerGroupsNeedRemoving[id]; 194 map._originalRemoveLayer(group); 195 delete this._proxyLayerGroupsNeedRemoving[id]; 196 } 197 198 // Restore Layers. 199 for (i = 0; i < toBeReAdded.length; i++) { 200 map.addLayer(toBeReAdded[i]); 201 } 202 }, 203 _originalOnAdd: L.MarkerClusterGroup.prototype.onAdd, 204 205 // Do not restore the original map methods when removing the group from it. 206 // Leaving them as-is does not harm, whereas restoring the original ones 207 // may kill the functionality of potential other LayerSupport groups on 208 // the same map. Therefore we do not need to override onRemove. 209 210 _bufferSingleAddRemove: function (layer, operationType) { 211 var duration = this.options.singleAddRemoveBufferDuration, 212 fn; 213 214 if (duration > 0) { 215 this._singleAddRemoveBuffer.push({ 216 type: operationType, 217 layer: layer 218 }); 219 220 if (!this._singleAddRemoveBufferTimeout) { 221 fn = L.bind(this._processSingleAddRemoveBuffer, this); 222 223 this._singleAddRemoveBufferTimeout = setTimeout(fn, duration); 224 } 225 } else { // If duration <= 0, process synchronously. 226 this[operationType](layer); 227 } 228 }, 229 _processSingleAddRemoveBuffer: function () { 230 // For now, simply cut the processes at each operation change 231 // (addLayers, removeLayers). 232 var singleAddRemoveBuffer = this._singleAddRemoveBuffer, 233 i = 0, 234 layersBuffer = [], 235 currentOperation, 236 currentOperationType; 237 238 for (; i < singleAddRemoveBuffer.length; i++) { 239 currentOperation = singleAddRemoveBuffer[i]; 240 if (!currentOperationType) { 241 currentOperationType = currentOperation.type; 242 } 243 if (currentOperation.type === currentOperationType) { 244 layersBuffer.push(currentOperation.layer); 245 } else { 246 this[currentOperationType](layersBuffer); 247 currentOperationType = currentOperation.type; 248 layersBuffer = [currentOperation.layer]; 249 } 250 } 251 this[currentOperationType](layersBuffer); 252 singleAddRemoveBuffer.length = 0; 253 clearTimeout(this._singleAddRemoveBufferTimeout); 254 this._singleAddRemoveBufferTimeout = null; 255 }, 256 257 _checkInGetSeparated: function (layersArray) { 258 var separated = this._separateSingleFromGroupLayers(layersArray, {
259 groups: [], 260 singles: [] 261 }), 262 groups = separated.groups, 263 singles = separated.singles, 264 i, layer; 265 266 // Recruit Layer Groups. 267 // If they do not already belong to this group, they will be 268 // removed from map (together will all child layers). 269 for (i = 0; i < groups.length; i++) { 270 layer = groups[i]; 271 this._recruitLayerGroupAsProxy(layer); 272 } 273 274 // Stamp single layers. 275 for (i = 0; i < singles.length; i++) { 276 layer = singles[i]; 277 278 // Remove from previous group first. 279 this._removeFromOtherGroupsOrMap(layer); 280 281 this._layers[L.stamp(layer)] = layer; 282 layer._mcgLayerSupportGroup = this; 283 } 284 285 return separated; 286 }, 287 288 _separateSingleFromGroupLayers: function (inputLayers, output) { 289 var groups = output.groups, 290 singles = output.singles, 291 isArray = L.Util.isArray, 292 layer; 293 294 for (var i = 0; i < inputLayers.length; i++) { 295 layer = inputLayers[i]; 296 297 if (layer instanceof L.LayerGroup) { 298 groups.push(layer); 299 this._separateSingleFromGroupLayers(layer.getLayers(), output); 300 continue; 301 } else if (isArray(layer)) { 302 this._separateSingleFromGroupLayers(layer, output); 303 continue; 304 } 305 306 singles.push(layer); 307 } 308 309 return output; 310 }, 311 312 // Recruit the LayerGroup as a proxy, so that any layer that is added 313 // to / removed from that group later on is also added to / removed from 314 // this group. 315 // Check in and addition of already contained markers must be taken care 316 // of externally. 317 _recruitLayerGroupAsProxy: function (layerGroup) { 318 var otherMcgLayerSupportGroup = layerGroup._proxyMcgLayerSupportGroup; 319 320 // If it is not yet in this group, remove it from previous group 321 // or from map. 322 if (otherMcgLayerSupportGroup) { 323 if (otherMcgLayerSupportGroup === this) { 324 return; 325 } 326 // Remove from previous Layer Support group first. 327 // It will also be removed from map with child layers. 328 otherMcgLayerSupportGroup.checkOut(layerGroup); 329 } else { 330 this._removeFromOwnMap(layerGroup); 331 } 332 333 layerGroup._proxyMcgLayerSupportGroup = this; 334 layerGroup._originalAddLayer = 335 layerGroup._originalAddLayer || layerGroup.addLayer; 336 layerGroup._originalRemoveLayer = 337 layerGroup._originalRemoveLayer || layerGroup.removeLayer; 338 layerGroup._originalOnAdd = 339 layerGroup._originalOnAdd || layerGroup.onAdd; 340 layerGroup._originalOnRemove = 341 layerGroup._originalOnRemove || layerGroup.onRemove; 342 L.extend(layerGroup, _proxyLayerGroup); 343 }, 344 345 // Restore the normal LayerGroup behaviour. 346 // Removal and check out of contained markers must be taken care of externally. 347 _dismissProxyLayerGroup: function (layerGroup) { 348 if (layerGroup._proxyMcgLayerSupportGroup === undefined || 349 layerGroup._proxyMcgLayerSupportGroup !== this) { 350 351 return; 352 } 353 354 delete layerGroup._proxyMcgLayerSupportGroup; 355 layerGroup.addLayer = layerGroup._originalAddLayer; 356 layerGroup.removeLayer = layerGroup._originalRemoveLayer; 357 layerGroup.onAdd = layerGroup._originalOnAdd; 358 layerGroup.onRemove = layerGroup._originalOnRemove; 359 360 var id = L.stamp(layerGroup); 361 delete this._proxyLayerGroups[id]; 362 delete this._proxyLayerGroupsNeedRemoving[id]; 363 364 this._removeFromOwnMap(layerGroup); 365 }, 366 367 _removeFromOtherGroupsOrMap: function (layer) { 368 var otherMcgLayerSupportGroup = layer._mcgLayerSupportGroup; 369 370 if (otherMcgLayerSupportGroup) { // It is a Layer Support group. 371 if (otherMcgLayerSupportGroup === this) { 372 return; 373 } 374 otherMcgLayerSupportGroup.checkOut(layer); 375 376 } else if (layer.__parent) { // It is in a normal MCG. 377 layer.__parent._group.removeLayer(layer); 378 379 } else { // It could still be on a map. 380 this._removeFromOwnMap(layer); 381 } 382 }, 383 384 // Remove layers that are being checked in, because they can now cluster. 385 _removeFromOwnMap: function (layer) { 386 if (layer._map) { 387 // This correctly fires layerremove event for Layer Groups as well. 388 layer._map.removeLayer(layer); 389 } 390 }, 391 392 // In case checked in layers have been added to map whereas map is not redirected. 393 _removePreAddedLayers: function (map) { 394 var layers = this._layers, 395 toBeReAdded = [], 396 layer; 397 398 for (var id in layers) { 399 layer = layers[id]; 400 if (layer._map) { 401 toBeReAdded.push(layer); 402 map._originalRemoveLayer(layer); 403 } 404 } 405 406 return toBeReAdded; 407 }, 408 409 _toArray: function (item) { 410 return L.Util.isArray(item) ? item : [item]; 411 } 412 413}); 414 415/** 416 * Extends the FeatureGroup by overriding add/removal methods that directly 417 * access the map original methods, bypassing the switch agent. 418 * Used internally in Layer Support for _featureGroup and _nonPointGroup only. 419 * @type {L.FeatureGroup} 420 * @private 421 */ 422var _ByPassingFeatureGroup = L.FeatureGroup.extend({ 423 424 // Re-implement just to change the map method. 425 addLayer: function (layer) { 426 if (this.hasLayer(layer)) { 427 return this; 428 } 429 430 layer.addEventParent(this); 431 432 var id = L.stamp(layer); 433 434 this._layers[id] = layer; 435 436 if (this._map) { 437 // Use the original map addLayer. 438 this._map._originalAddLayer(layer); 439 } 440 441 return this.fire('layeradd', {layer: layer}); 442 }, 443 444 // Re-implement just to change the map method. 445 removeLayer: function (layer) { 446 if (!this.hasLayer(layer)) { 447 return this; 448 } 449 if (layer in this._layers) { 450 layer = this._layers[layer]; 451 } 452 453 layer.removeEventParent(this); 454 455 var id = L.stamp(layer); 456 457 if (this._map && this._layers[id]) { 458 // Use the original map removeLayer. 459 this._map._originalRemoveLayer(this._layers[id]); 460 } 461 462 delete this._layers[id]; 463 464 return this.fire('layerremove', {layer: layer}); 465 }, 466 467 onAdd: function (map) { 468 this._map = map; 469 // Use the original map addLayer. 470 this.eachLayer(map._originalAddLayer, map); 471 }, 472 473 onRemove: function (map) { 474 // Use the original map removeLayer. 475 this.eachLayer(map._originalRemoveLayer, map); 476 this._map = null; 477 } 478 479}); 480 481/** 482 * Toolbox to equip LayerGroups recruited as proxy. 483 * @type {{addLayer: Function, removeLayer: Function}} 484 * @private 485 */ 486var _proxyLayerGroup = { 487 488 // Re-implement to redirect addLayer to Layer Support group instead of map. 489 addLayer: function (layer) { 490 var id = this.getLayerId(layer); 491 492 this._layers[id] = layer; 493 494 if (this._map) { 495 this._proxyMcgLayerSupportGroup.addLayer(layer); 496 } else { 497 this._proxyMcgLayerSupportGroup.checkIn(layer); 498 } 499 500 return this; 501 }, 502 503 // Re-implement to redirect removeLayer to Layer Support group instead of map. 504 removeLayer: function (layer) { 505 506 var id = layer in this._layers ? layer : this.getLayerId(layer); 507 508 this._proxyMcgLayerSupportGroup.removeLayer(layer); 509 510 delete this._layers[id]; 511 512 return this; 513 }, 514 515 // Make sure it uses addLayers when added to map. 516 onAdd: function () { 517 this._proxyMcgLayerSupportGroup.addLayers(this.getLayers()); 518 }, 519 520 // Make sure it uses removeLayers when removed from map. 521 onRemove: function () { 522 this._proxyMcgLayerSupportGroup.removeLayers(this.getLayers()); 523 } 524 525}; 526 527/** 528 * Toolbox to equip the Map with a switch agent that redirects layers 529 * addition/removal to their Layer Support group when defined. 530 * @type {{addLayer: Function, removeLayer: Function}} 531 * @private 532 */ 533var _layerSwitchMap = { 534 535 addLayer: function (layer) { 536 if (layer._mcgLayerSupportGroup) { 537 // Use the original MCG addLayer. 538 return layer._mcgLayerSupportGroup._originalAddLayer(layer); 539 } 540 541 return this._originalAddLayer(layer); 542 }, 543 544 removeLayer: function (layer) { 545 if (layer._mcgLayerSupportGroup) { 546 // Use the original MCG removeLayer. 547 return layer._mcgLayerSupportGroup._originalRemoveLayer(layer); 548 } 549 550 return this._originalRemoveLayer(layer); 551 } 552 553}; 554 555// Supply with a factory for consistency with Leaflet. 556L.markerClusterGroup.layerSupport = function (options) { 557 return new L.MarkerClusterGroup.LayerSupport(options); 558};
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.