vendor: 4,492 bytes, lines 1-152
1// ==ClosureCompiler== 2// @compilation_level ADVANCED_OPTIMIZATIONS 3// @externs_url http://closure-compiler.googlecode.com/svn/trunk/contrib/externs/maps/google_maps_api_v3_3.js 4// ==/ClosureCompiler== 5 6/** 7 * @name MarkerClusterer for Google Maps v3 8 * @version version 1.0 9 * @author Luke Mahe 10 * @fileoverview 11 * The library creates and manages per-zoom-level clusters for large amounts of 12 * markers. 13 * <br/> 14 * This is a v3 implementation of the 15 * <a href="http://gmaps-utility-library-dev.googlecode.com/svn/tags/markerclusterer/" 16 * >v2 MarkerClusterer</a>. 17 */ 18 19/** 20 * Licensed under the Apache License, Version 2.0 (the "License"); 21 * you may not use this file except in compliance with the License. 22 * You may obtain a copy of the License at 23 * 24 * http://www.apache.org/licenses/LICENSE-2.0 25 * 26 * Unless required by applicable law or agreed to in writing, software 27 * distributed under the License is distributed on an "AS IS" BASIS, 28 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 29 * See the License for the specific language governing permissions and 30 * limitations under the License. 31 */ 32 33 34/** 35 * A Marker Clusterer that clusters markers. 36 * 37 * @param {google.maps.Map} map The Google map to attach to. 38 * @param {Array.<google.maps.Marker>=} opt_markers Optional markers to add to 39 * the cluster. 40 * @param {Object=} opt_options support the following options: 41 * 'gridSize': (number) The grid size of a cluster in pixels. 42 * 'maxZoom': (number) The maximum zoom level that a marker can be part of a 43 * cluster. 44 * 'zoomOnClick': (boolean) Whether the default behaviour of clicking on a 45 * cluster is to zoom into it. 46 * 'averageCenter': (boolean) Wether the center of each cluster should be 47 * the average of all markers in the cluster. 48 * 'minimumClusterSize': (number) The minimum number of markers to be in a 49 * cluster before the markers are hidden and a count 50 * is shown. 51 * 'styles': (object) An object that has style properties: 52 * 'url': (string) The image url. 53 * 'height': (number) The image height. 54 * 'width': (number) The image width. 55 * 'anchor': (Array) The anchor position of the label text. 56 * 'textColor': (string) The text color. 57 * 'textSize': (number) The text size. 58 * 'backgroundPosition': (string) The position of the backgound x, y. 59 * @constructor 60 * @extends google.maps.OverlayView 61 */ 62function MarkerClusterer(map, opt_markers, opt_options) { 63 // MarkerClusterer implements google.maps.OverlayView interface. We use the 64 // extend function to extend MarkerClusterer with google.maps.OverlayView 65 // because it might not always be available when the code is defined so we 66 // look for it at the last possible moment. If it doesn't exist now then 67 // there is no point going ahead :) 68 this.extend(MarkerClusterer, google.maps.OverlayView); 69 this.map_ = map; 70 71 /** 72 * @type {Array.<google.maps.Marker>} 73 * @private 74 */ 75 this.markers_ = []; 76 77 /** 78 * @type {Array.<Cluster>} 79 */ 80 this.clusters_ = []; 81 82 this.sizes = [53, 56, 66, 78, 90]; 83 84 /** 85 * @private 86 */ 87 this.styles_ = []; 88 89 /** 90 * @type {boolean} 91 * @private 92 */ 93 this.ready_ = false; 94 95 var options = opt_options || {}; 96 97 /** 98 * @type {number} 99 * @private 100 */ 101 this.gridSize_ = options['gridSize'] || 60; 102 103 /** 104 * @private 105 */ 106 this.minClusterSize_ = options['minimumClusterSize'] || 2; 107 108 109 /** 110 * @type {?number} 111 * @private 112 */ 113 this.maxZoom_ = options['maxZoom'] || null; 114 115 this.styles_ = options['styles'] || []; 116 117 /** 118 * @type {string} 119 * @private 120 */ 121 this.imagePath_ = options['imagePath'] || 122 this.MARKER_CLUSTER_IMAGE_PATH_; 123 124 /** 125 * @type {string} 126 * @private 127 */ 128 this.imageExtension_ = options['imageExtension'] || 129 this.MARKER_CLUSTER_IMAGE_EXTENSION_; 130 131 /** 132 * @type {boolean} 133 * @private 134 */ 135 this.zoomOnClick_ = true; 136 137 if (options['zoomOnClick'] != undefined) { 138 this.zoomOnClick_ = options['zoomOnClick']; 139 } 140 141 /** 142 * @type {boolean} 143 * @private 144 */ 145 this.averageCenter_ = false; 146 147 if (options['averageCenter'] != undefined) { 148 this.averageCenter_ = options['averageCenter']; 149 } 150 151 this.setupStyles_(); 152
153 this.setMap(map); 154 155 /** 156 * @type {number} 157 * @private 158 */ 159 this.prevZoom_ = this.map_.getZoom(); 160 161 // Add the map event listeners 162 var that = this; 163 google.maps.event.addListener(this.map_, 'zoom_changed', function() { 164 var zoom = that.map_.getZoom(); 165 166 if (that.prevZoom_ != zoom) { 167 that.prevZoom_ = zoom; 168 that.resetViewport(); 169 } 170 }); 171 172 google.maps.event.addListener(this.map_, 'idle', function() { 173 that.redraw(); 174 }); 175 176 // Finally, add the markers 177 if (opt_markers && opt_markers.length) { 178 this.addMarkers(opt_markers, false); 179 } 180} 181 182 183/** 184 * The marker cluster image path. 185 * 186 * @type {string} 187 * @private 188 */ 189MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_PATH_ = 190 'http://wordpress-682175-4394224.cloudwaysapps.com/wp-content/plugins/congregation-search/' + 191 'images/m'; 192 193 194/** 195 * The marker cluster image path. 196 * 197 * @type {string} 198 * @private 199 */ 200MarkerClusterer.prototype.MARKER_CLUSTER_IMAGE_EXTENSION_ = 'png'; 201 202 203/** 204 * Extends a objects prototype by anothers. 205 * 206 * @param {Object} obj1 The object to be extended. 207 * @param {Object} obj2 The object to extend with. 208 * @return {Object} The new extended object. 209 * @ignore 210 */ 211MarkerClusterer.prototype.extend = function(obj1, obj2) { 212 return (function(object) { 213 for (var property in object.prototype) { 214 this.prototype[property] = object.prototype[property]; 215 } 216 return this; 217 }).apply(obj1, [obj2]); 218}; 219 220 221/** 222 * Implementaion of the interface method. 223 * @ignore 224 */ 225MarkerClusterer.prototype.onAdd = function() { 226 this.setReady_(true); 227}; 228 229/** 230 * Implementaion of the interface method. 231 * @ignore 232 */ 233MarkerClusterer.prototype.draw = function() {}; 234 235/** 236 * Sets up the styles object. 237 * 238 * @private 239 */ 240MarkerClusterer.prototype.setupStyles_ = function() { 241 if (this.styles_.length) { 242 return; 243 } 244 245 for (var i = 0, size; size = this.sizes[i]; i++) { 246 this.styles_.push({ 247 url: this.imagePath_ + (i + 1) + '.' + this.imageExtension_, 248 height: size, 249 width: size 250 }); 251 } 252}; 253 254/** 255 * Fit the map to the bounds of the markers in the clusterer. 256 */ 257MarkerClusterer.prototype.fitMapToMarkers = function() { 258 var markers = this.getMarkers(); 259 var bounds = new google.maps.LatLngBounds(); 260 for (var i = 0, marker; marker = markers[i]; i++) { 261 bounds.extend(marker.getPosition()); 262 } 263 264 this.map_.fitBounds(bounds); 265}; 266 267 268/** 269 * Sets the styles. 270 * 271 * @param {Object} styles The style to set. 272 */ 273MarkerClusterer.prototype.setStyles = function(styles) { 274 this.styles_ = styles; 275}; 276 277 278/** 279 * Gets the styles. 280 * 281 * @return {Object} The styles object. 282 */ 283MarkerClusterer.prototype.getStyles = function() { 284 return this.styles_; 285}; 286 287 288/** 289 * Whether zoom on click is set. 290 * 291 * @return {boolean} True if zoomOnClick_ is set. 292 */ 293MarkerClusterer.prototype.isZoomOnClick = function() { 294 return this.zoomOnClick_; 295}; 296 297/** 298 * Whether average center is set. 299 * 300 * @return {boolean} True if averageCenter_ is set. 301 */ 302MarkerClusterer.prototype.isAverageCenter = function() { 303 return this.averageCenter_; 304}; 305 306 307/** 308 * Returns the array of markers in the clusterer. 309 * 310 * @return {Array.<google.maps.Marker>} The markers. 311 */ 312MarkerClusterer.prototype.getMarkers = function() { 313 return this.markers_; 314}; 315 316 317/** 318 * Returns the number of markers in the clusterer 319 * 320 * @return {Number} The number of markers. 321 */ 322MarkerClusterer.prototype.getTotalMarkers = function() { 323 return this.markers_.length; 324}; 325 326 327/** 328 * Sets the max zoom for the clusterer. 329 * 330 * @param {number} maxZoom The max zoom level. 331 */ 332MarkerClusterer.prototype.setMaxZoom = function(maxZoom) { 333 this.maxZoom_ = maxZoom; 334}; 335 336 337/** 338 * Gets the max zoom for the clusterer. 339 * 340 * @return {number} The max zoom level. 341 */ 342MarkerClusterer.prototype.getMaxZoom = function() { 343 return this.maxZoom_; 344}; 345 346 347/** 348 * The function for calculating the cluster icon image. 349 * 350 * @param {Array.<google.maps.Marker>} markers The markers in the clusterer. 351 * @param {number} numStyles The number of styles available. 352 * @return {Object} A object properties: 'text' (string) and 'index' (number). 353 * @private 354 */ 355MarkerClusterer.prototype.calculator_ = function(markers, numStyles) { 356 var index = 0; 357 var count = markers.length; 358 var dv = count; 359 while (dv !== 0) { 360 dv = parseInt(dv / 10, 10); 361 index++; 362 } 363 364 index = Math.min(index, numStyles); 365 return { 366 text: count, 367 index: index 368 }; 369};
370 371 372/** 373 * Set the calculator function. 374 * 375 * @param {function(Array, number)} calculator The function to set as the 376 * calculator. The function should return a object properties: 377 * 'text' (string) and 'index' (number). 378 * 379 */ 380MarkerClusterer.prototype.setCalculator = function(calculator) { 381 this.calculator_ = calculator; 382}; 383 384 385/** 386 * Get the calculator function. 387 * 388 * @return {function(Array, number)} the calculator function. 389 */ 390MarkerClusterer.prototype.getCalculator = function() { 391 return this.calculator_; 392}; 393 394 395/** 396 * Add an array of markers to the clusterer. 397 * 398 * @param {Array.<google.maps.Marker>} markers The markers to add. 399 * @param {boolean=} opt_nodraw Whether to redraw the clusters. 400 */ 401MarkerClusterer.prototype.addMarkers = function(markers, opt_nodraw) { 402 for (var i = 0, marker; marker = markers[i]; i++) { 403 this.pushMarkerTo_(marker); 404 } 405 if (!opt_nodraw) { 406 this.redraw(); 407 } 408}; 409 410 411/** 412 * Pushes a marker to the clusterer. 413 * 414 * @param {google.maps.Marker} marker The marker to add. 415 * @private 416 */ 417MarkerClusterer.prototype.pushMarkerTo_ = function(marker) { 418 marker.isAdded = false; 419 if (marker['draggable']) { 420 // If the marker is draggable add a listener so we update the clusters on 421 // the drag end. 422 var that = this; 423 google.maps.event.addListener(marker, 'dragend', function() { 424 marker.isAdded = false; 425 that.repaint(); 426 }); 427 } 428 this.markers_.push(marker); 429}; 430 431 432/** 433 * Adds a marker to the clusterer and redraws if needed. 434 * 435 * @param {google.maps.Marker} marker The marker to add. 436 * @param {boolean=} opt_nodraw Whether to redraw the clusters. 437 */ 438MarkerClusterer.prototype.addMarker = function(marker, opt_nodraw) { 439 this.pushMarkerTo_(marker); 440 if (!opt_nodraw) { 441 this.redraw(); 442 } 443}; 444 445 446/** 447 * Removes a marker and returns true if removed, false if not 448 * 449 * @param {google.maps.Marker} marker The marker to remove 450 * @return {boolean} Whether the marker was removed or not 451 * @private 452 */ 453MarkerClusterer.prototype.removeMarker_ = function(marker) { 454 var index = -1; 455 if (this.markers_.indexOf) { 456 index = this.markers_.indexOf(marker); 457 } else { 458 for (var i = 0, m; m = this.markers_[i]; i++) { 459 if (m == marker) { 460 index = i; 461 break; 462 } 463 } 464 } 465 466 if (index == -1) { 467 // Marker is not in our list of markers. 468 return false; 469 } 470 471 marker.setMap(null); 472 473 this.markers_.splice(index, 1); 474 475 return true; 476}; 477 478 479/** 480 * Remove a marker from the cluster. 481 * 482 * @param {google.maps.Marker} marker The marker to remove. 483 * @param {boolean=} opt_nodraw Optional boolean to force no redraw. 484 * @return {boolean} True if the marker was removed. 485 */ 486MarkerClusterer.prototype.removeMarker = function(marker, opt_nodraw) { 487 var removed = this.removeMarker_(marker); 488 489 if (!opt_nodraw && removed) { 490 this.resetViewport(); 491 this.redraw(); 492 return true; 493 } else { 494 return false; 495 } 496}; 497 498 499/** 500 * Removes an array of markers from the cluster. 501 * 502 * @param {Array.<google.maps.Marker>} markers The markers to remove. 503 * @param {boolean=} opt_nodraw Optional boolean to force no redraw. 504 */ 505MarkerClusterer.prototype.removeMarkers = function(markers, opt_nodraw) { 506 var removed = false; 507 508 for (var i = 0, marker; marker = markers[i]; i++) { 509 var r = this.removeMarker_(marker); 510 removed = removed || r; 511 } 512 513 if (!opt_nodraw && removed) { 514 this.resetViewport(); 515 this.redraw(); 516 return true; 517 } 518}; 519 520 521/** 522 * Sets the clusterer's ready state. 523 * 524 * @param {boolean} ready The state. 525 * @private 526 */ 527MarkerClusterer.prototype.setReady_ = function(ready) { 528 if (!this.ready_) { 529 this.ready_ = ready; 530 this.createClusters_(); 531 } 532}; 533 534 535/** 536 * Returns the number of clusters in the clusterer. 537 * 538 * @return {number} The number of clusters. 539 */ 540MarkerClusterer.prototype.getTotalClusters = function() { 541 return this.clusters_.length; 542}; 543 544 545/** 546 * Returns the google map that the clusterer is associated with. 547 * 548 * @return {google.maps.Map} The map. 549 */ 550MarkerClusterer.prototype.getMap = function() { 551 return this.map_; 552}; 553 554 555/** 556 * Sets the google map that the clusterer is associated with. 557 * 558 * @param {google.maps.Map} map The map. 559 */ 560MarkerClusterer.prototype.setMap = function(map) { 561 this.map_ = map; 562}; 563 564 565/** 566 * Returns the size of the grid. 567 * 568 * @return {number} The grid size. 569 */ 570MarkerClusterer.prototype.getGridSize = function() { 571 return this.gridSize_; 572}; 573 574 575/** 576 * Sets the size of the grid. 577 * 578 * @param {number} size The grid size. 579 */ 580MarkerClusterer.prototype.setGridSize = function(size) { 581 this.gridSize_ = size; 582}; 583 584 585/** 586 * Returns the min cluster size. 587 * 588 * @return {number} The grid size. 589 */ 590MarkerClusterer.prototype.getMinClusterSize = function() { 591 return this.minClusterSize_; 592}; 593 594/** 595 * Sets the min cluster size. 596 * 597 * @param {number} size The grid size. 598 */ 599MarkerClusterer.prototype.setMinClusterSize = function(size) { 600 this.minClusterSize_ = size; 601}; 602 603 604/** 605 * Extends a bounds object by the grid size. 606 * 607 * @param {google.maps.LatLngBounds} bounds The bounds to extend. 608 * @return {google.maps.LatLngBounds}
608 The extended bounds. 609 */ 610MarkerClusterer.prototype.getExtendedBounds = function(bounds) { 611 var projection = this.getProjection(); 612 613 // Turn the bounds into latlng. 614 var tr = new google.maps.LatLng(bounds.getNorthEast().lat(), 615 bounds.getNorthEast().lng()); 616 var bl = new google.maps.LatLng(bounds.getSouthWest().lat(), 617 bounds.getSouthWest().lng()); 618 619 // Convert the points to pixels and the extend out by the grid size. 620 var trPix = projection.fromLatLngToDivPixel(tr); 621 trPix.x += this.gridSize_; 622 trPix.y -= this.gridSize_; 623 624 var blPix = projection.fromLatLngToDivPixel(bl); 625 blPix.x -= this.gridSize_; 626 blPix.y += this.gridSize_; 627 628 // Convert the pixel points back to LatLng 629 var ne = projection.fromDivPixelToLatLng(trPix); 630 var sw = projection.fromDivPixelToLatLng(blPix); 631 632 // Extend the bounds to contain the new bounds. 633 bounds.extend(ne); 634 bounds.extend(sw); 635 636 return bounds; 637}; 638 639 640/** 641 * Determins if a marker is contained in a bounds. 642 * 643 * @param {google.maps.Marker} marker The marker to check. 644 * @param {google.maps.LatLngBounds} bounds The bounds to check against. 645 * @return {boolean} True if the marker is in the bounds. 646 * @private 647 */ 648MarkerClusterer.prototype.isMarkerInBounds_ = function(marker, bounds) { 649 return bounds.contains(marker.getPosition()); 650}; 651 652 653/** 654 * Clears all clusters and markers from the clusterer. 655 */ 656MarkerClusterer.prototype.clearMarkers = function() { 657 this.resetViewport(true); 658 659 // Set the markers a empty array. 660 this.markers_ = []; 661}; 662 663 664/** 665 * Clears all existing clusters and recreates them. 666 * @param {boolean} opt_hide To also hide the marker. 667 */ 668MarkerClusterer.prototype.resetViewport = function(opt_hide) { 669 // Remove all the clusters 670 for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { 671 cluster.remove(); 672 } 673 674 // Reset the markers to not be added and to be invisible. 675 for (var i = 0, marker; marker = this.markers_[i]; i++) { 676 marker.isAdded = false; 677 if (opt_hide) { 678 marker.setMap(null); 679 } 680 } 681 682 this.clusters_ = []; 683}; 684 685/** 686 * 687 */ 688MarkerClusterer.prototype.repaint = function() { 689 var oldClusters = this.clusters_.slice(); 690 this.clusters_.length = 0; 691 this.resetViewport(); 692 this.redraw(); 693 694 // Remove the old clusters. 695 // Do it in a timeout so the other clusters have been drawn first. 696 window.setTimeout(function() { 697 for (var i = 0, cluster; cluster = oldClusters[i]; i++) { 698 cluster.remove(); 699 } 700 }, 0); 701}; 702 703 704/** 705 * Redraws the clusters. 706 */ 707MarkerClusterer.prototype.redraw = function() { 708 this.createClusters_(); 709}; 710 711 712/** 713 * Calculates the distance between two latlng locations in km. 714 * @see http://www.movable-type.co.uk/scripts/latlong.html 715 * 716 * @param {google.maps.LatLng} p1 The first lat lng point. 717 * @param {google.maps.LatLng} p2 The second lat lng point. 718 * @return {number} The distance between the two points in km. 719 * @private 720*/ 721MarkerClusterer.prototype.distanceBetweenPoints_ = function(p1, p2) { 722 if (!p1 || !p2) { 723 return 0; 724 } 725 726 var R = 6371; // Radius of the Earth in km 727 var dLat = (p2.lat() - p1.lat()) * Math.PI / 180; 728 var dLon = (p2.lng() - p1.lng()) * Math.PI / 180; 729 var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + 730 Math.cos(p1.lat() * Math.PI / 180) * Math.cos(p2.lat() * Math.PI / 180) * 731 Math.sin(dLon / 2) * Math.sin(dLon / 2); 732 var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); 733 var d = R * c; 734 return d; 735}; 736 737 738/** 739 * Add a marker to a cluster, or creates a new cluster. 740 * 741 * @param {google.maps.Marker} marker The marker to add. 742 * @private 743 */ 744MarkerClusterer.prototype.addToClosestCluster_ = function(marker) { 745 var distance = 40000; // Some large number 746 var clusterToAddTo = null; 747 var pos = marker.getPosition(); 748 for (var i = 0, cluster; cluster = this.clusters_[i]; i++) { 749 var center = cluster.getCenter(); 750 if (center) { 751 var d = this.distanceBetweenPoints_(center, marker.getPosition()); 752 if (d < distance) { 753 distance = d; 754 clusterToAddTo = cluster; 755 } 756 } 757 } 758 759 if (clusterToAddTo && clusterToAddTo.isMarkerInClusterBounds(marker)) { 760 clusterToAddTo.addMarker(marker); 761 } else { 762 var cluster = new Cluster(this); 763 cluster.addMarker(marker);
764 this.clusters_.push(cluster); 765 } 766}; 767 768 769/** 770 * Creates the clusters. 771 * 772 * @private 773 */ 774MarkerClusterer.prototype.createClusters_ = function() { 775 if (!this.ready_) { 776 return; 777 } 778 779 // Get our current map view bounds. 780 // Create a new bounds object so we don't affect the map. 781 var mapBounds = new google.maps.LatLngBounds(this.map_.getBounds().getSouthWest(), 782 this.map_.getBounds().getNorthEast()); 783 var bounds = this.getExtendedBounds(mapBounds); 784 785 for (var i = 0, marker; marker = this.markers_[i]; i++) { 786 if (!marker.isAdded && this.isMarkerInBounds_(marker, bounds)) { 787 this.addToClosestCluster_(marker); 788 } 789 } 790}; 791 792 793/** 794 * A cluster that contains markers. 795 * 796 * @param {MarkerClusterer} markerClusterer The markerclusterer that this 797 * cluster is associated with. 798 * @constructor 799 * @ignore 800 */ 801function Cluster(markerClusterer) { 802 this.markerClusterer_ = markerClusterer; 803 this.map_ = markerClusterer.getMap(); 804 this.gridSize_ = markerClusterer.getGridSize(); 805 this.minClusterSize_ = markerClusterer.getMinClusterSize(); 806 this.averageCenter_ = markerClusterer.isAverageCenter(); 807 this.center_ = null; 808 this.markers_ = []; 809 this.bounds_ = null; 810 this.clusterIcon_ = new ClusterIcon(this, markerClusterer.getStyles(), 811 markerClusterer.getGridSize()); 812} 813 814/** 815 * Determins if a marker is already added to the cluster. 816 * 817 * @param {google.maps.Marker} marker The marker to check. 818 * @return {boolean} True if the marker is already added. 819 */ 820Cluster.prototype.isMarkerAlreadyAdded = function(marker) { 821 if (this.markers_.indexOf) { 822 return this.markers_.indexOf(marker) != -1; 823 } else { 824 for (var i = 0, m; m = this.markers_[i]; i++) { 825 if (m == marker) { 826 return true; 827 } 828 } 829 } 830 return false; 831}; 832 833 834/** 835 * Add a marker the cluster. 836 * 837 * @param {google.maps.Marker} marker The marker to add. 838 * @return {boolean} True if the marker was added. 839 */ 840Cluster.prototype.addMarker = function(marker) { 841 if (this.isMarkerAlreadyAdded(marker)) { 842 return false; 843 } 844 845 if (!this.center_) { 846 this.center_ = marker.getPosition(); 847 this.calculateBounds_(); 848 } else { 849 if (this.averageCenter_) { 850 var l = this.markers_.length + 1; 851 var lat = (this.center_.lat() * (l-1) + marker.getPosition().lat()) / l; 852 var lng = (this.center_.lng() * (l-1) + marker.getPosition().lng()) / l; 853 this.center_ = new google.maps.LatLng(lat, lng); 854 this.calculateBounds_(); 855 } 856 } 857 858 marker.isAdded = true; 859 this.markers_.push(marker); 860 861 var len = this.markers_.length; 862 if (len < this.minClusterSize_ && marker.getMap() != this.map_) { 863 // Min cluster size not reached so show the marker. 864 marker.setMap(this.map_); 865 } 866 867 if (len == this.minClusterSize_) { 868 // Hide the markers that were showing. 869 for (var i = 0; i < len; i++) { 870 this.markers_[i].setMap(null); 871 } 872 } 873 874 if (len >= this.minClusterSize_) { 875 marker.setMap(null); 876 } 877 878 this.updateIcon(); 879 return true; 880}; 881 882 883/** 884 * Returns the marker clusterer that the cluster is associated with. 885 * 886 * @return {MarkerClusterer} The associated marker clusterer. 887 */ 888Cluster.prototype.getMarkerClusterer = function() { 889 return this.markerClusterer_; 890}; 891 892 893/** 894 * Returns the bounds of the cluster. 895 * 896 * @return {google.maps.LatLngBounds} the cluster bounds. 897 */ 898Cluster.prototype.getBounds = function() { 899 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 900 var markers = this.getMarkers(); 901 for (var i = 0, marker; marker = markers[i]; i++) { 902 bounds.extend(marker.getPosition()); 903 } 904 return bounds; 905}; 906 907 908/** 909 * Removes the cluster 910 */ 911Cluster.prototype.remove = function() { 912 this.clusterIcon_.remove(); 913 this.markers_.length = 0; 914 delete this.markers_; 915}; 916 917 918/** 919 * Returns the center of the cluster. 920 * 921 * @return {number} The cluster center. 922 */ 923Cluster.prototype.getSize = function() { 924 return this.markers_.length; 925}; 926 927 928/** 929 * Returns the center of the cluster. 930 * 931 * @return {Array.<google.maps.Marker>} The cluster center.
932 */ 933Cluster.prototype.getMarkers = function() { 934 return this.markers_; 935}; 936 937 938/** 939 * Returns the center of the cluster. 940 * 941 * @return {google.maps.LatLng} The cluster center. 942 */ 943Cluster.prototype.getCenter = function() { 944 return this.center_; 945}; 946 947 948/** 949 * Calculated the extended bounds of the cluster with the grid. 950 * 951 * @private 952 */ 953Cluster.prototype.calculateBounds_ = function() { 954 var bounds = new google.maps.LatLngBounds(this.center_, this.center_); 955 this.bounds_ = this.markerClusterer_.getExtendedBounds(bounds); 956}; 957 958 959/** 960 * Determines if a marker lies in the clusters bounds. 961 * 962 * @param {google.maps.Marker} marker The marker to check. 963 * @return {boolean} True if the marker lies in the bounds. 964 */ 965Cluster.prototype.isMarkerInClusterBounds = function(marker) { 966 return this.bounds_.contains(marker.getPosition()); 967}; 968 969 970/** 971 * Returns the map that the cluster is associated with. 972 * 973 * @return {google.maps.Map} The map. 974 */ 975Cluster.prototype.getMap = function() { 976 return this.map_; 977}; 978 979 980/** 981 * Updates the cluster icon 982 */ 983Cluster.prototype.updateIcon = function() { 984 var zoom = this.map_.getZoom(); 985 var mz = this.markerClusterer_.getMaxZoom(); 986 987 if (mz && zoom > mz) { 988 // The zoom is greater than our max zoom so show all the markers in cluster. 989 for (var i = 0, marker; marker = this.markers_[i]; i++) { 990 marker.setMap(this.map_); 991 } 992 return; 993 } 994 995 if (this.markers_.length < this.minClusterSize_) { 996 // Min cluster size not yet reached. 997 this.clusterIcon_.hide(); 998 return; 999 } 1000 1001 var numStyles = this.markerClusterer_.getStyles().length; 1002 var sums = this.markerClusterer_.getCalculator()(this.markers_, numStyles); 1003 this.clusterIcon_.setCenter(this.center_); 1004 this.clusterIcon_.setSums(sums); 1005 this.clusterIcon_.show(); 1006}; 1007 1008 1009/** 1010 * A cluster icon 1011 * 1012 * @param {Cluster} cluster The cluster to be associated with. 1013 * @param {Object} styles An object that has style properties: 1014 * 'url': (string) The image url. 1015 * 'height': (number) The image height. 1016 * 'width': (number) The image width. 1017 * 'anchor': (Array) The anchor position of the label text. 1018 * 'textColor': (string) The text color. 1019 * 'textSize': (number) The text size. 1020 * 'backgroundPosition: (string) The background postition x, y. 1021 * @param {number=} opt_padding Optional padding to apply to the cluster icon. 1022 * @constructor 1023 * @extends google.maps.OverlayView 1024 * @ignore 1025 */ 1026function ClusterIcon(cluster, styles, opt_padding) { 1027 cluster.getMarkerClusterer().extend(ClusterIcon, google.maps.OverlayView); 1028 1029 this.styles_ = styles; 1030 this.padding_ = opt_padding || 0; 1031 this.cluster_ = cluster; 1032 this.center_ = null; 1033 this.map_ = cluster.getMap(); 1034 this.div_ = null; 1035 this.sums_ = null; 1036 this.visible_ = false; 1037 1038 this.setMap(this.map_); 1039} 1040 1041 1042/** 1043 * Triggers the clusterclick event and zoom's if the option is set. 1044 */ 1045ClusterIcon.prototype.triggerClusterClick = function() { 1046 var markerClusterer = this.cluster_.getMarkerClusterer(); 1047 1048 // Trigger the clusterclick event. 1049 google.maps.event.trigger(markerClusterer, 'clusterclick', this.cluster_); 1050 1051 if (markerClusterer.isZoomOnClick()) { 1052 // Zoom into the cluster. 1053 this.map_.fitBounds(this.cluster_.getBounds()); 1054 } 1055}; 1056 1057 1058/** 1059 * Adding the cluster icon to the dom. 1060 * @ignore 1061 */ 1062ClusterIcon.prototype.onAdd = function() { 1063 this.div_ = document.createElement('DIV'); 1064 if (this.visible_) { 1065 var pos = this.getPosFromLatLng_(this.center_); 1066 this.div_.style.cssText = this.createCss(pos); 1067 this.div_.innerHTML = this.sums_.text; 1068 } 1069 1070 var panes = this.getPanes(); 1071 panes.overlayMouseTarget.appendChild(this.div_); 1072 1073 var that = this; 1074 google.maps.event.addDomListener(this.div_, 'click', function() { 1075 that.triggerClusterClick(); 1076 }); 1077}; 1078 1079 1080/** 1081 * Returns the position to place the div dending on the latlng. 1082 * 1083 * @param {google.maps.LatLng} latlng The position in latlng. 1084 * @return {google.maps.Point} The position in pixels. 1085 * @private 1086 */ 1087ClusterIcon.prototype.getPosFromLatLng_ = function(latlng) { 1088 var pos = this.getProjection().fromLatLngToDivPixel(latlng); 1089 pos.x -= parseInt(this.width_ / 2, 10); 1090 pos.y -= parseInt(this.height_ / 2, 10); 1091 return pos; 1092}; 1093 1094 1095/** 1096 * Draw the icon. 1097 * @ignore 1098 */ 1099ClusterIcon.prototype.draw = function() { 1100 if (this.visible_) { 1101 var pos = this.getPosFromLatLng_(this.center_); 1102 this.div_.style.top = pos.y + 'px'; 1103 this.div_.style.left = pos.x + 'px'; 1104 } 1105}; 1106 1107 1108/** 1109 * Hide the icon. 1110 */ 1111ClusterIcon.prototype.hide = function() { 1112 if (this.div_) { 1113 this.div_.style.display = 'none'; 1114 } 1115 this.visible_ = false; 1116}; 1117 1118 1119/** 1120 * Position and show the icon. 1121 */ 1122ClusterIcon.prototype.show = function() { 1123 if (this.div_) { 1124 var pos = this.getPosFromLatLng_(this.center_); 1125 this.div_.style.cssText = this.createCss(pos); 1126 this.div_.style.display = ''; 1127 } 1128 this.visible_ = true; 1129}; 1130 1131 1132/** 1133 * Remove the icon from the map 1134 */ 1135ClusterIcon.prototype.remove = function() { 1136 this.setMap(null); 1137}; 1138 1139 1140/** 1141 * Implementation of the onRemove interface. 1142 * @ignore 1143 */ 1144ClusterIcon.prototype.onRemove = function() { 1145 if (this.div_ && this.div_.parentNode) { 1146 this.hide(); 1147 this.div_.parentNode.removeChild(this.div_); 1148 this.div_ = null; 1149 } 1150}; 1151 1152 1153/** 1154 * Set the sums of the icon. 1155 * 1156 * @param {Object} sums The sums containing: 1157 * 'text': (string) The text to display in the icon. 1158 * 'index': (number) The style index of the icon. 1159 */ 1160ClusterIcon.prototype.setSums = function(sums) { 1161 this.sums_ = sums; 1162 this.text_ = sums.text; 1163 this.index_ = sums.index; 1164 if (this.div_) { 1165 this.div_.innerHTML = sums.text; 1166 } 1167 1168 this.useStyle(); 1169}; 1170 1171 1172/** 1173 * Sets the icon to the the styles. 1174 */ 1175ClusterIcon.prototype.useStyle = function() { 1176 var index = Math.max(0, this.sums_.index - 1); 1177 index = Math.min(this.styles_.length - 1, index); 1178 var style = this.styles_[index]; 1179 this.url_ = style['url']; 1180 this.height_ = style['height']; 1181 this.width_ = style['width']; 1182 this.textColor_ = style['textColor']; 1183 this.anchor_ = style['anchor']; 1184 this.textSize_ = style['textSize']; 1185 this.backgroundPosition_ = style['backgroundPosition']; 1186};
vendor: 4,429 bytes, lines 1187-1290
1187 1188 1189/** 1190 * Sets the center of the icon. 1191 * 1192 * @param {google.maps.LatLng} center The latlng to set as the center. 1193 */ 1194ClusterIcon.prototype.setCenter = function(center) { 1195 this.center_ = center; 1196}; 1197 1198 1199/** 1200 * Create the css text based on the position of the icon. 1201 * 1202 * @param {google.maps.Point} pos The position. 1203 * @return {string} The css style text. 1204 */ 1205ClusterIcon.prototype.createCss = function(pos) { 1206 var style = []; 1207 style.push('background-image:url(' + this.url_ + ');'); 1208 var backgroundPosition = this.backgroundPosition_ ? this.backgroundPosition_ : '0 0'; 1209 style.push('background-position:' + backgroundPosition + ';'); 1210 1211 if (typeof this.anchor_ === 'object') { 1212 if (typeof this.anchor_[0] === 'number' && this.anchor_[0] > 0 && 1213 this.anchor_[0] < this.height_) { 1214 style.push('height:' + (this.height_ - this.anchor_[0]) + 1215 'px; padding-top:' + this.anchor_[0] + 'px;'); 1216 } else { 1217 style.push('height:' + this.height_ + 'px; line-height:' + this.height_ + 1218 'px;'); 1219 } 1220 if (typeof this.anchor_[1] === 'number' && this.anchor_[1] > 0 && 1221 this.anchor_[1] < this.width_) { 1222 style.push('width:' + (this.width_ - this.anchor_[1]) + 1223 'px; padding-left:' + this.anchor_[1] + 'px;'); 1224 } else { 1225 style.push('width:' + this.width_ + 'px; text-align:center;'); 1226 } 1227 } else { 1228 style.push('height:' + this.height_ + 'px; line-height:' + 1229 this.height_ + 'px; width:' + this.width_ + 'px; text-align:center;'); 1230 } 1231 1232 var txtColor = this.textColor_ ? this.textColor_ : 'black'; 1233 var txtSize = this.textSize_ ? this.textSize_ : 11; 1234 1235 style.push('cursor:pointer; top:' + pos.y + 'px; left:' + 1236 pos.x + 'px; color:' + txtColor + '; position:absolute; font-size:' + 1237 txtSize + 'px; font-family:Arial,sans-serif; font-weight:bold'); 1238 return style.join(''); 1239}; 1240 1241 1242// Export Symbols for Closure 1243// If you are not going to compile with closure then you can remove the 1244// code below. 1245window['MarkerClusterer'] = MarkerClusterer; 1246MarkerClusterer.prototype['addMarker'] = MarkerClusterer.prototype.addMarker; 1247MarkerClusterer.prototype['addMarkers'] = MarkerClusterer.prototype.addMarkers; 1248MarkerClusterer.prototype['clearMarkers'] = 1249 MarkerClusterer.prototype.clearMarkers; 1250MarkerClusterer.prototype['fitMapToMarkers'] = 1251 MarkerClusterer.prototype.fitMapToMarkers; 1252MarkerClusterer.prototype['getCalculator'] = 1253 MarkerClusterer.prototype.getCalculator; 1254MarkerClusterer.prototype['getGridSize'] = 1255 MarkerClusterer.prototype.getGridSize; 1256MarkerClusterer.prototype['getExtendedBounds'] = 1257 MarkerClusterer.prototype.getExtendedBounds; 1258MarkerClusterer.prototype['getMap'] = MarkerClusterer.prototype.getMap; 1259MarkerClusterer.prototype['getMarkers'] = MarkerClusterer.prototype.getMarkers; 1260MarkerClusterer.prototype['getMaxZoom'] = MarkerClusterer.prototype.getMaxZoom; 1261MarkerClusterer.prototype['getStyles'] = MarkerClusterer.prototype.getStyles; 1262MarkerClusterer.prototype['getTotalClusters'] = 1263 MarkerClusterer.prototype.getTotalClusters; 1264MarkerClusterer.prototype['getTotalMarkers'] = 1265 MarkerClusterer.prototype.getTotalMarkers; 1266MarkerClusterer.prototype['redraw'] = MarkerClusterer.prototype.redraw; 1267MarkerClusterer.prototype['removeMarker'] = 1268 MarkerClusterer.prototype.removeMarker; 1269MarkerClusterer.prototype['removeMarkers'] = 1270 MarkerClusterer.prototype.removeMarkers; 1271MarkerClusterer.prototype['resetViewport'] = 1272 MarkerClusterer.prototype.resetViewport; 1273MarkerClusterer.prototype['repaint'] = 1274 MarkerClusterer.prototype.repaint; 1275MarkerClusterer.prototype['setCalculator'] = 1276 MarkerClusterer.prototype.setCalculator; 1277MarkerClusterer.prototype['setGridSize'] = 1278 MarkerClusterer.prototype.setGridSize; 1279MarkerClusterer.prototype['setMaxZoom'] = 1280 MarkerClusterer.prototype.setMaxZoom; 1281MarkerClusterer.prototype['onAdd'] = MarkerClusterer.prototype.onAdd; 1282MarkerClusterer.prototype['draw'] = MarkerClusterer.prototype.draw; 1283 1284Cluster.prototype['getCenter'] = Cluster.prototype.getCenter; 1285Cluster.prototype['getSize'] = Cluster.prototype.getSize; 1286Cluster.prototype['getMarkers'] = Cluster.prototype.getMarkers; 1287 1288ClusterIcon.prototype['onAdd'] = ClusterIcon.prototype.onAdd; 1289ClusterIcon.prototype['draw'] = ClusterIcon.prototype.draw; 1290ClusterIcon.prototype['onRemove'] = ClusterIcon.prototype.onRemove;
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.