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