1gMarkers.buildMarker = ({ position, mapIconItem }) => { 2 //create and add associate with map 3 let marker = new google.maps.Marker({ 4 clickable: mapIconItem.icon.isClickable, 5 position: position, 6 title: mapIconItem.title, 7 zIndex: mapIconItem.zindex, 8 preventClustering: mapIconItem.icon.preventClustering 9 }); 10 11 return marker; 12} 13 14gMarkers.setIcon = (marker, icon) => { 15 marker.setIcon(icon); 16} 17 18gMarkers.getMapId = () => { 19 //a placeholder function. It is needed for new_gMarkers.js 20 return undefined; 21} 22 23gMarkers.getPosition = (marker) => { 24 return marker.getPosition(); 25} 26 27gMarkers.setMarker = (options) => { 28 let marker = new google.maps.Marker(options); 29 return marker; 30} 31 32gMarkers.getHeight = (marker) => { 33 return marker.get('anchorSize').height; 34} 35/** 36 * @name MarkerClusterer for Google Maps v3 37 * @version version 1.0 38 * @author Luke Mahe 39 * @fileoverview 40 * The library creates and manages per-zoom-level clusters for large amounts of 41 * markers. 42 * <br/> 43 * This is a v3 implementation of the 44 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/" 45 * >v2 MarkerClusterer</a>. 46 */ 47 48/** 49 * Licensed under the Apache License, Version 2.0 (the "License"); 50 * you may not use this file except in compliance with the License. 51 * You may obtain a copy of the License at 52 * 53 * http://www.apache.org/licenses/LICENSE-2.0 54 * 55 * Unless required by applicable law or agreed to in writing, software 56 * distributed under the License is distributed on an "AS IS" BASIS, 57 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 58 * See the License for the specific language governing permissions and 59 * limitations under the License. 60 */ 61 62 63/******* See line 171 and 990 for our code modification, plus line 1105 to stop click propagation *******/ 64 65 66/** 67 * A Marker Clusterer that clusters markers. 68 * 69 * @param {google.maps.Map} map The Google map to attach to. 70 * @param {Array.<google.maps.Marker>=} opt_markers Optional markers to add to 71 * the cluster. 72 * @param {Object=} opt_options support the following options: 73 * 'gridSize': (number) The grid size of a cluster in pixels. 74 * 'maxZoom': (number) The maximum zoom level that a marker can be part of a 75 * cluster. 76 * 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a 77 * cluster is to zoom into it. 78 * 'averageCenter': (boolean) Wether the center of each cluster should be 79 * the average of all markers in the cluster. 80 * 'minimumClusterSize': (number) The minimum number of markers to be in a 81 * cluster before the markers are hidden and a count 82 * is shown. 83 * 'styles': (object) An object that has style properties: 84 * 'url': (string) The image url. 85 * 'height': (number) The image height. 86 * 'width': (number) The image width. 87 * 'anchor': (Array) The anchor position of the label text. 88 * 'textColor': (string) The text color. 89 * 'textSize': (number) The text size. 90 * 'backgroundPosition': (string) The position of the backgound x, y. 91 * 'iconAnchor': (Array) The anchor position of the icon x, y. 92 * @constructor 93 * @extends google.maps.OverlayView 94 */ 95function MarkerClusterer(map, opt_markers, opt_options, oms) { 96 // MarkerClusterer implements google.maps.OverlayView interface. We use the 97 // extend function to extend MarkerClusterer with google.maps.OverlayView 98 // because it might not always be available when the code is defined so we 99 // look for it at the last possible moment. If it doesn't exist now then 100 // there is no point going ahead :) 101 this.extend(MarkerClusterer, google.maps.OverlayView); 102 this.map_ = map; 103 this.oms_ = oms; 104 105 /** 106 * @type {Array.<google.maps.Marker>} 107 * @private 108 */ 109 this.markers_ = []; 110 111 /** 112 * @type {Array.<Cluster>} 113 */
114 this.clusters_ = []; 115 116 this.sizes = [53, 56, 66, 78, 90]; 117 118 /** 119 * @private 120 */ 121 this.styles_ = []; 122 123 /** 124 * @type {boolean} 125 * @private 126 */ 127 this.ready_ = false; 128 129 var options = opt_options || {}; 130 131 /** 132 * @type {number} 133 * @private 134 */ 135 this.gridSize_ = options['gridSize'] || 60; 136 137 /** 138 * @private 139 */ 140 this.minClusterSize_ = options['minimumClusterSize'] || 2; 141 142 143 /** 144 * @type {?number} 145 * @private 146 */ 147 this.maxZoom_ = options['maxZoom'] || null; 148 149 this.styles_ = options['styles'] || []; 150 151 /** 152 * @type {string} 153 * @private 154 */ 155 this.imagePath_ = options['imagePath'] || 156 this.MARKER_CLUSTER_IMAGE_PATH_; 157 158 /** 159 * @type {string} 160 * @private 161 */ 162 this.imageExtension_ = options['imageExtension'] || 163 this.MARKER_CLUSTER_IMAGE_EXTENSION_; 164 165 /** 166 * @type {boolean} 167 * @private 168 */ 169 this.zoomOnClick_ = true; 170 171 if (options['zoomOnClick'] != undefined) { 172 this.zoomOnClick_ = options['zoomOnClick']; 173 } 174 175 /** 176 * @type {boolean} 177 * @private 178 */ 179 this.averageCenter_ = false; 180 181 if (options['averageCenter'] != undefined) { 182 this.averageCenter_ = options['averageCenter']; 183 } 184 185 this.setupStyles_(); 186 187 this.setMap(map); 188 189 /** 190 * @type {number} 191 * @private 192 */ 193 this.prevZoom_ = this.map_.getZoom(); 194 195 // Add the map event listeners 196 var that = this; 197 198 // MODIFIED: Track zoom changes for reference but don't update clusters during gesture 199 // Application layer (iconManager/appHelper) handles all cluster updates via idle event 200 // This prevents zoom reset issues during rapid pinch-zoom on mobile 201 google.maps.event.addListener(this.map_, 'zoom_changed', function () { 202 that.prevZoom_ = that.map_.getZoom(); 203 }); 204 205 // NOTE: idle listener removed - redundant with application layer handling 206 // iconManager.Redraw() in appHelper.js calls markerClusterer.repaint() at appropriate times 207 //google.maps.event.addListener(this.map_, 'idle', function () { 208 // that.redraw(); 209 //}); 210 211 // Finally, add the markers 212 if (opt_markers && opt_markers.length) { 213 this.addMarkers(opt_markers, false); 214 } 215} 216 217 218/** 219 * The marker cluster image path. 220 * 221 * @type {string} 222 * @private 223 */ 224MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = 225 'https://google-maps-utility-library-v3.googlecode.com/svn/trunk/markerclusterer/' + 226 'images/m'; 227 228 229/** 230 * The marker cluster image path. 231 * 232 * @type {string} 233 * @private 234 */ 235MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png'; 236 237 238/** 239 * Extends a objects prototype by anothers. 240 * 241 * @param {Object} obj1 The object to be extended. 242 * @param {Object} obj2 The object to extend with. 243 * @return {Object} The new extended object. 244 * @ignore 245 */ 246MarkerClusterer.prototype.extend = function (obj1, obj2) { 247 return (function (object) { 248 for (var property in object.prototype) { 249 this.prototype[property] = object.prototype[property]; 250 } 251 return this; 252 }).apply(obj1, [obj2]); 253}; 254 255 256/** 257 * Implementaion of the interface method. 258 * @ignore 259 */ 260MarkerClusterer.prototype.onAdd = function () { 261 this.setReady_(true); 262}; 263 264/** 265 * Implementaion of the interface method. 266 * @ignore 267 */ 268MarkerClusterer.prototype.draw = function () { }; 269 270/** 271 * Sets up the styles object. 272 * 273 * @private 274 */ 275MarkerClusterer.prototype.setupStyles_ = function () { 276 if (this.styles_.length) { 277 return; 278 } 279 280 for (var i = 0, size; size = this.sizes[i]; i++) { 281 this.styles_.push({ 282 url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_, 283 height: size, 284 width: size 285 }); 286 } 287}; 288 289/** 290 * Fit the map to the bounds of the markers in the clusterer. 291 */ 292MarkerClusterer.prototype.fitMapToMarkers = function () { 293 var markers = this.getMarkers(); 294 var bounds = new google.maps.LatLngBounds(); 295 for (var i = 0, marker; marker = markers[i]; i++) { 296 bounds.extend(marker.getPosition()); 297 } 298 299 this.map_.fitBounds(bounds); 300 this.map_.panBy(0, 0); 301}; 302 303 304/** 305 * Sets the styles. 306 * 307 * @param {Object} styles The style to set. 308 */ 309MarkerClusterer.prototype.setStyles = function (styles) { 310 this.styles_ = styles; 311}; 312 313 314/** 315 * Gets the styles. 316 * 317 * @return {Object} The styles object. 318 */ 319MarkerClusterer.prototype.getStyles = function () { 320 return this.styles_; 321}; 322 323 324/** 325 * Whether zoom on click is set. 326 * 327 * @return {boolean} True if zoomOnClick_ is set. 328 */ 329MarkerClusterer.prototype.isZoomOnClick = function () { 330 return this.zoomOnClick_; 331}; 332 333/** 334 * Whether average center is set. 335 * 336 * @return {boolean} True if averageCenter_ is set. 337 */ 338MarkerClusterer.prototype.isAverageCenter = function () { 339 return this.averageCenter_; 340}; 341 342 343/** 344 * Returns the array of markers in the clusterer. 345 * 346 * @return {Array.<google.maps.Marker>} The markers. 347 */ 348MarkerClusterer.prototype.getMarkers = function () { 349 return this.markers_; 350}; 351 352 353/** 354 * Returns the number of markers in the clusterer 355 * 356 * @return {Number} The number of markers. 357 */ 358MarkerClusterer.prototype.getTotalMarkers = function () { 359 return this.markers_.length; 360}; 361 362 363/** 364 * Sets the max zoom for the clusterer. 365 * 366 * @param {number} maxZoom The max zoom level. 367 */ 368MarkerClusterer.prototype.setMaxZoom = function (maxZoom) { 369 this.maxZoom_ = maxZoom; 370}; 371 372 373/** 374 * Gets the max zoom for the clusterer. 375 * 376 * @return {number} The max zoom level. 377 */ 378MarkerClusterer.prototype.getMaxZoom = function () { 379 return this.maxZoom_; 380}; 381 382 383/** 384 * The function for calculating the cluster icon image. 385 * 386 * @param {Array.<google.maps.Marker>} markers The markers in the clusterer. 387 * @param {number} numStyles The number of styles available. 388 * @return {Object} A object properties: 'text' (string) and 'index' (number). 389 * @private 390 */ 391MarkerClusterer.prototype.calculator_ = function (markers, numStyles) { 392 var index = 0; 393 var count = markers.length; 394 var dv = count; 395 while (dv !== 0) { 396 dv = parseInt(dv / 10, 10); 397 index++; 398 } 399 400 index = Math.min(index, numStyles); 401 return { 402 text: count, 403 index: index 404 }; 405};
406 407 408/** 409 * Set the calculator function. 410 * 411 * @param {function(Array, number)} calculator The function to set as the 412 * calculator. The function should return a object properties: 413 * 'text' (string) and 'index' (number). 414 * 415 */ 416MarkerClusterer.prototype.setCalculator = function (calculator) { 417 this.calculator_ = calculator; 418}; 419 420 421/** 422 * Get the calculator function. 423 * 424 * @return {function(Array, number)} the calculator function. 425 */ 426MarkerClusterer.prototype.getCalculator = function () { 427 return this.calculator_; 428}; 429 430 431/** 432 * Add an array of markers to the clusterer. 433 * 434 * @param {Array.<google.maps.Marker>} markers The markers to add. 435 * @param {boolean=} opt_nodraw Whether to redraw the clusters. 436 */ 437MarkerClusterer.prototype.addMarkers = function (markers, opt_nodraw) { 438 for (var i = 0, marker; marker = markers[i]; i++) { 439 if (!marker.preventClustering) { 440 //ToDo: get oms to work with new Google markers 441 this.oms_.addMarker(marker); 442 } 443 this.pushMarkerTo_(marker); 444 } 445 if (!opt_nodraw) { 446 this.redraw(); 447 } 448}; 449 450 451/** 452 * Pushes a marker to the clusterer. 453 * 454 * @param {google.maps.Marker} marker The marker to add. 455 * @private 456 */ 457MarkerClusterer.prototype.pushMarkerTo_ = function (marker) { 458 marker.isAdded = false; 459 if (marker['draggable']) { 460 // If the marker is draggable add a listener so we update the clusters on 461 // the drag end. 462 var that = this; 463 google.maps.event.addListener(marker, 'dragend', function () { 464 marker.isAdded = false; 465 that.repaint(); 466 }); 467 } 468 this.markers_.push(marker); 469}; 470 471 472/** 473 * Adds a marker to the clusterer and redraws if needed. 474 * 475 * @param {google.maps.Marker} marker The marker to add. 476 * @param {boolean=} opt_nodraw Whether to redraw the clusters. 477 */ 478MarkerClusterer.prototype.addMarker = function (marker, opt_nodraw) { 479 if (!marker.preventClustering) { 480 this.oms_.addMarker(marker); 481 } 482 this.pushMarkerTo_(marker); 483 if (!opt_nodraw) { 484 this.redraw(); 485 } 486}; 487 488 489/** 490 * Removes a marker and returns true if removed, false if not 491 * 492 * @param {google.maps.Marker} marker The marker to remove 493 * @return {boolean} Whether the marker was removed or not 494 * @private 495 */ 496MarkerClusterer.prototype.removeMarker_ = function (marker) { 497 var index = -1; 498 if (this.markers_.indexOf) { 499 index = this.markers_.indexOf(marker); 500 } else { 501 for (var i = 0, m; m = this.markers_[i]; i++) { 502 if (m == marker) { 503 index = i; 504 break; 505 } 506 } 507 } 508 509 if (index == -1) { 510 // Marker is not in our list of markers. 511 return false; 512 } 513 514 marker.setMap(null); 515 marker.clustered = true; 516 google.maps.event.trigger(marker, 'marker-clustered', true); 517 this.markers_.splice(index, 1); 518 519 return true; 520}; 521 522 523/** 524 * Remove a marker from the cluster. 525 * 526 * @param {google.maps.Marker} marker The marker to remove. 527 * @param {boolean=} opt_nodraw Optional boolean to force no redraw. 528 * @return {boolean} True if the marker was removed. 529 */ 530MarkerClusterer.prototype.removeMarker = function (marker, opt_nodraw) { 531 this.oms_.removeMarker(marker); 532 var removed = this.removeMarker_(marker); 533 534 if (!opt_nodraw && removed) { 535 this.resetViewport(); 536 this.redraw(); 537 return true; 538 } else { 539 return false; 540 } 541}; 542 543 544/** 545 * Removes an array of markers from the cluster. 546 * 547 * @param {Array.<google.maps.Marker>} markers The markers to remove. 548 * @param {boolean=} opt_nodraw Optional boolean to force no redraw. 549 */ 550MarkerClusterer.prototype.removeMarkers = function (markers, opt_nodraw) { 551 var removed = false; 552 553 for (var i = 0, marker; marker = markers[i]; i++) { 554 this.oms_.removeMarker(marker); 555 var r = this.removeMarker_(marker); 556 removed = removed || r; 557 } 558 559 if (!opt_nodraw && removed) { 560 this.resetViewport(); 561 this.redraw(); 562 return true; 563 } 564}; 565 566 567/** 568 * Sets the clusterer's ready state. 569 * 570 * @param {boolean} ready The state. 571 * @private 572 */ 573MarkerClusterer.prototype.setReady_ = function (ready) { 574 if (!this.ready_) { 575 this.ready_ = ready;
576 this.createClusters_(); 577 } 578}; 579 580 581/** 582 * Returns the number of clusters in the clusterer. 583 * 584 * @return {number} The number of clusters. 585 */ 586MarkerClusterer.prototype.getTotalClusters = function () { 587 return this.clusters_.length; 588}; 589 590 591/** 592 * Returns the google map that the clusterer is associated with. 593 * 594 * @return {google.maps.Map} The map. 595 */ 596MarkerClusterer.prototype.getMap = function () { 597 return this.map_; 598}; 599 600 601/** 602 * Sets the google map that the clusterer is associated with. 603 * 604 * @param {google.maps.Map} map The map. 605 */ 606MarkerClusterer.prototype.setMap = function (map) { 607 this.map_ = map; 608}; 609 610 611/** 612 * Returns the size of the grid. 613 * 614 * @return {number} The grid size. 615 */ 616MarkerClusterer.prototype.getGridSize = function () { 617 return this.gridSize_; 618}; 619 620 621/** 622 * Sets the size of the grid. 623 * 624 * @param {number} size The grid size. 625 */ 626MarkerClusterer.prototype.setGridSize = function (size) { 627 this.gridSize_ = size; 628}; 629 630 631/** 632 * Returns the min cluster size. 633 * 634 * @return {number} The grid size. 635 */ 636MarkerClusterer.prototype.getMinClusterSize = function () { 637 return this.minClusterSize_; 638}; 639 640/** 641 * Sets the min cluster size. 642 * 643 * @param {number} size The grid size. 644 */ 645MarkerClusterer.prototype.setMinClusterSize = function (size) { 646 this.minClusterSize_ = size; 647}; 648 649 650/** 651 * Extends a bounds object by the grid size. 652 * 653 * @param {google.maps.LatLngBounds} bounds The bounds to extend. 654 * @return {google.maps.LatLngBounds}
654 The extended bounds. 655 */ 656MarkerClusterer.prototype.getExtendedBounds = function (bounds) { 657 var projection = this.getProjection(); 658 659 // Turn the bounds into latlng. 660 var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), 661 bounds.getNorthEast().lng()); 662 var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), 663 bounds.getSouthWest().lng()); 664 665 // Convert the points to pixels and the extend out by the grid size. 666 var trPix = projection.fromLatLngToDivPixel(tr); 667 trPix.x += this.gridSize_; 668 trPix.y -= this.gridSize_; 669 670 var blPix = projection.fromLatLngToDivPixel(bl); 671 blPix.x -= this.gridSize_; 672 blPix.y += this.gridSize_; 673 674 // Convert the pixel points back to LatLng 675 var ne = projection.fromDivPixelToLatLng(trPix); 676 var sw = projection.fromDivPixelToLatLng(blPix); 677 678 // Extend the bounds to contain the new bounds. 679 bounds.extend(ne); 680 bounds.extend(sw); 681 682 return bounds; 683}; 684 685 686/** 687 * Determins if a marker is contained in a bounds. 688 * 689 * @param {google.maps.Marker} marker The marker to check. 690 * @param {google.maps.LatLngBounds} bounds The bounds to check against. 691 * @return {boolean} True if the marker is in the bounds. 692 * @private 693 */ 694MarkerClusterer.prototype.isMarkerInBounds_ = function (marker, bounds) { 695 if (marker.getPosition) return bounds.contains(marker.getPosition()); 696 else return false; 697}; 698 699 700/** 701 * Clears all clusters and markers from the clusterer. 702 */ 703MarkerClusterer.prototype.clearMarkers = function () { 704 this.resetViewport(true); 705 706 // Set the markers a empty array. 707 this.markers_ = []; 708}; 709 710 711/** 712 * Clears all existing clusters and recreates them. 713 * @param {boolean} opt_hide To also hide the marker. 714 */ 715MarkerClusterer.prototype.resetViewport = function (opt_hide) { 716 // Remove all the clusters 717 for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { 718 cluster.removeCluster(); 719 } 720 721 // Reset the markers to not be added and to be invisible. 722 for (var i = 0, marker; marker = this.markers_[i]; i++) { 723 marker.isAdded = false; 724 if (opt_hide) { 725 marker.setMap(null); 726 } 727 } 728 729 this.clusters_ = []; 730}; 731 732/** 733 * 734 */ 735MarkerClusterer.prototype.repaint = function () { 736 var oldClusters = this.clusters_.slice(); 737 this.clusters_.length = 0; 738 this.resetViewport(); 739 this.redraw(); 740 741 // Remove the old clusters. 742 // Do it in a timeout so the other clusters have been drawn first. 743 window.setTimeout(function () { 744 for (var i = 0, cluster; cluster = oldClusters[i]; i++) { 745 cluster.removeCluster(); 746 } 747 }, 0); 748}; 749 750 751/** 752 * Redraws the clusters. 753 */ 754MarkerClusterer.prototype.redraw = function () { 755 this.createClusters_(); 756}; 757 758 759/** 760 * Calculates the distance between two latlng locations in km. 761 * @see http://www.movable-type.co.uk/scripts/latlong.html 762 * 763 * @param {google.maps.LatLng} p1 The first lat lng point. 764 * @param {google.maps.LatLng} p2 The second lat lng point. 765 * @return {number} The distance between the two points in km. 766 * @private 767*/ 768MarkerClusterer.prototype.distanceBetweenPoints_ = function (p1, p2) { 769 if (!p1 || !p2) { 770 return 0; 771 } 772 773 var R = 6371; // Radius of the Earth in km 774 var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; 775 var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; 776 var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + 777 Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * 778 Math.sin(dLon / 2) * Math.sin(dLon / 2); 779 var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 780 var d = R * c; 781 return d; 782}; 783 784 785/** 786 * Add a marker to a cluster, or creates a new cluster. 787 * 788 * @param {google.maps.Marker} marker The marker to add. 789 * @private 790 */ 791MarkerClusterer.prototype.addToClosestCluster_ = function (marker) { 792 var distance = 40000; // Some large number 793 var clusterToAddTo = null; 794 var pos = marker.getPosition(); 795 for (var i = 0, cluster;
795 cluster = this.clusters_[i]; i++) { 796 var center = cluster.getCenter(); 797 if (center) { 798 var d = this.distanceBetweenPoints_(center, marker.getPosition()); 799 if (d < distance) { 800 distance = d; 801 clusterToAddTo = cluster; 802 } 803 } 804 } 805 806 if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { 807 clusterToAddTo.addMarker(marker); 808 } else { 809 var cluster = new Cluster(this); 810 cluster.addMarker(marker); 811 this.clusters_.push(cluster); 812 } 813}; 814 815 816/** 817 * Creates the clusters. 818 * 819 * @private 820 */ 821MarkerClusterer.prototype.createClusters_ = function () { 822 if (!this.ready_) { 823 return; 824 } 825 826 // Get our current map view bounds. 827 // Create a new bounds object so we don't affect the map. 828 var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), 829 this.map_.getBounds().getNorthEast()); 830 var bounds = this.getExtendedBounds(mapBounds); 831 832 for (var i = 0, marker; marker = this.markers_[i]; i++) { 833 if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { 834 this.addToClosestCluster_(marker); 835 } 836 } 837}; 838 839 840/** 841 * A cluster that contains markers. 842 * 843 * @param {MarkerClusterer} markerClusterer The markerclusterer that this 844 * cluster is associated with. 845 * @constructor 846 * @ignore 847 */ 848function Cluster(markerClusterer) { 849 this.markerClusterer_ = markerClusterer; 850 this.map_ = markerClusterer.getMap(); 851 this.gridSize_ = markerClusterer.getGridSize(); 852 this.minClusterSize_ = markerClusterer.getMinClusterSize(); 853 this.averageCenter_ = markerClusterer.isAverageCenter(); 854 this.center_ = null; 855 this.markers_ = []; 856 this.bounds_ = null; 857 this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(), 858 markerClusterer.getGridSize()); 859} 860 861/** 862 * Determins if a marker is already added to the cluster. 863 * 864 * @param {google.maps.Marker} marker The marker to check. 865 * @return {boolean} True if the marker is already added. 866 */ 867Cluster.prototype.isMarkerAlreadyAdded = function (marker) { 868 if (this.markers_.indexOf) { 869 return this.markers_.indexOf(marker) != -1; 870 } else { 871 for (var i = 0, m; m = this.markers_[i]; i++) { 872 if (m == marker) { 873 return true; 874 } 875 } 876 } 877 return false; 878}; 879 880 881/** 882 * Add a marker the cluster. 883 * 884 * @param {google.maps.Marker} marker The marker to add. 885 * @return {boolean} True if the marker was added. 886 */ 887Cluster.prototype.addMarker = function (marker) { 888 if (this.isMarkerAlreadyAdded(marker)) { 889 return false; 890 } 891 892 if (!this.center_) { 893 this.center_ = marker.getPosition(); 894 this.calculateBounds_(); 895 } else { 896 if (this.averageCenter_) { 897 var l = this.markers_.length + 1; 898 var lat = (this.center_.lat() * (l - 1) + marker.getPosition().lat()) / l; 899 var lng = (this.center_.lng() * (l - 1) + marker.getPosition().lng()) / l; 900 this.center_ = new google.maps.LatLng(lat, lng); 901 this.calculateBounds_(); 902 } 903 } 904 905 marker.isAdded = true; 906 if (!marker.preventClustering) { 907 this.markers_.push(marker); 908 } 909 910 var len = this.markers_.length; 911 if (len < this.minClusterSize_ || marker.preventClustering) { 912 // Min cluster size not reached so show the marker. 913 if (marker.getMap() != this.map_) { 914 marker.setMap(this.map_); 915 } 916 marker.clustered = false; 917 google.maps.event.trigger(marker, 'marker-clustered', false); 918 } 919 920 if (len == this.minClusterSize_) { 921 // Hide the markers that were showing. 922 for (var i = 0; i < len; i++) { 923 this.markers_[i].setMap(null); 924 this.markers_[i].clustered = true; 925 google.maps.event.trigger(this.markers_[i], 'marker-clustered', true); 926 } 927 } 928 929 if (len >= this.minClusterSize_ && !marker.preventClustering) { 930 marker.setMap(null); 931 marker.clustered = true; 932 google.maps.event.trigger(marker, 'marker-clustered', true); 933 } 934 935 this.updateIcon(); 936 return true; 937}; 938 939 940/** 941 * Returns the marker clusterer that the cluster is associated with. 942 * 943 * @return {MarkerClusterer} The associated marker clusterer. 944 */ 945Cluster.prototype.getMarkerClusterer = function () { 946 return this.markerClusterer_; 947}; 948 949 950/** 951 * Returns the bounds of the cluster. 952 * 953 * @return {google.maps.LatLngBounds} the cluster bounds. 954 */ 955Cluster.prototype.getBounds = function () { 956 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 957 var markers = this.getMarkers(); 958 for (var i = 0, marker; marker = markers[i]; i++) { 959 bounds.extend(marker.getPosition()); 960 } 961 return bounds; 962}; 963 964 965/** 966 * Removes the cluster 967 */ 968Cluster.prototype.removeCluster = function () { 969 this.clusterIcon_.removeClusterIcon(); 970 this.markers_.length = 0; 971 delete this.markers_; 972}; 973 974 975/** 976 * Returns the center of the cluster. 977 * 978 * @return {number} The cluster center.
979 */ 980Cluster.prototype.getSize = function () { 981 return this.markers_.length; 982}; 983 984 985/** 986 * Returns the center of the cluster. 987 * 988 * @return {Array.<google.maps.Marker>} The cluster center. 989 */ 990Cluster.prototype.getMarkers = function () { 991 return this.markers_; 992}; 993 994 995/** 996 * Returns the center of the cluster. 997 * 998 * @return {google.maps.LatLng} The cluster center. 999 */ 1000Cluster.prototype.getCenter = function () { 1001 return this.center_; 1002}; 1003 1004 1005/** 1006 * Calculated the extended bounds of the cluster with the grid. 1007 * 1008 * @private 1009 */ 1010Cluster.prototype.calculateBounds_ = function () { 1011 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 1012 this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); 1013}; 1014 1015 1016/** 1017 * Determines if a marker lies in the clusters bounds. 1018 * 1019 * @param {google.maps.Marker} marker The marker to check. 1020 * @return {boolean} True if the marker lies in the bounds. 1021 */ 1022Cluster.prototype.isMarkerInClusterBounds = function (marker) { 1023 return this.bounds_.contains(marker.getPosition()); 1024}; 1025 1026 1027/** 1028 * Returns the map that the cluster is associated with. 1029 * 1030 * @return {google.maps.Map} The map. 1031 */ 1032Cluster.prototype.getMap = function () { 1033 return this.map_; 1034}; 1035 1036 1037/** 1038 * Updates the cluster icon 1039 */ 1040Cluster.prototype.updateIcon = function () { 1041 var zoom = this.map_.getZoom(); 1042 var mz = this.markerClusterer_.getMaxZoom(); 1043 1044 if (mz && zoom > mz) { 1045 // The zoom is greater than our max zoom so show all the markers in cluster. 1046 for (var i = 0, marker; marker = this.markers_[i]; i++) { 1047 //check to see if the map is already set so we don't flicker the icon 1048 if (marker.map !== this.map_) { 1049 marker.setMap(this.map_); 1050 marker.clustered = false; 1051 google.maps.event.trigger(marker, 'marker-clustered', false); 1052 } 1053 } 1054 return; 1055 } 1056 1057 if (this.markers_.length < this.minClusterSize_) { 1058 // Min cluster size not yet reached. 1059 this.clusterIcon_.hide(); 1060 return; 1061 } 1062 1063 var numStyles = this.markerClusterer_.getStyles().length; 1064 var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); 1065 this.clusterIcon_.setCenter(this.center_); 1066 this.clusterIcon_.setSums(sums); 1067 this.clusterIcon_.show(); 1068}; 1069 1070 1071/** 1072 * A cluster icon 1073 * 1074 * @param {Cluster} cluster The cluster to be associated with. 1075 * @param {Object} styles An object that has style properties: 1076 * 'url': (string) The image url. 1077 * 'height': (number) The image height. 1078 * 'width': (number) The image width. 1079 * 'anchor': (Array) The anchor position of the label text. 1080 * 'textColor': (string) The text color. 1081 * 'textSize': (number) The text size. 1082 * 'backgroundPosition: (string) The background postition x, y. 1083 * @param {number=} opt_padding Optional padding to apply to the cluster icon. 1084 * @constructor 1085 * @extends google.maps.OverlayView 1086 * @ignore 1087 */ 1088function ClusterIcon(cluster, styles, opt_padding) { 1089 cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); 1090 1091 this.styles_ = styles; 1092 this.padding_ = opt_padding || 0; 1093 this.cluster_ = cluster; 1094 this.center_ = null; 1095 this.map_ = cluster.getMap(); 1096 this.div_ = null; 1097 this.sums_ = null; 1098 this.visible_ = false; 1099 1100 this.setMap(this.map_); 1101} 1102 1103 1104/** 1105 * Triggers the clusterclick event and zoom's if the option is set. 1106 * 1107 * @param {google.maps.MouseEvent} event The event to propagate 1108 */ 1109ClusterIcon.prototype.triggerClusterClick = function (event) { 1110 var markerClusterer = this.cluster_.getMarkerClusterer(); 1111 1112 // Trigger the clusterclick event. 1113 google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_, event); 1114 1115 if (markerClusterer.isZoomOnClick()) { 1116 // Zoom into the cluster. 1117 this.map_.fitBounds(this.cluster_.getBounds()); 1118 this.map_.panBy(0, 0); 1119 } 1120}; 1121 1122 1123/** 1124 * Adding the cluster icon to the dom. 1125 * @ignore 1126 */ 1127ClusterIcon.prototype.onAdd = function () { 1128 this.div_ = document.createElement('DIV'); 1129 if (this.visible_) { 1130 var pos = this.getPosFromLatLng_(this.center_); 1131 this.div_.style.cssText = this.createCss(pos); 1132 this.div_.innerHTML = this.sums_.text; 1133 } 1134 1135 var panes = this.getPanes(); 1136 panes.overlayMouseTarget.appendChild(this.div_); 1137 1138 var that = this; 1139 this.div_.addEventListener('click', function (event) { 1140 event.stopPropagation(); //to avoid click conflict with KML layer (County Boundary) 1141 that.triggerClusterClick(event); 1142 });
1143}; 1144 1145 1146/** 1147 * Returns the position to place the div dending on the latlng. 1148 * 1149 * @param {google.maps.LatLng} latlng The position in latlng. 1150 * @return {google.maps.Point} The position in pixels. 1151 * @private 1152 */ 1153ClusterIcon.prototype.getPosFromLatLng_ = function (latlng) { 1154 var pos = this.getProjection().fromLatLngToDivPixel(latlng); 1155 1156 if (typeof this.iconAnchor_ === 'object' && this.iconAnchor_.length === 2) { 1157 pos.x -= this.iconAnchor_[0]; 1158 pos.y -= this.iconAnchor_[1]; 1159 } else { 1160 pos.x -= parseInt(this.width_ / 2, 10); 1161 pos.y -= parseInt(this.height_ / 2, 10); 1162 } 1163 return pos; 1164}; 1165 1166 1167/** 1168 * Draw the icon. 1169 * @ignore 1170 */ 1171ClusterIcon.prototype.draw = function () { 1172 if (this.visible_) { 1173 var pos = this.getPosFromLatLng_(this.center_); 1174 this.div_.style.top = pos.y + 'px'; 1175 this.div_.style.left = pos.x + 'px'; 1176 } 1177}; 1178 1179 1180/** 1181 * Hide the icon. 1182 */ 1183ClusterIcon.prototype.hide = function () { 1184 if (this.div_) { 1185 this.div_.style.display = 'none'; 1186 } 1187 this.visible_ = false; 1188}; 1189 1190 1191/** 1192 * Position and show the icon. 1193 */ 1194ClusterIcon.prototype.show = function () { 1195 if (this.div_) { 1196 var pos = this.getPosFromLatLng_(this.center_); 1197 this.div_.style.cssText = this.createCss(pos); 1198 this.div_.style.display = ''; 1199 } 1200 this.visible_ = true; 1201}; 1202 1203 1204/** 1205 * Remove the icon from the map 1206 */ 1207ClusterIcon.prototype.removeClusterIcon = function () { 1208 this.setMap(null); 1209}; 1210 1211 1212/** 1213 * Implementation of the onRemove interface. 1214 * @ignore 1215 */ 1216ClusterIcon.prototype.onRemove = function () { 1217 if (this.div_ && this.div_.parentNode) { 1218 this.hide(); 1219 this.div_.parentNode.removeChild(this.div_); 1220 this.div_ = null; 1221 } 1222}; 1223 1224 1225/** 1226 * Set the sums of the icon. 1227 * 1228 * @param {Object} sums The sums containing: 1229 * 'text': (string) The text to display in the icon. 1230 * 'index': (number) The style index of the icon. 1231 */ 1232ClusterIcon.prototype.setSums = function (sums) { 1233 this.sums_ = sums; 1234 this.text_ = sums.text; 1235 this.index_ = sums.index; 1236 if (this.div_) { 1237 this.div_.innerHTML = sums.text; 1238 } 1239 1240 this.useStyle(); 1241}; 1242 1243 1244/** 1245 * Sets the icon to the the styles. 1246 */ 1247ClusterIcon.prototype.useStyle = function () { 1248 var index = Math.max(0, this.sums_.index - 1); 1249 index = Math.min(this.styles_.length - 1, index); 1250 var style = this.styles_[index]; 1251 this.url_ = style['url']; 1252 this.height_ = style['height']; 1253 this.width_ = style['width']; 1254 this.textColor_ = style['textColor']; 1255 this.anchor_ = style['anchor']; 1256 this.textSize_ = style['textSize']; 1257 this.backgroundPosition_ = style['backgroundPosition']; 1258 this.iconAnchor_ = style['iconAnchor']; 1259}; 1260 1261 1262/** 1263 * Sets the center of the icon. 1264 * 1265 * @param {google.maps.LatLng} center The latlng to set as the center. 1266 */ 1267ClusterIcon.prototype.setCenter = function (center) { 1268 this.center_ = center; 1269}; 1270 1271 1272/** 1273 * Create the css text based on the position of the icon. 1274 * 1275 * @param {google.maps.Point} pos The position. 1276 * @return {string} The css style text. 1277 */ 1278ClusterIcon.prototype.createCss = function (pos) { 1279 var style = []; 1280 style.push('background-image:url(' + this.url_ + ');'); 1281 var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0'; 1282 style.push('background-position:' + backgroundPosition + ';'); 1283 1284 if (typeof this.anchor_ === 'object') { 1285 if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && 1286 this.anchor_[0] < this.height_) { 1287 style.push('height:' + (this.height_ - this.anchor_[0]) + 1288 'px; padding-top:' + this.anchor_[0] + 'px;'); 1289 } else if (typeof this.anchor_[0] === 'number' && this.anchor_[0] < 0 && 1290 -this.anchor_[0] < this.height_) { 1291 style.push('height:' + this.height_ + 'px; line-height:' + (this.height_ + this.anchor_[0]) + 1292 'px;'); 1293 } else { 1294 style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + 1295 'px;'); 1296 } 1297 if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && 1298 this.anchor_[1] < this.width_) { 1299 style.push('width:' + (this.width_ - this.anchor_[1]) + 1300 'px; padding-left:' + this.anchor_[1] + 'px;'); 1301 } else { 1302 style.push('width:' + this.width_ + 'px; text-align:center;'); 1303 } 1304 } else { 1305 style.push('height:' + this.height_ + 'px; line-height:' + 1306 this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); 1307 } 1308 1309 var txtColor = this.textColor_ ? this.textColor_ : 'black'; 1310 var txtSize = this.textSize_ ? this.textSize_ : 11; 1311 1312 style.push('cursor:pointer; top:' + pos.y + 'px; left:' + 1313 pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' + 1314 txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold'); 1315 return style.join(''); 1316};
vendor: 28,321 bytes, lines 1317-2002
1317/* 1318 OverlappingMarkerSpiderfier 1319https://github.com/jawj/OverlappingMarkerSpiderfier 1320Copyright (c) 2011 - 2017 George MacKerron 1321Released under the MIT licence: http://opensource.org/licenses/mit-license 1322Note: The Google Maps API v3 must be included *before* this code 1323*/ 1324 1325(function () { 1326 var callbackName, callbackRegEx, ref, ref1, scriptTag, tag, 1327 hasProp = {}.hasOwnProperty, 1328 slice = [].slice; 1329 1330 this['OverlappingMarkerSpiderfier'] = (function () { 1331 var ge, gm, j, len, mt, p, ref, twoPi, x; 1332 1333 p = _Class.prototype; 1334 1335 ref = [_Class, p]; 1336 for (j = 0, len = ref.length; j < len; j++) { 1337 x = ref[j]; 1338 x['VERSION'] = '1.0.3'; 1339 } 1340 1341 twoPi = Math.PI * 2; 1342 1343 gm = ge = mt = null; 1344 1345 _Class['markerStatus'] = { 1346 'SPIDERFIED': 'SPIDERFIED', 1347 'SPIDERFIABLE': 'SPIDERFIABLE', 1348 'UNSPIDERFIABLE': 'UNSPIDERFIABLE', 1349 'UNSPIDERFIED': 'UNSPIDERFIED' 1350 }; 1351 1352 function _Class(map1, opts) { 1353 var k, lcH, lcU, v; 1354 this.map = map1; 1355 if (opts == null) { 1356 opts = {}; 1357 } 1358 if (this.constructor.hasInitialized == null) { 1359 this.constructor.hasInitialized = true; 1360 gm = google.maps; 1361 ge = gm.event; 1362 mt = gm.MapTypeId; 1363 p['keepSpiderfied'] = false; 1364 p['ignoreMapClick'] = false; 1365 p['markersWontHide'] = false; 1366 p['markersWontMove'] = false; 1367 p['basicFormatEvents'] = false; 1368 p['nearbyDistance'] = 20; 1369 p['circleSpiralSwitchover'] = 9; 1370 p['circleFootSeparation'] = 23; 1371 p['circleStartAngle'] = twoPi / 12; 1372 p['spiralFootSeparation'] = 26; 1373 p['spiralLengthStart'] = 11; 1374 p['spiralLengthFactor'] = 4; 1375 p['spiderfiedZIndex'] = gm.Marker.MAX_ZINDEX + 20000; 1376 p['highlightedLegZIndex'] = gm.Marker.MAX_ZINDEX + 10000; 1377 p['usualLegZIndex'] = gm.Marker.MAX_ZINDEX + 1; 1378 p['legWeight'] = 1.5; 1379 p['legColors'] = { 1380 'usual': {}, 1381 'highlighted': {} 1382 }; 1383 lcU = p['legColors']['usual']; 1384 lcH = p['legColors']['highlighted']; 1385 lcU[mt.HYBRID] = lcU[mt.SATELLITE] = '#fff'; 1386 lcH[mt.HYBRID] = lcH[mt.SATELLITE] = '#f00'; 1387 lcU[mt.TERRAIN] = lcU[mt.ROADMAP] = '#444'; 1388 lcH[mt.TERRAIN] = lcH[mt.ROADMAP] = '#f00'; 1389 this.constructor.ProjHelper = function (map) { 1390 return this.setMap(map); 1391 }; 1392 this.constructor.ProjHelper.prototype = new gm.OverlayView(); 1393 this.constructor.ProjHelper.prototype['draw'] = function () { }; 1394 } 1395 for (k in opts) { 1396 if (!hasProp.call(opts, k)) continue; 1397 v = opts[k]; 1398 this[k] = v; 1399 } 1400 this.projHelper = new this.constructor.ProjHelper(this.map); 1401 this.initMarkerArrays(); 1402 this.listeners = {}; 1403 this.formatIdleListener = this.formatTimeoutId = null; 1404 this.addListener('click', function (marker, e) { 1405 return ge.trigger(marker, 'spider_click', e); 1406 }); 1407 this.addListener('format', function (marker, status) { 1408 return ge.trigger(marker, 'spider_format', status); 1409 }); 1410 if (!this['ignoreMapClick']) { 1411 ge.addListener(this.map, 'click', (function (_this) { 1412 return function () { 1413 return _this['unspiderfy'](); 1414 }; 1415 })(this)); 1416 } 1417 ge.addListener(this.map, 'maptypeid_changed', (function (_this) { 1418 return function () { 1419 return _this['unspiderfy'](); 1420 }; 1421 })(this)); 1422 ge.addListener(this.map, 'zoom_changed', (function (_this) { 1423 return function () { 1424 _this['unspiderfy'](); 1425 if (!_this['basicFormatEvents']) { 1426 return _this.formatMarkers(); 1427 } 1428 }; 1429 })(this)); 1430 } 1431 1432 p.initMarkerArrays = function () { 1433 this.markers = []; 1434 return this.markerListenerRefs = []; 1435 }; 1436 1437 p['addMarker'] = function (marker, spiderClickHandler) { 1438 marker.setMap(this.map); 1439 return this['trackMarker'](marker, spiderClickHandler); 1440 }; 1441 1442 p['trackMarker'] = function (marker, spiderClickHandler) { 1443 var listenerRefs; 1444 if (marker['_oms'] != null) { 1445 return this; 1446 } 1447 marker['_oms'] = true; 1448 listenerRefs = [ 1449 ge.addListener(marker, 'click', (function (_this) { 1450 return function (e) { 1451 return _this.spiderListener(marker, e); 1452 }; 1453 })(this)) 1454 ]; 1455 if (!this['markersWontHide']) { 1456 listenerRefs.push(ge.addListener(marker, 'visible_changed', (function (_this) { 1457 return function () { 1458 return _this.markerChangeListener(marker, false); 1459 }; 1460 })(this))); 1461 } 1462 if (!this['markersWontMove']) { 1463 listenerRefs.push(ge.addListener(marker, 'position_changed', (function (_this) { 1464 return function () { 1465 return _this.markerChangeListener(marker, true); 1466 }; 1467 })(this))); 1468 } 1469 if (spiderClickHandler != null) { 1470 listenerRefs.push(ge.addListener(marker, 'spider_click', spiderClickHandler)); 1471 } 1472 this.markerListenerRefs.push(listenerRefs); 1473 this.markers.push(marker); 1474 if (this['basicFormatEvents']) { 1475 this.trigger('format', marker, this.constructor['markerStatus']['UNSPIDERFIED']); 1476 } else { 1477 this.trigger('format', marker, this.constructor['markerStatus']['UNSPIDERFIABLE']); 1478 this.formatMarkers(); 1479 } 1480 return this; 1481 }; 1482 1483 p.markerChangeListener = function (marker, positionChanged) { 1484 if (this.spiderfying || this.unspiderfying) { 1485 return; 1486 } 1487 if ((marker['_omsData'] != null) && (positionChanged || !marker.getVisible())) { 1488 this['unspiderfy'](positionChanged ? marker : null); 1489 } 1490 return this.formatMarkers(); 1491 }; 1492 1493 p['getMarkers'] = function () { 1494 return this.markers.slice(0); 1495 }; 1496 1497 p['removeMarker'] = function (marker) { 1498 this['forgetMarker'](marker); 1499 return marker.setMap(null); 1500 }; 1501 1502 p['forgetMarker'] = function (marker) { 1503 var i, l, len1, listenerRef, listenerRefs; 1504 if (marker['_omsData'] != null) { 1505 this['unspiderfy'](); 1506 } 1507 i = this.arrIndexOf(this.markers, marker); 1508 if (i < 0) { 1509 return this; 1510 } 1511 listenerRefs = this.markerListenerRefs.splice(i, 1)[0]; 1512 for (l = 0, len1 = listenerRefs.length; l < len1; l++) { 1513 listenerRef = listenerRefs[l]; 1514 ge.removeListener(listenerRef); 1515 } 1516 delete marker['_oms']; 1517 this.markers.splice(i, 1); 1518 this.formatMarkers(); 1519 return this; 1520 }; 1521 1522 p['removeAllMarkers'] = p['clearMarkers'] = function () { 1523 var l, len1, marker, markers; 1524 markers = this['getMarkers'](); 1525 this['forgetAllMarkers'](); 1526 for (l = 0, len1 = markers.length; l < len1; l++) { 1527 marker = markers[l]; 1528 marker.setMap(null); 1529 } 1530 return this; 1531 }; 1532 1533 p['forgetAllMarkers'] = function () { 1534 var i, l, len1, len2, listenerRef, listenerRefs, marker, n, ref1; 1535 this['unspiderfy'](); 1536 ref1 = this.markers; 1537 for (i = l = 0, len1 = ref1.length; l < len1; i = ++l) { 1538 marker = ref1[i]; 1539 listenerRefs = this.markerListenerRefs[i]; 1540 for (n = 0, len2 = listenerRefs.length; n < len2; n++) { 1541 listenerRef = listenerRefs[n]; 1542 ge.removeListener(listenerRef); 1543 } 1544 delete marker['_oms']; 1545 } 1546 this.initMarkerArrays(); 1547 return this; 1548 }; 1549 1550 p['addListener'] = function (eventName, func) { 1551 var base; 1552 ((base = this.listeners)[eventName] != null ? base[eventName] : base[eventName] = []).push(func); 1553 return this; 1554 }; 1555 1556 p['removeListener'] = function (eventName, func) { 1557 var i; 1558 i = this.arrIndexOf(this.listeners[eventName], func); 1559 if (!(i < 0)) { 1560 this.listeners[eventName].splice(i, 1); 1561 } 1562 return this; 1563 }; 1564 1565 p['clearListeners'] = function (eventName) { 1566 this.listeners[eventName] = []; 1567 return this; 1568 }; 1569 1570 p.trigger = function () { 1571 var args, eventName, func, l, len1, ref1, ref2, results; 1572 eventName = arguments[0], args = 2 <= arguments.length ? slice.call(arguments, 1) : []; 1573 ref2 = (ref1 = this.listeners[eventName]) != null ? ref1 : []; 1574 results = []; 1575 for (l = 0, len1 = ref2.length; l < len1; l++) { 1576 func = ref2[l]; 1577 results.push(func.apply(null, args)); 1578 } 1579 return results; 1580 }; 1581 1582 p.generatePtsCircle = function (count, centerPt) { 1583 var angle, angleStep, circumference, i, l, legLength, ref1, results; 1584 circumference = this['circleFootSeparation'] * (2 + count); 1585 legLength = circumference / twoPi; 1586 angleStep = twoPi / count; 1587 results = []; 1588 for (i = l = 0, ref1 = count; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) { 1589 angle = this['circleStartAngle'] + i * angleStep; 1590 results.push(new gm.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle))); 1591 } 1592 return results; 1593 }; 1594 1595 p.generatePtsSpiral = function (count, centerPt) { 1596 var angle, i, l, legLength, pt, ref1, results; 1597 legLength = this['spiralLengthStart']; 1598 angle = 0; 1599 results = []; 1600 for (i = l = 0, ref1 = count; 0 <= ref1 ? l < ref1 : l > ref1; i = 0 <= ref1 ? ++l : --l) { 1601 angle += this['spiralFootSeparation'] / legLength + i * 0.0005; 1602 pt = new gm.Point(centerPt.x + legLength * Math.cos(angle), centerPt.y + legLength * Math.sin(angle)); 1603 legLength += twoPi * this['spiralLengthFactor'] / angle; 1604 results.push(pt); 1605 } 1606 return results; 1607 }; 1608 1609 p.spiderListener = function (marker, e) { 1610 var l, len1, m, mPt, markerPt, markerSpiderfied, nDist, nearbyMarkerData, nonNearbyMarkers, pxSq, ref1; 1611 markerSpiderfied = marker['_omsData'] != null; 1612 if (!(markerSpiderfied && this['keepSpiderfied'])) { 1613 this['unspiderfy'](); 1614 } 1615 if (markerSpiderfied || this.map.getStreetView().getVisible() || this.map.getMapTypeId() === 'GoogleEarthAPI') { 1616 return this.trigger('click', marker, e); 1617 } else { 1618 nearbyMarkerData = []; 1619 nonNearbyMarkers = []; 1620 nDist = this['nearbyDistance']; 1621 pxSq = nDist * nDist; 1622 markerPt = this.llToPt(marker.position); 1623 ref1 = this.markers; 1624 for (l = 0, len1 = ref1.length; l < len1; l++) { 1625 m = ref1[l]; 1626 if (!((m.map != null) && m.getVisible())) { 1627 continue; 1628 } 1629 mPt = this.llToPt(m.position); 1630 if (this.ptDistanceSq(mPt, markerPt) < pxSq) { 1631 nearbyMarkerData.push({ 1632 marker: m, 1633 markerPt: mPt 1634 }); 1635 } else { 1636 nonNearbyMarkers.push(m); 1637 } 1638 } 1639 if (nearbyMarkerData.length === 1) { 1640 return this.trigger('click', marker, e); 1641 } else { 1642 return this.spiderfy(nearbyMarkerData, nonNearbyMarkers); 1643 } 1644 } 1645 }; 1646 1647 p['markersNearMarker'] = function (marker, firstOnly) { 1648 var l, len1, m, mPt, markerPt, markers, nDist, pxSq, ref1, ref2, ref3; 1649 if (firstOnly == null) { 1650 firstOnly = false; 1651 } 1652 if (this.projHelper.getProjection() == null) { 1653 throw "Must wait for 'idle' event on map before calling markersNearMarker"; 1654 } 1655 nDist = this['nearbyDistance']; 1656 pxSq = nDist * nDist; 1657 markerPt = this.llToPt(marker.position); 1658 markers = []; 1659 ref1 = this.markers; 1660 for (l = 0, len1 = ref1.length; l < len1; l++) { 1661 m = ref1[l]; 1662 if (m === marker || (m.map == null) || !m.getVisible()) { 1663 continue; 1664 } 1665 mPt = this.llToPt((ref2 = (ref3 = m['_omsData']) != null ? ref3.usualPosition : void 0) != null ? ref2 : m.position); 1666 if (this.ptDistanceSq(mPt, markerPt) < pxSq) { 1667 markers.push(m); 1668 if (firstOnly) { 1669 break; 1670 } 1671 } 1672 } 1673 return markers; 1674 }; 1675 1676 p.markerProximityData = function () { 1677 var i1, i2, l, len1, len2, m, m1, m1Data, m2, m2Data, mData, n, nDist, pxSq, ref1, ref2; 1678 if (this.projHelper.getProjection() == null) { 1679 throw "Must wait for 'idle' event on map before calling markersNearAnyOtherMarker"; 1680 } 1681 nDist = this['nearbyDistance']; 1682 pxSq = nDist * nDist; 1683 mData = (function () { 1684 var l, len1, ref1, ref2, ref3, results; 1685 ref1 = this.markers; 1686 results = []; 1687 for (l = 0, len1 = ref1.length; l < len1; l++) { 1688 m = ref1[l]; 1689 results.push({ 1690 pt: this.llToPt((ref2 = (ref3 = m['_omsData']) != null ? ref3.usualPosition : void 0) != null ? ref2 : m.position), 1691 willSpiderfy: false 1692 }); 1693 } 1694 return results; 1695 }).call(this); 1696 ref1 = this.markers; 1697 for (i1 = l = 0, len1 = ref1.length; l < len1; i1 = ++l) { 1698 m1 = ref1[i1]; 1699 if (!((m1.getMap() != null) && m1.getVisible())) { 1700 continue; 1701 } 1702 m1Data = mData[i1]; 1703 if (m1Data.willSpiderfy) { 1704 continue; 1705 } 1706 ref2 = this.markers; 1707 for (i2 = n = 0, len2 = ref2.length; n < len2; i2 = ++n) { 1708 m2 = ref2[i2]; 1709 if (i2 === i1) { 1710 continue; 1711 } 1712 if (!((m2.getMap() != null) && m2.getVisible())) { 1713 continue; 1714 } 1715 m2Data = mData[i2]; 1716 if (i2 < i1 && !m2Data.willSpiderfy) { 1717 continue; 1718 } 1719 if (this.ptDistanceSq(m1Data.pt, m2Data.pt) < pxSq) { 1720 m1Data.willSpiderfy = m2Data.willSpiderfy = true; 1721 break; 1722 } 1723 } 1724 } 1725 return mData; 1726 }; 1727 1728 p['markersNearAnyOtherMarker'] = function () { 1729 var i, l, len1, m, mData, ref1, results; 1730 mData = this.markerProximityData(); 1731 ref1 = this.markers; 1732 results = []; 1733 for (i = l = 0, len1 = ref1.length; l < len1; i = ++l) { 1734 m = ref1[i]; 1735 if (mData[i].willSpiderfy) { 1736 results.push(m); 1737 } 1738 } 1739 return results; 1740 }; 1741 1742 p.setImmediate = function (func) { 1743 return window.setTimeout(func, 0); 1744 }; 1745 1746 p.formatMarkers = function () { 1747 if (this['basicFormatEvents']) { 1748 return; 1749 } 1750 if (this.formatTimeoutId != null) { 1751 return; 1752 } 1753 return this.formatTimeoutId = this.setImmediate((function (_this) { 1754 return function () { 1755 _this.formatTimeoutId = null; 1756 if (_this.projHelper.getProjection() != null) { 1757 return _this._formatMarkers(); 1758 } else { 1759 if (_this.formatIdleListener != null) { 1760 return; 1761 } 1762 return _this.formatIdleListener = ge.addListenerOnce(_this.map, 'idle', function () { 1763 return _this._formatMarkers(); 1764 }); 1765 } 1766 }; 1767 })(this)); 1768 }; 1769 1770 p._formatMarkers = function () { 1771 var i, l, len1, len2, marker, n, proximities, ref1, results, results1, status; 1772 if (this['basicFormatEvents']) { 1773 results = []; 1774 for (l = 0, len1 = markers.length; l < len1; l++) { 1775 marker = markers[l]; 1776 status = marker['_omsData'] != null ? 'SPIDERFIED' : 'UNSPIDERFIED'; 1777 results.push(this.trigger('format', marker, this.constructor['markerStatus'][status])); 1778 } 1779 return results; 1780 } else { 1781 proximities = this.markerProximityData(); 1782 ref1 = this.markers; 1783 results1 = []; 1784 for (i = n = 0, len2 = ref1.length; n < len2; i = ++n) { 1785 marker = ref1[i]; 1786 status = marker['_omsData'] != null ? 'SPIDERFIED' : proximities[i].willSpiderfy ? 'SPIDERFIABLE' : 'UNSPIDERFIABLE'; 1787 results1.push(this.trigger('format', marker, this.constructor['markerStatus'][status])); 1788 } 1789 return results1; 1790 } 1791 }; 1792 1793 p.makeHighlightListenerFuncs = function (marker) { 1794 return { 1795 highlight: (function (_this) { 1796 return function () { 1797 return marker['_omsData'].leg.setOptions({ 1798 strokeColor: _this['legColors']['highlighted'][_this.map.mapTypeId], 1799 zIndex: _this['highlightedLegZIndex'] 1800 }); 1801 }; 1802 })(this), 1803 unhighlight: (function (_this) { 1804 return function () { 1805 return marker['_omsData'].leg.setOptions({ 1806 strokeColor: _this['legColors']['usual'][_this.map.mapTypeId], 1807 zIndex: _this['usualLegZIndex'] 1808 }); 1809 }; 1810 })(this) 1811 }; 1812 }; 1813 1814 p.spiderfy = function (markerData, nonNearbyMarkers) { 1815 var bodyPt, footLl, footPt, footPts, highlightListenerFuncs, leg, marker, md, nearestMarkerDatum, numFeet, spiderfiedMarkers; 1816 this.spiderfying = true; 1817 numFeet = markerData.length; 1818 bodyPt = this.ptAverage((function () { 1819 var l, len1, results; 1820 results = []; 1821 for (l = 0, len1 = markerData.length; l < len1; l++) { 1822 md = markerData[l]; 1823 results.push(md.markerPt); 1824 } 1825 return results; 1826 })()); 1827 footPts = numFeet >= this['circleSpiralSwitchover'] ? this.generatePtsSpiral(numFeet, bodyPt).reverse() : this.generatePtsCircle(numFeet, bodyPt); 1828 spiderfiedMarkers = (function () { 1829 var l, len1, results; 1830 results = []; 1831 for (l = 0, len1 = footPts.length; l < len1; l++) { 1832 footPt = footPts[l]; 1833 footLl = this.ptToLl(footPt); 1834 nearestMarkerDatum = this.minExtract(markerData, (function (_this) { 1835 return function (md) { 1836 return _this.ptDistanceSq(md.markerPt, footPt); 1837 }; 1838 })(this)); 1839 marker = nearestMarkerDatum.marker; 1840 leg = new gm.Polyline({ 1841 map: this.map, 1842 path: [marker.position, footLl], 1843 strokeColor: this['legColors']['usual'][this.map.mapTypeId], 1844 strokeWeight: this['legWeight'], 1845 zIndex: this['usualLegZIndex'] 1846 }); 1847 marker['_omsData'] = { 1848 usualPosition: marker.getPosition(), 1849 usualZIndex: marker.getZIndex(), 1850 leg: leg 1851 }; 1852 if (this['legColors']['highlighted'][this.map.mapTypeId] !== this['legColors']['usual'][this.map.mapTypeId]) { 1853 highlightListenerFuncs = this.makeHighlightListenerFuncs(marker); 1854 marker['_omsData'].hightlightListeners = { 1855 highlight: ge.addListener(marker, 'mouseover', highlightListenerFuncs.highlight), 1856 unhighlight: ge.addListener(marker, 'mouseout', highlightListenerFuncs.unhighlight) 1857 }; 1858 } 1859 this.trigger('format', marker, this.constructor['markerStatus']['SPIDERFIED']); 1860 marker.setPosition(footLl); 1861 marker.setZIndex(Math.round(this['spiderfiedZIndex'] + footPt.y)); 1862 results.push(marker); 1863 } 1864 return results; 1865 }).call(this); 1866 delete this.spiderfying; 1867 this.spiderfied = true; 1868 return this.trigger('spiderfy', spiderfiedMarkers, nonNearbyMarkers); 1869 }; 1870 1871 p['unspiderfy'] = function (markerNotToMove) { 1872 var l, len1, listeners, marker, nonNearbyMarkers, ref1, status, unspiderfiedMarkers; 1873 if (markerNotToMove == null) { 1874 markerNotToMove = null; 1875 } 1876 if (this.spiderfied == null) { 1877 return this; 1878 } 1879 this.unspiderfying = true; 1880 unspiderfiedMarkers = []; 1881 nonNearbyMarkers = []; 1882 ref1 = this.markers; 1883 for (l = 0, len1 = ref1.length; l < len1; l++) { 1884 marker = ref1[l]; 1885 if (marker['_omsData'] != null) { 1886 marker['_omsData'].leg.setMap(null); 1887 if (marker !== markerNotToMove) { 1888 marker.setPosition(marker['_omsData'].usualPosition); 1889 } 1890 marker.setZIndex(marker['_omsData'].usualZIndex); 1891 listeners = marker['_omsData'].hightlightListeners; 1892 if (listeners != null) { 1893 ge.removeListener(listeners.highlight); 1894 ge.removeListener(listeners.unhighlight); 1895 } 1896 delete marker['_omsData']; 1897 if (marker !== markerNotToMove) { 1898 status = this['basicFormatEvents'] ? 'UNSPIDERFIED' : 'SPIDERFIABLE'; 1899 this.trigger('format', marker, this.constructor['markerStatus'][status]); 1900 } 1901 unspiderfiedMarkers.push(marker); 1902 } else { 1903 nonNearbyMarkers.push(marker); 1904 } 1905 } 1906 delete this.unspiderfying; 1907 delete this.spiderfied; 1908 this.trigger('unspiderfy', unspiderfiedMarkers, nonNearbyMarkers); 1909 return this; 1910 }; 1911 1912 p.ptDistanceSq = function (pt1, pt2) { 1913 var dx, dy; 1914 dx = pt1.x - pt2.x; 1915 dy = pt1.y - pt2.y; 1916 return dx * dx + dy * dy; 1917 }; 1918 1919 p.ptAverage = function (pts) { 1920 var l, len1, numPts, pt, sumX, sumY; 1921 sumX = sumY = 0; 1922 for (l = 0, len1 = pts.length; l < len1; l++) { 1923 pt = pts[l]; 1924 sumX += pt.x; 1925 sumY += pt.y; 1926 } 1927 numPts = pts.length; 1928 return new gm.Point(sumX / numPts, sumY / numPts); 1929 }; 1930 1931 p.llToPt = function (ll) { 1932 return this.projHelper.getProjection().fromLatLngToDivPixel(ll); 1933 }; 1934 1935 p.ptToLl = function (pt) { 1936 return this.projHelper.getProjection().fromDivPixelToLatLng(pt); 1937 }; 1938 1939 p.minExtract = function (set, func) { 1940 var bestIndex, bestVal, index, item, l, len1, val; 1941 for (index = l = 0, len1 = set.length; l < len1; index = ++l) { 1942 item = set[index]; 1943 val = func(item); 1944 if ((typeof bestIndex === "undefined" || bestIndex === null) || val < bestVal) { 1945 bestVal = val; 1946 bestIndex = index; 1947 } 1948 } 1949 return set.splice(bestIndex, 1)[0]; 1950 }; 1951 1952 p.arrIndexOf = function (arr, obj) { 1953 var i, l, len1, o; 1954 if (arr.indexOf != null) { 1955 return arr.indexOf(obj); 1956 } 1957 for (i = l = 0, len1 = arr.length; l < len1; i = ++l) { 1958 o = arr[i]; 1959 if (o === obj) { 1960 return i; 1961 } 1962 } 1963 return -1; 1964 }; 1965 1966 return _Class; 1967 1968 })(); 1969 1970 callbackRegEx = /(\?.*(&|&)|\?)spiderfier_callback=(\w+)/; 1971 1972 scriptTag = document.currentScript; 1973 1974 if (scriptTag == null) { 1975 scriptTag = ((function () { 1976 var j, len, ref, ref1, results; 1977 ref = document.getElementsByTagName('script'); 1978 results = []; 1979 for (j = 0, len = ref.length; j < len; j++) { 1980 tag = ref[j]; 1981 if ((ref1 = tag.getAttribute('src')) != null ? ref1.match(callbackRegEx) : void 0) { 1982 results.push(tag); 1983 } 1984 } 1985 return results; 1986 })())[0]; 1987 } 1988 1989 if (scriptTag != null) { 1990 callbackName = (ref = scriptTag.getAttribute('src')) != null ? (ref1 = ref.match(callbackRegEx)) != null ? ref1[3] : void 0 : void 0; 1991 if (callbackName) { 1992 if (typeof window[callbackName] === "function") { 1993 window[callbackName](); 1994 } 1995 } 1996 } 1997 1998 if (typeof window['spiderfier_callback'] === "function") { 1999 window['spiderfier_callback'](); 2000 } 2001 2002}).call(this);
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.